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