001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.processor;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import org.apache.camel.Exchange;
023    import org.apache.camel.Navigate;
024    import org.apache.camel.Processor;
025    import org.apache.camel.impl.ServiceSupport;
026    import org.apache.camel.util.ExchangeHelper;
027    import org.apache.camel.util.ServiceHelper;
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    
031    /**
032     * Implements try/catch/finally type processing
033     *
034     * @version $Revision: 794544 $
035     */
036    public class TryProcessor extends ServiceSupport implements Processor, Navigate<Processor>, Traceable {
037        private static final transient Log LOG = LogFactory.getLog(TryProcessor.class);
038    
039        protected final Processor tryProcessor;
040        protected final List<CatchProcessor> catchClauses;
041        protected final Processor finallyProcessor;
042    
043        public TryProcessor(Processor tryProcessor, List<CatchProcessor> catchClauses, Processor finallyProcessor) {
044            this.tryProcessor = tryProcessor;
045            this.catchClauses = catchClauses;
046            this.finallyProcessor = finallyProcessor;
047        }
048    
049        public String toString() {
050            String finallyText = (finallyProcessor == null) ? "" : " Finally {" + finallyProcessor + "}";
051            return "Try {" + tryProcessor + "} " + (catchClauses != null ? catchClauses : "") + finallyText;
052        }
053    
054        public String getTraceLabel() {
055            return "Try";
056        }
057    
058        public void process(Exchange exchange) throws Exception {
059            Exception e;
060    
061            // try processor first
062            try {
063                tryProcessor.process(exchange);
064                e = exchange.getException();
065    
066                // Ignore it if it was handled by the dead letter channel.
067                if (e != null && ExchangeHelper.isFailureHandled(exchange)) {
068                    e = null;
069                }
070            } catch (Exception ex) {
071                e = ex;
072                exchange.setException(e);
073            }
074    
075            // handle any exception occured during the try processor
076            try {
077                if (e != null) {
078                    handleException(exchange, e);
079                }
080            } finally {
081                // and run finally
082                // notice its always executed since we always enter the try block
083                processFinally(exchange);
084            }
085        }
086    
087        protected void doStart() throws Exception {
088            ServiceHelper.startServices(tryProcessor, catchClauses, finallyProcessor);
089        }
090    
091        protected void doStop() throws Exception {
092            ServiceHelper.stopServices(finallyProcessor, catchClauses, tryProcessor);
093        }
094    
095        protected void handleException(Exchange exchange, Throwable e) throws Exception {
096            if (catchClauses == null) {
097                return;
098            }
099    
100            for (CatchProcessor catchClause : catchClauses) {
101                if (catchClause.catches(exchange, e)) {
102                    if (LOG.isTraceEnabled()) {
103                        LOG.trace("This TryProcessor catches the exception: " + e.getClass().getName() + " caused by: " + e.getMessage());
104                    }
105    
106                    // lets attach the exception to the exchange
107                    Exchange localExchange = exchange.copy();
108                    
109                    localExchange.setProperty(Exchange.EXCEPTION_CAUGHT, e);
110                    // give the rest of the pipeline another chance
111                    localExchange.setException(null);
112    
113                    // do not catch any exception here, let it propagate up
114                    catchClause.process(localExchange);
115    
116                    boolean handled = catchClause.handles(exchange);
117    
118                    if (LOG.isDebugEnabled()) {
119                        LOG.debug("The exception is handled: " + handled + " for the exception: " + e.getClass().getName()
120                            + " caused by: " + e.getMessage());
121                    }
122    
123                    if (handled) {
124                        localExchange.removeProperty(Exchange.EXCEPTION_CAUGHT);
125                    } else {
126                        // put exception back as it was not handled
127                        if (localExchange.getException() == null) {
128                            localExchange.setException(localExchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class));
129                        }
130                    }
131    
132                    // copy result back to the original exchange
133                    ExchangeHelper.copyResults(exchange, localExchange);
134                    return;
135                }
136            }
137    
138            if (LOG.isTraceEnabled()) {
139                LOG.trace("This TryProcessor does not catch the exception: " + e.getClass().getName() + " caused by: " + e.getMessage());
140            }
141        }
142    
143        protected void processFinally(Exchange exchange) throws Exception {
144            if (finallyProcessor != null) {
145                Exception lastException = exchange.getException();
146                exchange.setException(null);
147    
148                // do not catch any exception here, let it propagate up
149                finallyProcessor.process(exchange);
150                if (exchange.getException() == null) {
151                    exchange.setException(lastException);
152                }
153            }
154        }
155    
156        public List<Processor> next() {
157            if (!hasNext()) {
158                return null;
159            }
160            List<Processor> answer = new ArrayList<Processor>();
161            if (tryProcessor != null) {
162                answer.add(tryProcessor);
163            }
164            if (catchClauses != null) {
165                answer.addAll(catchClauses);
166            }
167            if (finallyProcessor != null) {
168                answer.add(finallyProcessor);
169            }
170            return answer;
171        }
172    
173        public boolean hasNext() {
174            return tryProcessor != null;
175        }
176    }