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: 881175 $
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 "doTry";
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 occurred 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                Throwable caught = catchClause.catches(exchange, e);
102                if (caught != null) {
103                    if (LOG.isTraceEnabled()) {
104                        LOG.trace("This TryProcessor catches the exception: " + caught.getClass().getName() + " caused by: " + e.getMessage());
105                    }
106    
107                    // give the rest of the pipeline another chance
108                    exchange.setProperty(Exchange.EXCEPTION_CAUGHT, caught);
109                    exchange.setException(null);
110    
111                    // do not catch any exception here, let it propagate up
112                    catchClause.process(exchange);
113    
114                    // is the exception handled by the catch clause
115                    boolean handled = catchClause.handles(exchange);
116    
117                    if (LOG.isDebugEnabled()) {
118                        LOG.debug("The exception is handled: " + handled + " for the exception: " + e.getClass().getName()
119                            + " caused by: " + caught.getMessage());
120                    }
121    
122                    if (!handled) {
123                        // put exception back as it was not handled
124                        if (exchange.getException() == null) {
125                            exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class));
126                        }
127                    }
128    
129                    return;
130                }
131            }
132    
133            if (LOG.isTraceEnabled()) {
134                LOG.trace("This TryProcessor does not catch the exception: " + e.getClass().getName() + " caused by: " + e.getMessage());
135            }
136        }
137    
138        protected void processFinally(Exchange exchange) throws Exception {
139            if (finallyProcessor != null) {
140                Exception lastException = exchange.getException();
141                exchange.setException(null);
142    
143                // do not catch any exception here, let it propagate up
144                finallyProcessor.process(exchange);
145                if (exchange.getException() == null) {
146                    exchange.setException(lastException);
147                }
148            }
149        }
150    
151        public List<Processor> next() {
152            if (!hasNext()) {
153                return null;
154            }
155            List<Processor> answer = new ArrayList<Processor>();
156            if (tryProcessor != null) {
157                answer.add(tryProcessor);
158            }
159            if (catchClauses != null) {
160                answer.addAll(catchClauses);
161            }
162            if (finallyProcessor != null) {
163                answer.add(finallyProcessor);
164            }
165            return answer;
166        }
167    
168        public boolean hasNext() {
169            return tryProcessor != null;
170        }
171    }