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.Date; 021import java.util.LinkedList; 022import java.util.List; 023import java.util.concurrent.RejectedExecutionException; 024 025import org.apache.camel.AsyncCallback; 026import org.apache.camel.CamelContext; 027import org.apache.camel.Exchange; 028import org.apache.camel.MessageHistory; 029import org.apache.camel.Ordered; 030import org.apache.camel.Processor; 031import org.apache.camel.Route; 032import org.apache.camel.StatefulService; 033import org.apache.camel.StreamCache; 034import org.apache.camel.api.management.PerformanceCounter; 035import org.apache.camel.management.DelegatePerformanceCounter; 036import org.apache.camel.management.mbean.ManagedPerformanceCounter; 037import org.apache.camel.model.ProcessorDefinition; 038import org.apache.camel.model.ProcessorDefinitionHelper; 039import org.apache.camel.processor.interceptor.BacklogDebugger; 040import org.apache.camel.processor.interceptor.BacklogTracer; 041import org.apache.camel.processor.interceptor.DefaultBacklogTracerEventMessage; 042import org.apache.camel.spi.InflightRepository; 043import org.apache.camel.spi.MessageHistoryFactory; 044import org.apache.camel.spi.RouteContext; 045import org.apache.camel.spi.RoutePolicy; 046import org.apache.camel.spi.StreamCachingStrategy; 047import org.apache.camel.spi.Transformer; 048import org.apache.camel.spi.UnitOfWork; 049import org.apache.camel.util.MessageHelper; 050import org.apache.camel.util.OrderedComparator; 051import org.apache.camel.util.StopWatch; 052import org.apache.camel.util.UnitOfWorkHelper; 053import org.slf4j.Logger; 054import org.slf4j.LoggerFactory; 055 056/** 057 * Internal {@link Processor} that Camel routing engine used during routing for cross cutting functionality such as: 058 * <ul> 059 * <li>Execute {@link UnitOfWork}</li> 060 * <li>Keeping track which route currently is being routed</li> 061 * <li>Execute {@link RoutePolicy}</li> 062 * <li>Gather JMX performance statics</li> 063 * <li>Tracing</li> 064 * <li>Debugging</li> 065 * <li>Message History</li> 066 * <li>Stream Caching</li> 067 * <li>{@link Transformer}</li> 068 * </ul> 069 * ... and more. 070 * <p/> 071 * This implementation executes this cross cutting functionality as a {@link CamelInternalProcessorAdvice} advice (before and after advice) 072 * by executing the {@link CamelInternalProcessorAdvice#before(org.apache.camel.Exchange)} and 073 * {@link CamelInternalProcessorAdvice#after(org.apache.camel.Exchange, Object)} callbacks in correct order during routing. 074 * This reduces number of stack frames needed during routing, and reduce the number of lines in stacktraces, as well 075 * makes debugging the routing engine easier for end users. 076 * <p/> 077 * <b>Debugging tips:</b> Camel end users whom want to debug their Camel applications with the Camel source code, then make sure to 078 * read the source code of this class about the debugging tips, which you can find in the 079 * {@link #process(org.apache.camel.Exchange, org.apache.camel.AsyncCallback)} method. 080 * <p/> 081 * The added advices can implement {@link Ordered} to control in which order the advices are executed. 082 */ 083public class CamelInternalProcessor extends DelegateAsyncProcessor { 084 085 private static final Logger LOG = LoggerFactory.getLogger(CamelInternalProcessor.class); 086 private final List<CamelInternalProcessorAdvice> advices = new ArrayList<CamelInternalProcessorAdvice>(); 087 088 public CamelInternalProcessor() { 089 } 090 091 public CamelInternalProcessor(Processor processor) { 092 super(processor); 093 } 094 095 /** 096 * Adds an {@link CamelInternalProcessorAdvice} advice to the list of advices to execute by this internal processor. 097 * 098 * @param advice the advice to add 099 */ 100 public void addAdvice(CamelInternalProcessorAdvice advice) { 101 advices.add(advice); 102 // ensure advices are sorted so they are in the order we want 103 advices.sort(OrderedComparator.get()); 104 } 105 106 /** 107 * Gets the advice with the given type. 108 * 109 * @param type the type of the advice 110 * @return the advice if exists, or <tt>null</tt> if no advices has been added with the given type. 111 */ 112 public <T> T getAdvice(Class<T> type) { 113 for (CamelInternalProcessorAdvice task : advices) { 114 if (type.isInstance(task)) { 115 return type.cast(task); 116 } 117 } 118 return null; 119 } 120 121 @Override 122 public boolean process(Exchange exchange, AsyncCallback callback) { 123 // ---------------------------------------------------------- 124 // CAMEL END USER - READ ME FOR DEBUGGING TIPS 125 // ---------------------------------------------------------- 126 // If you want to debug the Camel routing engine, then there is a lot of internal functionality 127 // the routing engine executes during routing messages. You can skip debugging this internal 128 // functionality and instead debug where the routing engine continues routing to the next node 129 // in the routes. The CamelInternalProcessor is a vital part of the routing engine, as its 130 // being used in between the nodes. As an end user you can just debug the code in this class 131 // in between the: 132 // CAMEL END USER - DEBUG ME HERE +++ START +++ 133 // CAMEL END USER - DEBUG ME HERE +++ END +++ 134 // you can see in the code below. 135 // ---------------------------------------------------------- 136 137 if (processor == null || !continueProcessing(exchange)) { 138 // no processor or we should not continue then we are done 139 callback.done(true); 140 return true; 141 } 142 143 // optimise to use object array for states 144 final Object[] states = new Object[advices.size()]; 145 // optimise for loop using index access to avoid creating iterator object 146 for (int i = 0; i < advices.size(); i++) { 147 CamelInternalProcessorAdvice task = advices.get(i); 148 try { 149 Object state = task.before(exchange); 150 states[i] = state; 151 } catch (Throwable e) { 152 exchange.setException(e); 153 callback.done(true); 154 return true; 155 } 156 } 157 158 // create internal callback which will execute the advices in reverse order when done 159 callback = new InternalCallback(states, exchange, callback); 160 161 // UNIT_OF_WORK_PROCESS_SYNC is @deprecated and we should remove it from Camel 3.0 162 Object synchronous = exchange.removeProperty(Exchange.UNIT_OF_WORK_PROCESS_SYNC); 163 if (exchange.isTransacted() || synchronous != null) { 164 // must be synchronized for transacted exchanges 165 if (LOG.isTraceEnabled()) { 166 if (exchange.isTransacted()) { 167 LOG.trace("Transacted Exchange must be routed synchronously for exchangeId: {} -> {}", exchange.getExchangeId(), exchange); 168 } else { 169 LOG.trace("Synchronous UnitOfWork Exchange must be routed synchronously for exchangeId: {} -> {}", exchange.getExchangeId(), exchange); 170 } 171 } 172 // ---------------------------------------------------------- 173 // CAMEL END USER - DEBUG ME HERE +++ START +++ 174 // ---------------------------------------------------------- 175 try { 176 processor.process(exchange); 177 } catch (Throwable e) { 178 exchange.setException(e); 179 } 180 // ---------------------------------------------------------- 181 // CAMEL END USER - DEBUG ME HERE +++ END +++ 182 // ---------------------------------------------------------- 183 callback.done(true); 184 return true; 185 } else { 186 final UnitOfWork uow = exchange.getUnitOfWork(); 187 188 // allow unit of work to wrap callback in case it need to do some special work 189 // for example the MDCUnitOfWork 190 AsyncCallback async = callback; 191 if (uow != null) { 192 async = uow.beforeProcess(processor, exchange, callback); 193 } 194 195 // ---------------------------------------------------------- 196 // CAMEL END USER - DEBUG ME HERE +++ START +++ 197 // ---------------------------------------------------------- 198 if (LOG.isTraceEnabled()) { 199 LOG.trace("Processing exchange for exchangeId: {} -> {}", exchange.getExchangeId(), exchange); 200 } 201 boolean sync = processor.process(exchange, async); 202 // ---------------------------------------------------------- 203 // CAMEL END USER - DEBUG ME HERE +++ END +++ 204 // ---------------------------------------------------------- 205 206 // execute any after processor work (in current thread, not in the callback) 207 if (uow != null) { 208 uow.afterProcess(processor, exchange, callback, sync); 209 } 210 211 if (LOG.isTraceEnabled()) { 212 LOG.trace("Exchange processed and is continued routed {} for exchangeId: {} -> {}", 213 new Object[]{sync ? "synchronously" : "asynchronously", exchange.getExchangeId(), exchange}); 214 } 215 return sync; 216 } 217 } 218 219 @Override 220 public String toString() { 221 return processor != null ? processor.toString() : super.toString(); 222 } 223 224 /** 225 * Internal callback that executes the after advices. 226 */ 227 private final class InternalCallback implements AsyncCallback { 228 229 private final Object[] states; 230 private final Exchange exchange; 231 private final AsyncCallback callback; 232 233 private InternalCallback(Object[] states, Exchange exchange, AsyncCallback callback) { 234 this.states = states; 235 this.exchange = exchange; 236 this.callback = callback; 237 } 238 239 @Override 240 @SuppressWarnings("unchecked") 241 public void done(boolean doneSync) { 242 // NOTE: if you are debugging Camel routes, then all the code in the for loop below is internal only 243 // so you can step straight to the finally block and invoke the callback 244 245 // we should call after in reverse order 246 try { 247 for (int i = advices.size() - 1; i >= 0; i--) { 248 CamelInternalProcessorAdvice task = advices.get(i); 249 Object state = states[i]; 250 try { 251 task.after(exchange, state); 252 } catch (Throwable e) { 253 exchange.setException(e); 254 // allow all advices to complete even if there was an exception 255 } 256 } 257 } finally { 258 // ---------------------------------------------------------- 259 // CAMEL END USER - DEBUG ME HERE +++ START +++ 260 // ---------------------------------------------------------- 261 // callback must be called 262 callback.done(doneSync); 263 // ---------------------------------------------------------- 264 // CAMEL END USER - DEBUG ME HERE +++ END +++ 265 // ---------------------------------------------------------- 266 } 267 } 268 } 269 270 /** 271 * Strategy to determine if we should continue processing the {@link Exchange}. 272 */ 273 protected boolean continueProcessing(Exchange exchange) { 274 Object stop = exchange.getProperty(Exchange.ROUTE_STOP); 275 if (stop != null) { 276 boolean doStop = exchange.getContext().getTypeConverter().convertTo(Boolean.class, stop); 277 if (doStop) { 278 LOG.debug("Exchange is marked to stop routing: {}", exchange); 279 return false; 280 } 281 } 282 283 // determine if we can still run, or the camel context is forcing a shutdown 284 boolean forceShutdown = exchange.getContext().getShutdownStrategy().forceShutdown(this); 285 if (forceShutdown) { 286 String msg = "Run not allowed as ShutdownStrategy is forcing shutting down, will reject executing exchange: " + exchange; 287 LOG.debug(msg); 288 if (exchange.getException() == null) { 289 exchange.setException(new RejectedExecutionException(msg)); 290 } 291 return false; 292 } 293 294 // yes we can continue 295 return true; 296 } 297 298 /** 299 * Advice to invoke callbacks for before and after routing. 300 */ 301 public static class RouteLifecycleAdvice implements CamelInternalProcessorAdvice<Object> { 302 303 private Route route; 304 305 public void setRoute(Route route) { 306 this.route = route; 307 } 308 309 @Override 310 public Object before(Exchange exchange) throws Exception { 311 UnitOfWork uow = exchange.getUnitOfWork(); 312 if (uow != null) { 313 uow.beforeRoute(exchange, route); 314 } 315 return null; 316 } 317 318 @Override 319 public void after(Exchange exchange, Object object) throws Exception { 320 UnitOfWork uow = exchange.getUnitOfWork(); 321 if (uow != null) { 322 uow.afterRoute(exchange, route); 323 } 324 } 325 } 326 327 /** 328 * Advice for JMX instrumentation of the process being invoked. 329 * <p/> 330 * This advice keeps track of JMX metrics for performance statistics. 331 * <p/> 332 * The current implementation of this advice is only used for route level statistics. For processor levels 333 * they are still wrapped in the route processor chains. 334 */ 335 public static class InstrumentationAdvice implements CamelInternalProcessorAdvice<StopWatch> { 336 337 private PerformanceCounter counter; 338 private String type; 339 340 public InstrumentationAdvice(String type) { 341 this.type = type; 342 } 343 344 public void setCounter(Object counter) { 345 ManagedPerformanceCounter mpc = null; 346 if (counter instanceof ManagedPerformanceCounter) { 347 mpc = (ManagedPerformanceCounter) counter; 348 } 349 350 if (this.counter instanceof DelegatePerformanceCounter) { 351 ((DelegatePerformanceCounter) this.counter).setCounter(mpc); 352 } else if (mpc != null) { 353 this.counter = mpc; 354 } else if (counter instanceof PerformanceCounter) { 355 this.counter = (PerformanceCounter) counter; 356 } 357 } 358 359 protected void beginTime(Exchange exchange) { 360 counter.processExchange(exchange); 361 } 362 363 protected void recordTime(Exchange exchange, long duration) { 364 if (LOG.isTraceEnabled()) { 365 LOG.trace("{}Recording duration: {} millis for exchange: {}", new Object[]{type != null ? type + ": " : "", duration, exchange}); 366 } 367 368 if (!exchange.isFailed() && exchange.getException() == null) { 369 counter.completedExchange(exchange, duration); 370 } else { 371 counter.failedExchange(exchange); 372 } 373 } 374 375 public String getType() { 376 return type; 377 } 378 379 public void setType(String type) { 380 this.type = type; 381 } 382 383 @Override 384 public StopWatch before(Exchange exchange) throws Exception { 385 // only record time if stats is enabled 386 StopWatch answer = counter != null && counter.isStatisticsEnabled() ? new StopWatch() : null; 387 if (answer != null) { 388 beginTime(exchange); 389 } 390 return answer; 391 } 392 393 @Override 394 public void after(Exchange exchange, StopWatch watch) throws Exception { 395 // record end time 396 if (watch != null) { 397 recordTime(exchange, watch.taken()); 398 } 399 } 400 } 401 402 /** 403 * Advice to inject the current {@link RouteContext} into the {@link UnitOfWork} on the {@link Exchange} 404 * 405 * @deprecated this logic has been merged into {@link org.apache.camel.processor.CamelInternalProcessor.UnitOfWorkProcessorAdvice} 406 */ 407 @Deprecated 408 public static class RouteContextAdvice implements CamelInternalProcessorAdvice<UnitOfWork> { 409 410 private final RouteContext routeContext; 411 412 public RouteContextAdvice(RouteContext routeContext) { 413 this.routeContext = routeContext; 414 } 415 416 @Override 417 public UnitOfWork before(Exchange exchange) throws Exception { 418 // push the current route context 419 final UnitOfWork unitOfWork = exchange.getUnitOfWork(); 420 if (unitOfWork != null) { 421 unitOfWork.pushRouteContext(routeContext); 422 } 423 return unitOfWork; 424 } 425 426 @Override 427 public void after(Exchange exchange, UnitOfWork unitOfWork) throws Exception { 428 if (unitOfWork != null) { 429 unitOfWork.popRouteContext(); 430 } 431 } 432 } 433 434 /** 435 * Advice to keep the {@link InflightRepository} up to date. 436 */ 437 public static class RouteInflightRepositoryAdvice implements CamelInternalProcessorAdvice { 438 439 private final InflightRepository inflightRepository; 440 private final String id; 441 442 public RouteInflightRepositoryAdvice(InflightRepository inflightRepository, String id) { 443 this.inflightRepository = inflightRepository; 444 this.id = id; 445 } 446 447 @Override 448 public Object before(Exchange exchange) throws Exception { 449 inflightRepository.add(exchange, id); 450 return null; 451 } 452 453 @Override 454 public void after(Exchange exchange, Object state) throws Exception { 455 inflightRepository.remove(exchange, id); 456 } 457 } 458 459 /** 460 * Advice to execute any {@link RoutePolicy} a route may have been configured with. 461 */ 462 public static class RoutePolicyAdvice implements CamelInternalProcessorAdvice { 463 464 private final List<RoutePolicy> routePolicies; 465 private Route route; 466 467 public RoutePolicyAdvice(List<RoutePolicy> routePolicies) { 468 this.routePolicies = routePolicies; 469 } 470 471 public void setRoute(Route route) { 472 this.route = route; 473 } 474 475 /** 476 * Strategy to determine if this policy is allowed to run 477 * 478 * @param policy the policy 479 * @return <tt>true</tt> to run 480 */ 481 protected boolean isRoutePolicyRunAllowed(RoutePolicy policy) { 482 if (policy instanceof StatefulService) { 483 StatefulService ss = (StatefulService) policy; 484 return ss.isRunAllowed(); 485 } 486 return true; 487 } 488 489 @Override 490 public Object before(Exchange exchange) throws Exception { 491 // invoke begin 492 for (RoutePolicy policy : routePolicies) { 493 try { 494 if (isRoutePolicyRunAllowed(policy)) { 495 policy.onExchangeBegin(route, exchange); 496 } 497 } catch (Exception e) { 498 LOG.warn("Error occurred during onExchangeBegin on RoutePolicy: " + policy 499 + ". This exception will be ignored", e); 500 } 501 } 502 return null; 503 } 504 505 @Override 506 public void after(Exchange exchange, Object data) throws Exception { 507 // do not invoke it if Camel is stopping as we don't want 508 // the policy to start a consumer during Camel is stopping 509 if (isCamelStopping(exchange.getContext())) { 510 return; 511 } 512 513 for (RoutePolicy policy : routePolicies) { 514 try { 515 if (isRoutePolicyRunAllowed(policy)) { 516 policy.onExchangeDone(route, exchange); 517 } 518 } catch (Exception e) { 519 LOG.warn("Error occurred during onExchangeDone on RoutePolicy: " + policy 520 + ". This exception will be ignored", e); 521 } 522 } 523 } 524 525 private static boolean isCamelStopping(CamelContext context) { 526 if (context instanceof StatefulService) { 527 StatefulService ss = (StatefulService) context; 528 return ss.isStopping() || ss.isStopped(); 529 } 530 return false; 531 } 532 } 533 534 /** 535 * Advice to execute the {@link BacklogTracer} if enabled. 536 */ 537 public static final class BacklogTracerAdvice implements CamelInternalProcessorAdvice, Ordered { 538 539 private final BacklogTracer backlogTracer; 540 private final ProcessorDefinition<?> processorDefinition; 541 private final ProcessorDefinition<?> routeDefinition; 542 private final boolean first; 543 544 public BacklogTracerAdvice(BacklogTracer backlogTracer, ProcessorDefinition<?> processorDefinition, 545 ProcessorDefinition<?> routeDefinition, boolean first) { 546 this.backlogTracer = backlogTracer; 547 this.processorDefinition = processorDefinition; 548 this.routeDefinition = routeDefinition; 549 this.first = first; 550 } 551 552 @Override 553 public Object before(Exchange exchange) throws Exception { 554 if (backlogTracer.shouldTrace(processorDefinition, exchange)) { 555 Date timestamp = new Date(); 556 String toNode = processorDefinition.getId(); 557 String exchangeId = exchange.getExchangeId(); 558 String messageAsXml = MessageHelper.dumpAsXml(exchange.getIn(), true, 4, 559 backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(), backlogTracer.getBodyMaxChars()); 560 561 // if first we should add a pseudo trace message as well, so we have a starting message (eg from the route) 562 String routeId = routeDefinition != null ? routeDefinition.getId() : null; 563 if (first) { 564 Date created = exchange.getProperty(Exchange.CREATED_TIMESTAMP, timestamp, Date.class); 565 DefaultBacklogTracerEventMessage pseudo = new DefaultBacklogTracerEventMessage(backlogTracer.incrementTraceCounter(), created, routeId, null, exchangeId, messageAsXml); 566 backlogTracer.traceEvent(pseudo); 567 } 568 DefaultBacklogTracerEventMessage event = new DefaultBacklogTracerEventMessage(backlogTracer.incrementTraceCounter(), timestamp, routeId, toNode, exchangeId, messageAsXml); 569 backlogTracer.traceEvent(event); 570 } 571 572 return null; 573 } 574 575 @Override 576 public void after(Exchange exchange, Object data) throws Exception { 577 // noop 578 } 579 580 @Override 581 public int getOrder() { 582 // we want tracer just before calling the processor 583 return Ordered.LOWEST - 1; 584 } 585 586 } 587 588 /** 589 * Advice to execute the {@link org.apache.camel.processor.interceptor.BacklogDebugger} if enabled. 590 */ 591 public static final class BacklogDebuggerAdvice implements CamelInternalProcessorAdvice<StopWatch>, Ordered { 592 593 private final BacklogDebugger backlogDebugger; 594 private final Processor target; 595 private final ProcessorDefinition<?> definition; 596 private final String nodeId; 597 598 public BacklogDebuggerAdvice(BacklogDebugger backlogDebugger, Processor target, ProcessorDefinition<?> definition) { 599 this.backlogDebugger = backlogDebugger; 600 this.target = target; 601 this.definition = definition; 602 this.nodeId = definition.getId(); 603 } 604 605 @Override 606 public StopWatch before(Exchange exchange) throws Exception { 607 if (backlogDebugger.isEnabled() && (backlogDebugger.hasBreakpoint(nodeId) || backlogDebugger.isSingleStepMode())) { 608 StopWatch watch = new StopWatch(); 609 backlogDebugger.beforeProcess(exchange, target, definition); 610 return watch; 611 } else { 612 return null; 613 } 614 } 615 616 @Override 617 public void after(Exchange exchange, StopWatch stopWatch) throws Exception { 618 if (stopWatch != null) { 619 backlogDebugger.afterProcess(exchange, target, definition, stopWatch.taken()); 620 } 621 } 622 623 @Override 624 public int getOrder() { 625 // we want debugger just before calling the processor 626 return Ordered.LOWEST; 627 } 628 } 629 630 /** 631 * Advice to inject new {@link UnitOfWork} to the {@link Exchange} if needed, and as well to ensure 632 * the {@link UnitOfWork} is done and stopped. 633 */ 634 public static class UnitOfWorkProcessorAdvice implements CamelInternalProcessorAdvice<UnitOfWork> { 635 636 private final RouteContext routeContext; 637 private String routeId; 638 639 public UnitOfWorkProcessorAdvice(RouteContext routeContext) { 640 this.routeContext = routeContext; 641 if (routeContext != null) { 642 this.routeId = routeContext.getRoute().idOrCreate(routeContext.getCamelContext().getNodeIdFactory()); 643 } 644 } 645 646 @Override 647 public UnitOfWork before(Exchange exchange) throws Exception { 648 // if the exchange doesn't have from route id set, then set it if it originated 649 // from this unit of work 650 if (routeContext != null && exchange.getFromRouteId() == null) { 651 if (routeId == null) { 652 routeId = routeContext.getRoute().idOrCreate(routeContext.getCamelContext().getNodeIdFactory()); 653 } 654 exchange.setFromRouteId(routeId); 655 } 656 657 // only return UnitOfWork if we created a new as then its us that handle the lifecycle to done the created UoW 658 UnitOfWork created = null; 659 660 if (exchange.getUnitOfWork() == null) { 661 // If there is no existing UoW, then we should start one and 662 // terminate it once processing is completed for the exchange. 663 created = createUnitOfWork(exchange); 664 exchange.setUnitOfWork(created); 665 created.start(); 666 } 667 668 // for any exchange we should push/pop route context so we can keep track of which route we are routing 669 if (routeContext != null) { 670 UnitOfWork existing = exchange.getUnitOfWork(); 671 if (existing != null) { 672 existing.pushRouteContext(routeContext); 673 } 674 } 675 676 return created; 677 } 678 679 @Override 680 public void after(Exchange exchange, UnitOfWork uow) throws Exception { 681 UnitOfWork existing = exchange.getUnitOfWork(); 682 683 // execute done on uow if we created it, and the consumer is not doing it 684 if (uow != null) { 685 UnitOfWorkHelper.doneUow(uow, exchange); 686 } 687 688 // after UoW is done lets pop the route context which must be done on every existing UoW 689 if (routeContext != null && existing != null) { 690 existing.popRouteContext(); 691 } 692 } 693 694 protected UnitOfWork createUnitOfWork(Exchange exchange) { 695 return exchange.getContext().getUnitOfWorkFactory().createUnitOfWork(exchange); 696 } 697 698 } 699 700 /** 701 * Advice when an EIP uses the <tt>shareUnitOfWork</tt> functionality. 702 */ 703 public static class ChildUnitOfWorkProcessorAdvice extends UnitOfWorkProcessorAdvice { 704 705 private final UnitOfWork parent; 706 707 public ChildUnitOfWorkProcessorAdvice(RouteContext routeContext, UnitOfWork parent) { 708 super(routeContext); 709 this.parent = parent; 710 } 711 712 @Override 713 protected UnitOfWork createUnitOfWork(Exchange exchange) { 714 // let the parent create a child unit of work to be used 715 return parent.createChildUnitOfWork(exchange); 716 } 717 718 } 719 720 /** 721 * Advice when an EIP uses the <tt>shareUnitOfWork</tt> functionality. 722 */ 723 public static class SubUnitOfWorkProcessorAdvice implements CamelInternalProcessorAdvice<UnitOfWork> { 724 725 @Override 726 public UnitOfWork before(Exchange exchange) throws Exception { 727 // begin savepoint 728 exchange.getUnitOfWork().beginSubUnitOfWork(exchange); 729 return exchange.getUnitOfWork(); 730 } 731 732 @Override 733 public void after(Exchange exchange, UnitOfWork unitOfWork) throws Exception { 734 // end sub unit of work 735 unitOfWork.endSubUnitOfWork(exchange); 736 } 737 } 738 739 /** 740 * Advice when Message History has been enabled. 741 */ 742 @SuppressWarnings("unchecked") 743 public static class MessageHistoryAdvice implements CamelInternalProcessorAdvice<MessageHistory> { 744 745 private final MessageHistoryFactory factory; 746 private final ProcessorDefinition<?> definition; 747 private final String routeId; 748 749 public MessageHistoryAdvice(MessageHistoryFactory factory, ProcessorDefinition<?> definition) { 750 this.factory = factory; 751 this.definition = definition; 752 this.routeId = ProcessorDefinitionHelper.getRouteId(definition); 753 } 754 755 @Override 756 public MessageHistory before(Exchange exchange) throws Exception { 757 List<MessageHistory> list = exchange.getProperty(Exchange.MESSAGE_HISTORY, List.class); 758 if (list == null) { 759 list = new LinkedList<>(); 760 exchange.setProperty(Exchange.MESSAGE_HISTORY, list); 761 } 762 763 // we may be routing outside a route in an onException or interceptor and if so then grab 764 // route id from the exchange UoW state 765 String targetRouteId = this.routeId; 766 if (targetRouteId == null) { 767 UnitOfWork uow = exchange.getUnitOfWork(); 768 if (uow != null && uow.getRouteContext() != null) { 769 targetRouteId = uow.getRouteContext().getRoute().getId(); 770 } 771 } 772 773 MessageHistory history = factory.newMessageHistory(targetRouteId, definition, System.currentTimeMillis()); 774 list.add(history); 775 return history; 776 } 777 778 @Override 779 public void after(Exchange exchange, MessageHistory history) throws Exception { 780 if (history != null) { 781 history.nodeProcessingDone(); 782 } 783 } 784 } 785 786 /** 787 * Advice for {@link org.apache.camel.spi.StreamCachingStrategy} 788 */ 789 public static class StreamCachingAdvice implements CamelInternalProcessorAdvice<StreamCache>, Ordered { 790 791 private final StreamCachingStrategy strategy; 792 793 public StreamCachingAdvice(StreamCachingStrategy strategy) { 794 this.strategy = strategy; 795 } 796 797 @Override 798 public StreamCache before(Exchange exchange) throws Exception { 799 // check if body is already cached 800 Object body = exchange.getIn().getBody(); 801 if (body == null) { 802 return null; 803 } else if (body instanceof StreamCache) { 804 StreamCache sc = (StreamCache) body; 805 // reset so the cache is ready to be used before processing 806 sc.reset(); 807 return sc; 808 } 809 // cache the body and if we could do that replace it as the new body 810 StreamCache sc = strategy.cache(exchange); 811 if (sc != null) { 812 exchange.getIn().setBody(sc); 813 } 814 return sc; 815 } 816 817 @Override 818 public void after(Exchange exchange, StreamCache sc) throws Exception { 819 Object body; 820 if (exchange.hasOut()) { 821 body = exchange.getOut().getBody(); 822 } else { 823 body = exchange.getIn().getBody(); 824 } 825 if (body != null && body instanceof StreamCache) { 826 // reset so the cache is ready to be reused after processing 827 ((StreamCache) body).reset(); 828 } 829 } 830 831 @Override 832 public int getOrder() { 833 // we want stream caching first 834 return Ordered.HIGHEST; 835 } 836 } 837 838 /** 839 * Advice for delaying 840 */ 841 public static class DelayerAdvice implements CamelInternalProcessorAdvice { 842 843 private final long delay; 844 845 public DelayerAdvice(long delay) { 846 this.delay = delay; 847 } 848 849 @Override 850 public Object before(Exchange exchange) throws Exception { 851 try { 852 LOG.trace("Sleeping for: {} millis", delay); 853 Thread.sleep(delay); 854 } catch (InterruptedException e) { 855 LOG.debug("Sleep interrupted"); 856 Thread.currentThread().interrupt(); 857 throw e; 858 } 859 return null; 860 } 861 862 @Override 863 public void after(Exchange exchange, Object data) throws Exception { 864 // noop 865 } 866 } 867}