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: 769303 $
035     */
036    public class TryProcessor extends ServiceSupport implements Processor, Navigate<Processor> {
037        private static final transient Log LOG = LogFactory.getLog(TryProcessor.class);
038    
039        private final Processor tryProcessor;
040        private final List<CatchProcessor> catchClauses;
041        private 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 + finallyText;
052        }
053    
054        public void process(Exchange exchange) throws Exception {
055            Exception e;
056    
057            // try processor first
058            try {
059                tryProcessor.process(exchange);
060                e = exchange.getException();
061    
062                // Ignore it if it was handled by the dead letter channel.
063                if (e != null && ExchangeHelper.isFailureHandled(exchange)) {
064                    e = null;
065                }
066            } catch (Exception ex) {
067                e = ex;
068                exchange.setException(e);
069            }
070    
071            // handle any exception occured during the try processor
072            try {
073                if (e != null) {
074                    handleException(exchange, e);
075                }
076            } finally {
077                // and run finally
078                // notice its always executed since we always enter the try block
079                processFinally(exchange);
080            }
081        }
082    
083        protected void doStart() throws Exception {
084            ServiceHelper.startServices(tryProcessor, catchClauses, finallyProcessor);
085        }
086    
087        protected void doStop() throws Exception {
088            ServiceHelper.stopServices(finallyProcessor, catchClauses, tryProcessor);
089        }
090    
091        protected void handleException(Exchange exchange, Throwable e) throws Exception {
092            for (CatchProcessor catchClause : catchClauses) {
093                if (catchClause.catches(exchange, e)) {
094                    if (LOG.isTraceEnabled()) {
095                        LOG.trace("This TryProcessor catches the exception: " + e.getClass().getName() + " caused by: " + e.getMessage());
096                    }
097    
098                    // lets attach the exception to the exchange
099                    Exchange localExchange = exchange.copy();
100                    
101                    localExchange.setProperty(Exchange.EXCEPTION_CAUGHT, e);
102                    // give the rest of the pipeline another chance
103                    localExchange.setException(null);
104    
105                    // do not catch any exception here, let it propagate up
106                    catchClause.process(localExchange);
107    
108                    boolean handled = catchClause.handles(exchange);
109    
110                    if (LOG.isDebugEnabled()) {
111                        LOG.debug("The exception is handled: " + handled + " for the exception: " + e.getClass().getName()
112                            + " caused by: " + e.getMessage());
113                    }
114    
115                    if (handled) {
116                        localExchange.removeProperty(Exchange.EXCEPTION_CAUGHT);
117                    } else {
118                        // put exception back as it was not handled
119                        if (localExchange.getException() == null) {
120                            localExchange.setException(localExchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class));
121                        }
122                    }
123    
124                    // copy result back to the original exchange
125                    ExchangeHelper.copyResults(exchange, localExchange);
126                    return;
127                }
128            }
129    
130            if (LOG.isTraceEnabled()) {
131                LOG.trace("This TryProcessor does not catch the exception: " + e.getClass().getName() + " caused by: " + e.getMessage());
132            }
133        }
134    
135        protected void processFinally(Exchange exchange) throws Exception {
136            if (finallyProcessor != null) {
137                Exception lastException = exchange.getException();
138                exchange.setException(null);
139    
140                // do not catch any exception here, let it propagate up
141                finallyProcessor.process(exchange);
142                if (exchange.getException() == null) {
143                    exchange.setException(lastException);
144                }
145            }
146        }
147    
148        public List<Processor> next() {
149            if (!hasNext()) {
150                return null;
151            }
152            List<Processor> answer = new ArrayList<Processor>();
153            if (tryProcessor != null) {
154                answer.add(tryProcessor);
155            }
156            if (catchClauses != null) {
157                answer.addAll(catchClauses);
158            }
159            if (finallyProcessor != null) {
160                answer.add(finallyProcessor);
161            }
162            return answer;
163        }
164    
165        public boolean hasNext() {
166            return tryProcessor != null;
167        }
168    }