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}