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 }