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.Arrays; 021import java.util.List; 022import java.util.concurrent.CountDownLatch; 023import java.util.concurrent.RejectedExecutionException; 024 025import org.apache.camel.AsyncCallback; 026import org.apache.camel.AsyncProcessor; 027import org.apache.camel.Exchange; 028import org.apache.camel.Ordered; 029import org.apache.camel.Processor; 030import org.apache.camel.Service; 031import org.apache.camel.spi.AsyncProcessorAwaitManager; 032import org.apache.camel.spi.RoutePolicy; 033import org.apache.camel.spi.Transformer; 034import org.apache.camel.spi.UnitOfWork; 035import org.apache.camel.util.OrderedComparator; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * A Shared (thread safe) internal {@link Processor} that Camel routing engine used during routing for cross cutting functionality such as: 041 * <ul> 042 * <li>Execute {@link UnitOfWork}</li> 043 * <li>Keeping track which route currently is being routed</li> 044 * <li>Execute {@link RoutePolicy}</li> 045 * <li>Gather JMX performance statics</li> 046 * <li>Tracing</li> 047 * <li>Debugging</li> 048 * <li>Message History</li> 049 * <li>Stream Caching</li> 050 * <li>{@link Transformer}</li> 051 * </ul> 052 * ... and more. 053 * <p/> 054 * This implementation executes this cross cutting functionality as a {@link CamelInternalProcessorAdvice} advice (before and after advice) 055 * by executing the {@link CamelInternalProcessorAdvice#before(Exchange)} and 056 * {@link CamelInternalProcessorAdvice#after(Exchange, Object)} callbacks in correct order during routing. 057 * This reduces number of stack frames needed during routing, and reduce the number of lines in stacktraces, as well 058 * makes debugging the routing engine easier for end users. 059 * <p/> 060 * <b>Debugging tips:</b> Camel end users whom want to debug their Camel applications with the Camel source code, then make sure to 061 * read the source code of this class about the debugging tips, which you can find in the 062 * {@link #process(Exchange, AsyncCallback, AsyncProcessor, Processor)} method. 063 * <p/> 064 * The added advices can implement {@link Ordered} to control in which order the advices are executed. 065 */ 066public class SharedCamelInternalProcessor { 067 068 private static final Logger LOG = LoggerFactory.getLogger(SharedCamelInternalProcessor.class); 069 private final List<CamelInternalProcessorAdvice> advices = new ArrayList<CamelInternalProcessorAdvice>(); 070 071 public SharedCamelInternalProcessor(CamelInternalProcessorAdvice... advices) { 072 if (advices != null) { 073 this.advices.addAll(Arrays.asList(advices)); 074 // ensure advices are sorted so they are in the order we want 075 this.advices.sort(OrderedComparator.get()); 076 } 077 } 078 079 /** 080 * Synchronous API 081 */ 082 public void process(Exchange exchange, AsyncProcessor processor, Processor resultProcessor) { 083 final AsyncProcessorAwaitManager awaitManager = exchange.getContext().getAsyncProcessorAwaitManager(); 084 final CountDownLatch latch = new CountDownLatch(1); 085 086 boolean sync = process(exchange, new AsyncCallback() { 087 public void done(boolean doneSync) { 088 if (!doneSync) { 089 awaitManager.countDown(exchange, latch); 090 } 091 } 092 093 @Override 094 public String toString() { 095 return "Done " + processor; 096 } 097 }, processor, resultProcessor); 098 099 if (!sync) { 100 awaitManager.await(exchange, latch); 101 } 102 } 103 104 /** 105 * Asynchronous API 106 */ 107 public boolean process(Exchange exchange, AsyncCallback callback, AsyncProcessor processor, Processor resultProcessor) { 108 // ---------------------------------------------------------- 109 // CAMEL END USER - READ ME FOR DEBUGGING TIPS 110 // ---------------------------------------------------------- 111 // If you want to debug the Camel routing engine, then there is a lot of internal functionality 112 // the routing engine executes during routing messages. You can skip debugging this internal 113 // functionality and instead debug where the routing engine continues routing to the next node 114 // in the routes. The CamelInternalProcessor is a vital part of the routing engine, as its 115 // being used in between the nodes. As an end user you can just debug the code in this class 116 // in between the: 117 // CAMEL END USER - DEBUG ME HERE +++ START +++ 118 // CAMEL END USER - DEBUG ME HERE +++ END +++ 119 // you can see in the code below. 120 // ---------------------------------------------------------- 121 122 if (processor == null || !continueProcessing(exchange, processor)) { 123 // no processor or we should not continue then we are done 124 callback.done(true); 125 return true; 126 } 127 128 // optimise to use object array for states 129 final Object[] states = new Object[advices.size()]; 130 // optimise for loop using index access to avoid creating iterator object 131 for (int i = 0; i < advices.size(); i++) { 132 CamelInternalProcessorAdvice task = advices.get(i); 133 try { 134 Object state = task.before(exchange); 135 states[i] = state; 136 } catch (Throwable e) { 137 exchange.setException(e); 138 callback.done(true); 139 return true; 140 } 141 } 142 143 // create internal callback which will execute the advices in reverse order when done 144 callback = new InternalCallback(states, exchange, callback, resultProcessor); 145 146 // UNIT_OF_WORK_PROCESS_SYNC is @deprecated and we should remove it from Camel 3.0 147 Object synchronous = exchange.removeProperty(Exchange.UNIT_OF_WORK_PROCESS_SYNC); 148 if (exchange.isTransacted() || synchronous != null) { 149 // must be synchronized for transacted exchanges 150 if (LOG.isTraceEnabled()) { 151 if (exchange.isTransacted()) { 152 LOG.trace("Transacted Exchange must be routed synchronously for exchangeId: {} -> {}", exchange.getExchangeId(), exchange); 153 } else { 154 LOG.trace("Synchronous UnitOfWork Exchange must be routed synchronously for exchangeId: {} -> {}", exchange.getExchangeId(), exchange); 155 } 156 } 157 // ---------------------------------------------------------- 158 // CAMEL END USER - DEBUG ME HERE +++ START +++ 159 // ---------------------------------------------------------- 160 try { 161 processor.process(exchange); 162 } catch (Throwable e) { 163 exchange.setException(e); 164 } 165 // ---------------------------------------------------------- 166 // CAMEL END USER - DEBUG ME HERE +++ END +++ 167 // ---------------------------------------------------------- 168 callback.done(true); 169 return true; 170 } else { 171 final UnitOfWork uow = exchange.getUnitOfWork(); 172 173 // allow unit of work to wrap callback in case it need to do some special work 174 // for example the MDCUnitOfWork 175 AsyncCallback async = callback; 176 if (uow != null) { 177 async = uow.beforeProcess(processor, exchange, callback); 178 } 179 180 // ---------------------------------------------------------- 181 // CAMEL END USER - DEBUG ME HERE +++ START +++ 182 // ---------------------------------------------------------- 183 if (LOG.isTraceEnabled()) { 184 LOG.trace("Processing exchange for exchangeId: {} -> {}", exchange.getExchangeId(), exchange); 185 } 186 boolean sync = processor.process(exchange, async); 187 // ---------------------------------------------------------- 188 // CAMEL END USER - DEBUG ME HERE +++ END +++ 189 // ---------------------------------------------------------- 190 191 // execute any after processor work (in current thread, not in the callback) 192 if (uow != null) { 193 uow.afterProcess(processor, exchange, callback, sync); 194 } 195 196 if (LOG.isTraceEnabled()) { 197 LOG.trace("Exchange processed and is continued routed {} for exchangeId: {} -> {}", 198 new Object[]{sync ? "synchronously" : "asynchronously", exchange.getExchangeId(), exchange}); 199 } 200 return sync; 201 } 202 } 203 204 /** 205 * Internal callback that executes the after advices. 206 */ 207 private final class InternalCallback implements AsyncCallback { 208 209 private final Object[] states; 210 private final Exchange exchange; 211 private final AsyncCallback callback; 212 private final Processor resultProcessor; 213 214 private InternalCallback(Object[] states, Exchange exchange, AsyncCallback callback, Processor resultProcessor) { 215 this.states = states; 216 this.exchange = exchange; 217 this.callback = callback; 218 this.resultProcessor = resultProcessor; 219 } 220 221 @Override 222 @SuppressWarnings("unchecked") 223 public void done(boolean doneSync) { 224 // NOTE: if you are debugging Camel routes, then all the code in the for loop below is internal only 225 // so you can step straight to the finally block and invoke the callback 226 227 if (resultProcessor != null) { 228 try { 229 resultProcessor.process(exchange); 230 } catch (Throwable e) { 231 exchange.setException(e); 232 } 233 } 234 235 // we should call after in reverse order 236 try { 237 for (int i = advices.size() - 1; i >= 0; i--) { 238 CamelInternalProcessorAdvice task = advices.get(i); 239 Object state = states[i]; 240 try { 241 task.after(exchange, state); 242 } catch (Throwable e) { 243 exchange.setException(e); 244 // allow all advices to complete even if there was an exception 245 } 246 } 247 } finally { 248 // ---------------------------------------------------------- 249 // CAMEL END USER - DEBUG ME HERE +++ START +++ 250 // ---------------------------------------------------------- 251 // callback must be called 252 callback.done(doneSync); 253 // ---------------------------------------------------------- 254 // CAMEL END USER - DEBUG ME HERE +++ END +++ 255 // ---------------------------------------------------------- 256 } 257 } 258 } 259 260 /** 261 * Strategy to determine if we should continue processing the {@link Exchange}. 262 */ 263 protected boolean continueProcessing(Exchange exchange, AsyncProcessor processor) { 264 Object stop = exchange.getProperty(Exchange.ROUTE_STOP); 265 if (stop != null) { 266 boolean doStop = exchange.getContext().getTypeConverter().convertTo(Boolean.class, stop); 267 if (doStop) { 268 LOG.debug("Exchange is marked to stop routing: {}", exchange); 269 return false; 270 } 271 } 272 273 // determine if we can still run, or the camel context is forcing a shutdown 274 if (processor instanceof Service) { 275 boolean forceShutdown = exchange.getContext().getShutdownStrategy().forceShutdown((Service) processor); 276 if (forceShutdown) { 277 String msg = "Run not allowed as ShutdownStrategy is forcing shutting down, will reject executing exchange: " + exchange; 278 LOG.debug(msg); 279 if (exchange.getException() == null) { 280 exchange.setException(new RejectedExecutionException(msg)); 281 } 282 return false; 283 } 284 } 285 286 // yes we can continue 287 return true; 288 } 289 290}