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