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 */
017package org.apache.camel.processor;
018
019import java.util.ArrayList;
020import java.util.Iterator;
021import java.util.List;
022
023import org.apache.camel.AsyncCallback;
024import org.apache.camel.AsyncProcessor;
025import org.apache.camel.Exchange;
026import org.apache.camel.Navigate;
027import org.apache.camel.Processor;
028import org.apache.camel.Traceable;
029import org.apache.camel.spi.IdAware;
030import org.apache.camel.support.ServiceSupport;
031import org.apache.camel.util.AsyncProcessorConverterHelper;
032import org.apache.camel.util.AsyncProcessorHelper;
033import org.apache.camel.util.ExchangeHelper;
034import org.apache.camel.util.ServiceHelper;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038import static org.apache.camel.processor.PipelineHelper.continueProcessing;
039
040/**
041 * Implements try/catch/finally type processing
042 *
043 * @version 
044 */
045public class TryProcessor extends ServiceSupport implements AsyncProcessor, Navigate<Processor>, Traceable, IdAware {
046    private static final Logger LOG = LoggerFactory.getLogger(TryProcessor.class);
047
048    protected String id;
049    protected final Processor tryProcessor;
050    protected final List<Processor> catchClauses;
051    protected final Processor finallyProcessor;
052
053    public TryProcessor(Processor tryProcessor, List<Processor> catchClauses, Processor finallyProcessor) {
054        this.tryProcessor = tryProcessor;
055        this.catchClauses = catchClauses;
056        this.finallyProcessor = finallyProcessor;
057    }
058
059    public String toString() {
060        String catchText = catchClauses == null || catchClauses.isEmpty() ? "" : " Catches {" + catchClauses + "}";
061        String finallyText = (finallyProcessor == null) ? "" : " Finally {" + finallyProcessor + "}";
062        return "Try {" + tryProcessor + "}" + catchText + finallyText;
063    }
064
065    public String getTraceLabel() {
066        return "doTry";
067    }
068
069    public void process(Exchange exchange) throws Exception {
070        AsyncProcessorHelper.process(this, exchange);
071    }
072
073    public boolean process(Exchange exchange, AsyncCallback callback) {
074        Iterator<Processor> processors = next().iterator();
075
076        Object lastHandled = exchange.getProperty(Exchange.EXCEPTION_HANDLED);
077        exchange.setProperty(Exchange.EXCEPTION_HANDLED, null);
078
079        while (continueRouting(processors, exchange)) {
080            exchange.setProperty(Exchange.TRY_ROUTE_BLOCK, true);
081            ExchangeHelper.prepareOutToIn(exchange);
082
083            // process the next processor
084            Processor processor = processors.next();
085            AsyncProcessor async = AsyncProcessorConverterHelper.convert(processor);
086            boolean sync = process(exchange, callback, processors, async, lastHandled);
087
088            // continue as long its being processed synchronously
089            if (!sync) {
090                LOG.trace("Processing exchangeId: {} is continued being processed asynchronously", exchange.getExchangeId());
091                // the remainder of the try .. catch .. finally will be completed async
092                // so we break out now, then the callback will be invoked which then continue routing from where we left here
093                return false;
094            }
095
096            LOG.trace("Processing exchangeId: {} is continued being processed synchronously", exchange.getExchangeId());
097        }
098
099        ExchangeHelper.prepareOutToIn(exchange);
100        exchange.removeProperty(Exchange.TRY_ROUTE_BLOCK);
101        exchange.setProperty(Exchange.EXCEPTION_HANDLED, lastHandled);
102        LOG.trace("Processing complete for exchangeId: {} >>> {}", exchange.getExchangeId(), exchange);
103        callback.done(true);
104        return true;
105    }
106
107    protected boolean process(final Exchange exchange, final AsyncCallback callback,
108                              final Iterator<Processor> processors, final AsyncProcessor processor,
109                              final Object lastHandled) {
110        // this does the actual processing so log at trace level
111        LOG.trace("Processing exchangeId: {} >>> {}", exchange.getExchangeId(), exchange);
112
113        // implement asynchronous routing logic in callback so we can have the callback being
114        // triggered and then continue routing where we left
115        boolean sync = processor.process(exchange, new AsyncCallback() {
116            public void done(boolean doneSync) {
117                // we only have to handle async completion of the pipeline
118                if (doneSync) {
119                    return;
120                }
121
122                // continue processing the try .. catch .. finally asynchronously
123                while (continueRouting(processors, exchange)) {
124                    exchange.setProperty(Exchange.TRY_ROUTE_BLOCK, true);
125                    ExchangeHelper.prepareOutToIn(exchange);
126
127                    // process the next processor
128                    AsyncProcessor processor = AsyncProcessorConverterHelper.convert(processors.next());
129                    doneSync = process(exchange, callback, processors, processor, lastHandled);
130
131                    if (!doneSync) {
132                        LOG.trace("Processing exchangeId: {} is continued being processed asynchronously", exchange.getExchangeId());
133                        // the remainder of the try .. catch .. finally will be completed async
134                        // so we break out now, then the callback will be invoked which then continue routing from where we left here
135                        return;
136                    }
137                }
138
139                ExchangeHelper.prepareOutToIn(exchange);
140                exchange.removeProperty(Exchange.TRY_ROUTE_BLOCK);
141                exchange.setProperty(Exchange.EXCEPTION_HANDLED, lastHandled);
142                LOG.trace("Processing complete for exchangeId: {} >>> {}", exchange.getExchangeId(), exchange);
143                callback.done(false);
144            }
145        });
146
147        return sync;
148    }
149
150    protected boolean continueRouting(Iterator<Processor> it, Exchange exchange) {
151        Object stop = exchange.getProperty(Exchange.ROUTE_STOP);
152        if (stop != null) {
153            boolean doStop = exchange.getContext().getTypeConverter().convertTo(Boolean.class, stop);
154            if (doStop) {
155                LOG.debug("Exchange is marked to stop routing: {}", exchange);
156                return false;
157            }
158        }
159
160        // continue if there are more processors to route
161        return it.hasNext();
162    }
163
164    protected void doStart() throws Exception {
165        ServiceHelper.startServices(tryProcessor, catchClauses, finallyProcessor);
166    }
167
168    protected void doStop() throws Exception {
169        ServiceHelper.stopServices(tryProcessor, catchClauses, finallyProcessor);
170    }
171
172    public List<Processor> next() {
173        if (!hasNext()) {
174            return null;
175        }
176        List<Processor> answer = new ArrayList<Processor>();
177        if (tryProcessor != null) {
178            answer.add(tryProcessor);
179        }
180        if (catchClauses != null) {
181            answer.addAll(catchClauses);
182        }
183        if (finallyProcessor != null) {
184            answer.add(finallyProcessor);
185        }
186        return answer;
187    }
188
189    public boolean hasNext() {
190        return tryProcessor != null || catchClauses != null && !catchClauses.isEmpty() || finallyProcessor != null;
191    }
192
193    public String getId() {
194        return id;
195    }
196
197    public void setId(String id) {
198        this.id = id;
199    }
200}