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 }