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