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.concurrent.Callable; 020import java.util.concurrent.ExecutorService; 021 022import org.apache.camel.AsyncCallback; 023import org.apache.camel.AsyncProcessor; 024import org.apache.camel.CamelContext; 025import org.apache.camel.Exchange; 026import org.apache.camel.ExchangePattern; 027import org.apache.camel.Message; 028import org.apache.camel.Ordered; 029import org.apache.camel.Predicate; 030import org.apache.camel.Processor; 031import org.apache.camel.Route; 032import org.apache.camel.Traceable; 033import org.apache.camel.support.ServiceSupport; 034import org.apache.camel.support.SynchronizationAdapter; 035import org.apache.camel.util.AsyncProcessorHelper; 036import org.apache.camel.util.ExchangeHelper; 037import org.apache.camel.util.ServiceHelper; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import static org.apache.camel.util.ObjectHelper.notNull; 042 043/** 044 * Processor implementing <a href="http://camel.apache.org/oncompletion.html">onCompletion</a>. 045 * 046 * @version 047 */ 048public class OnCompletionProcessor extends ServiceSupport implements AsyncProcessor, Traceable { 049 050 private static final Logger LOG = LoggerFactory.getLogger(OnCompletionProcessor.class); 051 private final CamelContext camelContext; 052 private final Processor processor; 053 private final ExecutorService executorService; 054 private final boolean shutdownExecutorService; 055 private final boolean onCompleteOnly; 056 private final boolean onFailureOnly; 057 private final Predicate onWhen; 058 private final boolean useOriginalBody; 059 private final boolean afterConsumer; 060 061 public OnCompletionProcessor(CamelContext camelContext, Processor processor, ExecutorService executorService, boolean shutdownExecutorService, 062 boolean onCompleteOnly, boolean onFailureOnly, Predicate onWhen, boolean useOriginalBody, boolean afterConsumer) { 063 notNull(camelContext, "camelContext"); 064 notNull(processor, "processor"); 065 this.camelContext = camelContext; 066 this.processor = processor; 067 this.executorService = executorService; 068 this.shutdownExecutorService = shutdownExecutorService; 069 this.onCompleteOnly = onCompleteOnly; 070 this.onFailureOnly = onFailureOnly; 071 this.onWhen = onWhen; 072 this.useOriginalBody = useOriginalBody; 073 this.afterConsumer = afterConsumer; 074 } 075 076 @Override 077 protected void doStart() throws Exception { 078 ServiceHelper.startService(processor); 079 } 080 081 @Override 082 protected void doStop() throws Exception { 083 ServiceHelper.stopService(processor); 084 } 085 086 @Override 087 protected void doShutdown() throws Exception { 088 ServiceHelper.stopAndShutdownService(processor); 089 if (shutdownExecutorService) { 090 getCamelContext().getExecutorServiceManager().shutdownNow(executorService); 091 } 092 } 093 094 public CamelContext getCamelContext() { 095 return camelContext; 096 } 097 098 public void process(Exchange exchange) throws Exception { 099 AsyncProcessorHelper.process(this, exchange); 100 } 101 102 public boolean process(Exchange exchange, AsyncCallback callback) { 103 if (processor != null) { 104 // register callback 105 if (afterConsumer) { 106 exchange.getUnitOfWork().addSynchronization(new OnCompletionSynchronizationAfterConsumer()); 107 } else { 108 exchange.getUnitOfWork().addSynchronization(new OnCompletionSynchronizationBeforeConsumer()); 109 } 110 } 111 112 callback.done(true); 113 return true; 114 } 115 116 protected boolean isCreateCopy() { 117 // we need to create a correlated copy if we run in parallel mode or is in after consumer mode (as the UoW would be done on the original exchange otherwise) 118 return executorService != null || afterConsumer; 119 } 120 121 /** 122 * Processes the exchange by the processors 123 * 124 * @param processor the processor 125 * @param exchange the exchange 126 */ 127 protected static void doProcess(Processor processor, Exchange exchange) { 128 // must remember some properties which we cannot use during onCompletion processing 129 // as otherwise we may cause issues 130 Object stop = exchange.removeProperty(Exchange.ROUTE_STOP); 131 Object failureHandled = exchange.removeProperty(Exchange.FAILURE_HANDLED); 132 Object caught = exchange.removeProperty(Exchange.EXCEPTION_CAUGHT); 133 Object errorhandlerHandled = exchange.removeProperty(Exchange.ERRORHANDLER_HANDLED); 134 Object rollbackOnly = exchange.removeProperty(Exchange.ROLLBACK_ONLY); 135 Object rollbackOnlyLast = exchange.removeProperty(Exchange.ROLLBACK_ONLY_LAST); 136 137 Exception cause = exchange.getException(); 138 exchange.setException(null); 139 140 try { 141 processor.process(exchange); 142 } catch (Exception e) { 143 exchange.setException(e); 144 } finally { 145 // restore the options 146 if (stop != null) { 147 exchange.setProperty(Exchange.ROUTE_STOP, stop); 148 } 149 if (failureHandled != null) { 150 exchange.setProperty(Exchange.FAILURE_HANDLED, failureHandled); 151 } 152 if (caught != null) { 153 exchange.setProperty(Exchange.EXCEPTION_CAUGHT, caught); 154 } 155 if (errorhandlerHandled != null) { 156 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, errorhandlerHandled); 157 } 158 if (rollbackOnly != null) { 159 exchange.setProperty(Exchange.ROLLBACK_ONLY, rollbackOnly); 160 } 161 if (rollbackOnlyLast != null) { 162 exchange.setProperty(Exchange.ROLLBACK_ONLY, rollbackOnlyLast); 163 } 164 if (cause != null) { 165 exchange.setException(cause); 166 } 167 } 168 } 169 170 /** 171 * Prepares the {@link Exchange} to send as onCompletion. 172 * 173 * @param exchange the current exchange 174 * @return the exchange to be routed in onComplete 175 */ 176 protected Exchange prepareExchange(Exchange exchange) { 177 Exchange answer; 178 179 if (isCreateCopy()) { 180 // for asynchronous routing we must use a copy as we dont want it 181 // to cause side effects of the original exchange 182 // (the original thread will run in parallel) 183 answer = ExchangeHelper.createCorrelatedCopy(exchange, false); 184 if (answer.hasOut()) { 185 // move OUT to IN (pipes and filters) 186 answer.setIn(answer.getOut()); 187 answer.setOut(null); 188 } 189 // set MEP to InOnly as this onCompletion is a fire and forget 190 answer.setPattern(ExchangePattern.InOnly); 191 } else { 192 // use the exchange as-is 193 answer = exchange; 194 } 195 196 if (useOriginalBody) { 197 LOG.trace("Using the original IN message instead of current"); 198 199 Message original = exchange.getUnitOfWork().getOriginalInMessage(); 200 answer.setIn(original); 201 } 202 203 // add a header flag to indicate its a on completion exchange 204 answer.setProperty(Exchange.ON_COMPLETION, Boolean.TRUE); 205 206 return answer; 207 } 208 209 private final class OnCompletionSynchronizationAfterConsumer extends SynchronizationAdapter implements Ordered { 210 211 public int getOrder() { 212 // we want to be last 213 return Ordered.LOWEST; 214 } 215 216 @Override 217 public void onComplete(final Exchange exchange) { 218 if (onFailureOnly) { 219 return; 220 } 221 222 if (onWhen != null && !onWhen.matches(exchange)) { 223 // predicate did not match so do not route the onComplete 224 return; 225 } 226 227 // must use a copy as we dont want it to cause side effects of the original exchange 228 final Exchange copy = prepareExchange(exchange); 229 230 if (executorService != null) { 231 executorService.submit(new Callable<Exchange>() { 232 public Exchange call() throws Exception { 233 LOG.debug("Processing onComplete: {}", copy); 234 doProcess(processor, copy); 235 return copy; 236 } 237 }); 238 } else { 239 // run without thread-pool 240 LOG.debug("Processing onComplete: {}", copy); 241 doProcess(processor, copy); 242 } 243 } 244 245 public void onFailure(final Exchange exchange) { 246 if (onCompleteOnly) { 247 return; 248 } 249 250 if (onWhen != null && !onWhen.matches(exchange)) { 251 // predicate did not match so do not route the onComplete 252 return; 253 } 254 255 256 // must use a copy as we dont want it to cause side effects of the original exchange 257 final Exchange copy = prepareExchange(exchange); 258 final Exception original = copy.getException(); 259 final boolean originalFault = copy.hasOut() ? copy.getOut().isFault() : copy.getIn().isFault(); 260 // must remove exception otherwise onFailure routing will fail as well 261 // the caused exception is stored as a property (Exchange.EXCEPTION_CAUGHT) on the exchange 262 copy.setException(null); 263 // must clear fault otherwise onFailure routing will fail as well 264 if (copy.hasOut()) { 265 copy.getOut().setFault(false); 266 } else { 267 copy.getIn().setFault(false); 268 } 269 270 if (executorService != null) { 271 executorService.submit(new Callable<Exchange>() { 272 public Exchange call() throws Exception { 273 LOG.debug("Processing onFailure: {}", copy); 274 doProcess(processor, copy); 275 // restore exception after processing 276 copy.setException(original); 277 return null; 278 } 279 }); 280 } else { 281 // run without thread-pool 282 LOG.debug("Processing onFailure: {}", copy); 283 doProcess(processor, copy); 284 // restore exception after processing 285 copy.setException(original); 286 // restore fault after processing 287 if (copy.hasOut()) { 288 copy.getOut().setFault(originalFault); 289 } else { 290 copy.getIn().setFault(originalFault); 291 } 292 } 293 } 294 295 @Override 296 public String toString() { 297 if (!onCompleteOnly && !onFailureOnly) { 298 return "onCompleteOrFailure"; 299 } else if (onCompleteOnly) { 300 return "onCompleteOnly"; 301 } else { 302 return "onFailureOnly"; 303 } 304 } 305 } 306 307 private final class OnCompletionSynchronizationBeforeConsumer extends SynchronizationAdapter implements Ordered { 308 309 public int getOrder() { 310 // we want to be last 311 return Ordered.LOWEST; 312 } 313 314 @Override 315 public void onAfterRoute(Route route, Exchange exchange) { 316 if (exchange.isFailed() && onCompleteOnly) { 317 return; 318 } 319 320 if (!exchange.isFailed() && onFailureOnly) { 321 return; 322 } 323 324 if (onWhen != null && !onWhen.matches(exchange)) { 325 // predicate did not match so do not route the onComplete 326 return; 327 } 328 329 // must use a copy as we dont want it to cause side effects of the original exchange 330 final Exchange copy = prepareExchange(exchange); 331 332 if (executorService != null) { 333 executorService.submit(new Callable<Exchange>() { 334 public Exchange call() throws Exception { 335 LOG.debug("Processing onAfterRoute: {}", copy); 336 doProcess(processor, copy); 337 return copy; 338 } 339 }); 340 } else { 341 // run without thread-pool 342 LOG.debug("Processing onAfterRoute: {}", copy); 343 doProcess(processor, copy); 344 } 345 } 346 347 @Override 348 public String toString() { 349 return "onAfterRoute"; 350 } 351 } 352 353 @Override 354 public String toString() { 355 return "OnCompletionProcessor[" + processor + "]"; 356 } 357 358 public String getTraceLabel() { 359 return "onCompletion"; 360 } 361}