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 recordTime(Exchange exchange, long duration) { 348 if (LOG.isTraceEnabled()) { 349 LOG.trace("{}Recording duration: {} millis for exchange: {}", new Object[]{type != null ? type + ": " : "", duration, exchange}); 350 } 351 352 if (!exchange.isFailed() && exchange.getException() == null) { 353 counter.completedExchange(exchange, duration); 354 } else { 355 counter.failedExchange(exchange); 356 } 357 } 358 359 public String getType() { 360 return type; 361 } 362 363 public void setType(String type) { 364 this.type = type; 365 } 366 367 @Override 368 public StopWatch before(Exchange exchange) throws Exception { 369 // only record time if stats is enabled 370 return (counter != null && counter.isStatisticsEnabled()) ? new StopWatch() : null; 371 } 372 373 @Override 374 public void after(Exchange exchange, StopWatch watch) throws Exception { 375 // record end time 376 if (watch != null) { 377 recordTime(exchange, watch.stop()); 378 } 379 } 380 } 381 382 /** 383 * Advice to inject the current {@link RouteContext} into the {@link UnitOfWork} on the {@link Exchange} 384 */ 385 public static class RouteContextAdvice implements CamelInternalProcessorAdvice<UnitOfWork> { 386 387 private final RouteContext routeContext; 388 389 public RouteContextAdvice(RouteContext routeContext) { 390 this.routeContext = routeContext; 391 } 392 393 @Override 394 public UnitOfWork before(Exchange exchange) throws Exception { 395 // push the current route context 396 final UnitOfWork unitOfWork = exchange.getUnitOfWork(); 397 if (unitOfWork != null) { 398 unitOfWork.pushRouteContext(routeContext); 399 } 400 return unitOfWork; 401 } 402 403 @Override 404 public void after(Exchange exchange, UnitOfWork unitOfWork) throws Exception { 405 if (unitOfWork != null) { 406 unitOfWork.popRouteContext(); 407 } 408 } 409 } 410 411 /** 412 * Advice to keep the {@link InflightRepository} up to date. 413 */ 414 public static class RouteInflightRepositoryAdvice implements CamelInternalProcessorAdvice { 415 416 private final InflightRepository inflightRepository; 417 private final String id; 418 419 public RouteInflightRepositoryAdvice(InflightRepository inflightRepository, String id) { 420 this.inflightRepository = inflightRepository; 421 this.id = id; 422 } 423 424 @Override 425 public Object before(Exchange exchange) throws Exception { 426 inflightRepository.add(exchange, id); 427 return null; 428 } 429 430 @Override 431 public void after(Exchange exchange, Object state) throws Exception { 432 inflightRepository.remove(exchange, id); 433 } 434 } 435 436 /** 437 * Advice to execute any {@link RoutePolicy} a route may have been configured with. 438 */ 439 public static class RoutePolicyAdvice implements CamelInternalProcessorAdvice { 440 441 private final List<RoutePolicy> routePolicies; 442 private Route route; 443 444 public RoutePolicyAdvice(List<RoutePolicy> routePolicies) { 445 this.routePolicies = routePolicies; 446 } 447 448 public void setRoute(Route route) { 449 this.route = route; 450 } 451 452 /** 453 * Strategy to determine if this policy is allowed to run 454 * 455 * @param policy the policy 456 * @return <tt>true</tt> to run 457 */ 458 protected boolean isRoutePolicyRunAllowed(RoutePolicy policy) { 459 if (policy instanceof StatefulService) { 460 StatefulService ss = (StatefulService) policy; 461 return ss.isRunAllowed(); 462 } 463 return true; 464 } 465 466 @Override 467 public Object before(Exchange exchange) throws Exception { 468 // invoke begin 469 for (RoutePolicy policy : routePolicies) { 470 try { 471 if (isRoutePolicyRunAllowed(policy)) { 472 policy.onExchangeBegin(route, exchange); 473 } 474 } catch (Exception e) { 475 LOG.warn("Error occurred during onExchangeBegin on RoutePolicy: " + policy 476 + ". This exception will be ignored", e); 477 } 478 } 479 return null; 480 } 481 482 @Override 483 public void after(Exchange exchange, Object data) throws Exception { 484 // do not invoke it if Camel is stopping as we don't want 485 // the policy to start a consumer during Camel is stopping 486 if (isCamelStopping(exchange.getContext())) { 487 return; 488 } 489 490 for (RoutePolicy policy : routePolicies) { 491 try { 492 if (isRoutePolicyRunAllowed(policy)) { 493 policy.onExchangeDone(route, exchange); 494 } 495 } catch (Exception e) { 496 LOG.warn("Error occurred during onExchangeDone on RoutePolicy: " + policy 497 + ". This exception will be ignored", e); 498 } 499 } 500 } 501 502 private static boolean isCamelStopping(CamelContext context) { 503 if (context instanceof StatefulService) { 504 StatefulService ss = (StatefulService) context; 505 return ss.isStopping() || ss.isStopped(); 506 } 507 return false; 508 } 509 } 510 511 /** 512 * Advice to execute the {@link BacklogTracer} if enabled. 513 */ 514 public static final class BacklogTracerAdvice implements CamelInternalProcessorAdvice { 515 516 private final Queue<DefaultBacklogTracerEventMessage> queue; 517 private final BacklogTracer backlogTracer; 518 private final ProcessorDefinition<?> processorDefinition; 519 private final ProcessorDefinition<?> routeDefinition; 520 private final boolean first; 521 522 public BacklogTracerAdvice(Queue<DefaultBacklogTracerEventMessage> queue, BacklogTracer backlogTracer, 523 ProcessorDefinition<?> processorDefinition, ProcessorDefinition<?> routeDefinition, boolean first) { 524 this.queue = queue; 525 this.backlogTracer = backlogTracer; 526 this.processorDefinition = processorDefinition; 527 this.routeDefinition = routeDefinition; 528 this.first = first; 529 } 530 531 @Override 532 public Object before(Exchange exchange) throws Exception { 533 if (backlogTracer.shouldTrace(processorDefinition, exchange)) { 534 // ensure there is space on the queue 535 int drain = queue.size() - backlogTracer.getBacklogSize(); 536 // and we need room for ourselves and possible also a first pseudo message as well 537 drain += first ? 2 : 1; 538 if (drain > 0) { 539 for (int i = 0; i < drain; i++) { 540 queue.poll(); 541 } 542 } 543 544 Date timestamp = new Date(); 545 String toNode = processorDefinition.getId(); 546 String exchangeId = exchange.getExchangeId(); 547 String messageAsXml = MessageHelper.dumpAsXml(exchange.getIn(), true, 4, 548 backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(), backlogTracer.getBodyMaxChars()); 549 550 // if first we should add a pseudo trace message as well, so we have a starting message (eg from the route) 551 String routeId = routeDefinition != null ? routeDefinition.getId() : null; 552 if (first) { 553 Date created = exchange.getProperty(Exchange.CREATED_TIMESTAMP, timestamp, Date.class); 554 DefaultBacklogTracerEventMessage pseudo = new DefaultBacklogTracerEventMessage(backlogTracer.incrementTraceCounter(), created, routeId, null, exchangeId, messageAsXml); 555 queue.add(pseudo); 556 } 557 DefaultBacklogTracerEventMessage event = new DefaultBacklogTracerEventMessage(backlogTracer.incrementTraceCounter(), timestamp, routeId, toNode, exchangeId, messageAsXml); 558 queue.add(event); 559 } 560 561 return null; 562 } 563 564 @Override 565 public void after(Exchange exchange, Object data) throws Exception { 566 // noop 567 } 568 } 569 570 /** 571 * Advice to execute the {@link org.apache.camel.processor.interceptor.BacklogDebugger} if enabled. 572 */ 573 public static final class BacklogDebuggerAdvice implements CamelInternalProcessorAdvice<StopWatch> { 574 575 private final BacklogDebugger backlogDebugger; 576 private final Processor target; 577 private final ProcessorDefinition<?> definition; 578 private final String nodeId; 579 580 public BacklogDebuggerAdvice(BacklogDebugger backlogDebugger, Processor target, ProcessorDefinition<?> definition) { 581 this.backlogDebugger = backlogDebugger; 582 this.target = target; 583 this.definition = definition; 584 this.nodeId = definition.getId(); 585 } 586 587 @Override 588 public StopWatch before(Exchange exchange) throws Exception { 589 if (backlogDebugger.isEnabled() && (backlogDebugger.hasBreakpoint(nodeId) || backlogDebugger.isSingleStepMode())) { 590 StopWatch watch = new StopWatch(); 591 backlogDebugger.beforeProcess(exchange, target, definition); 592 return watch; 593 } else { 594 return null; 595 } 596 } 597 598 @Override 599 public void after(Exchange exchange, StopWatch stopWatch) throws Exception { 600 if (stopWatch != null) { 601 backlogDebugger.afterProcess(exchange, target, definition, stopWatch.stop()); 602 } 603 } 604 } 605 606 /** 607 * Advice to inject new {@link UnitOfWork} to the {@link Exchange} if needed, and as well to ensure 608 * the {@link UnitOfWork} is done and stopped. 609 */ 610 public static class UnitOfWorkProcessorAdvice implements CamelInternalProcessorAdvice<UnitOfWork> { 611 612 private final String routeId; 613 614 public UnitOfWorkProcessorAdvice(String routeId) { 615 this.routeId = routeId; 616 } 617 618 @Override 619 public UnitOfWork before(Exchange exchange) throws Exception { 620 // if the exchange doesn't have from route id set, then set it if it originated 621 // from this unit of work 622 if (routeId != null && exchange.getFromRouteId() == null) { 623 exchange.setFromRouteId(routeId); 624 } 625 626 if (exchange.getUnitOfWork() == null) { 627 // If there is no existing UoW, then we should start one and 628 // terminate it once processing is completed for the exchange. 629 UnitOfWork uow = createUnitOfWork(exchange); 630 exchange.setUnitOfWork(uow); 631 uow.start(); 632 return uow; 633 } 634 635 return null; 636 } 637 638 @Override 639 public void after(Exchange exchange, UnitOfWork uow) throws Exception { 640 // execute done on uow if we created it, and the consumer is not doing it 641 if (uow != null) { 642 UnitOfWorkHelper.doneUow(uow, exchange); 643 } 644 } 645 646 protected UnitOfWork createUnitOfWork(Exchange exchange) { 647 return exchange.getContext().getUnitOfWorkFactory().createUnitOfWork(exchange); 648 } 649 650 } 651 652 /** 653 * Advice when an EIP uses the <tt>shareUnitOfWork</tt> functionality. 654 */ 655 public static class ChildUnitOfWorkProcessorAdvice extends UnitOfWorkProcessorAdvice { 656 657 private final UnitOfWork parent; 658 659 public ChildUnitOfWorkProcessorAdvice(String routeId, UnitOfWork parent) { 660 super(routeId); 661 this.parent = parent; 662 } 663 664 @Override 665 protected UnitOfWork createUnitOfWork(Exchange exchange) { 666 // let the parent create a child unit of work to be used 667 return parent.createChildUnitOfWork(exchange); 668 } 669 670 } 671 672 /** 673 * Advice when an EIP uses the <tt>shareUnitOfWork</tt> functionality. 674 */ 675 public static class SubUnitOfWorkProcessorAdvice implements CamelInternalProcessorAdvice<UnitOfWork> { 676 677 @Override 678 public UnitOfWork before(Exchange exchange) throws Exception { 679 // begin savepoint 680 exchange.getUnitOfWork().beginSubUnitOfWork(exchange); 681 return exchange.getUnitOfWork(); 682 } 683 684 @Override 685 public void after(Exchange exchange, UnitOfWork unitOfWork) throws Exception { 686 // end sub unit of work 687 unitOfWork.endSubUnitOfWork(exchange); 688 } 689 } 690 691 /** 692 * Advice when Message History has been enabled. 693 */ 694 @SuppressWarnings("unchecked") 695 public static class MessageHistoryAdvice implements CamelInternalProcessorAdvice<MessageHistory> { 696 697 private final ProcessorDefinition<?> definition; 698 private final String routeId; 699 700 public MessageHistoryAdvice(ProcessorDefinition<?> definition) { 701 this.definition = definition; 702 this.routeId = ProcessorDefinitionHelper.getRouteId(definition); 703 } 704 705 @Override 706 public MessageHistory before(Exchange exchange) throws Exception { 707 List<MessageHistory> list = exchange.getProperty(Exchange.MESSAGE_HISTORY, List.class); 708 if (list == null) { 709 list = new ArrayList<MessageHistory>(); 710 exchange.setProperty(Exchange.MESSAGE_HISTORY, list); 711 } 712 MessageHistory history = new DefaultMessageHistory(routeId, definition, new Date()); 713 list.add(history); 714 return history; 715 } 716 717 @Override 718 public void after(Exchange exchange, MessageHistory history) throws Exception { 719 if (history != null) { 720 history.nodeProcessingDone(); 721 } 722 } 723 } 724 725 /** 726 * Advice for {@link org.apache.camel.spi.StreamCachingStrategy} 727 */ 728 public static class StreamCachingAdvice implements CamelInternalProcessorAdvice<StreamCache> { 729 730 private final StreamCachingStrategy strategy; 731 732 public StreamCachingAdvice(StreamCachingStrategy strategy) { 733 this.strategy = strategy; 734 } 735 736 @Override 737 public StreamCache before(Exchange exchange) throws Exception { 738 // check if body is already cached 739 Object body = exchange.getIn().getBody(); 740 if (body == null) { 741 return null; 742 } else if (body instanceof StreamCache) { 743 StreamCache sc = (StreamCache) body; 744 // reset so the cache is ready to be used before processing 745 sc.reset(); 746 return sc; 747 } 748 // cache the body and if we could do that replace it as the new body 749 StreamCache sc = strategy.cache(exchange); 750 if (sc != null) { 751 exchange.getIn().setBody(sc); 752 } 753 return sc; 754 } 755 756 @Override 757 public void after(Exchange exchange, StreamCache sc) throws Exception { 758 Object body = null; 759 if (exchange.hasOut()) { 760 body = exchange.getOut().getBody(); 761 } else { 762 body = exchange.getIn().getBody(); 763 } 764 if (body != null && body instanceof StreamCache) { 765 // reset so the cache is ready to be reused after processing 766 ((StreamCache) body).reset(); 767 } 768 } 769 } 770 771 /** 772 * Advice for delaying 773 */ 774 public static class DelayerAdvice implements CamelInternalProcessorAdvice { 775 776 private final long delay; 777 778 public DelayerAdvice(long delay) { 779 this.delay = delay; 780 } 781 782 @Override 783 public Object before(Exchange exchange) throws Exception { 784 try { 785 LOG.trace("Sleeping for: {} millis", delay); 786 Thread.sleep(delay); 787 } catch (InterruptedException e) { 788 LOG.debug("Sleep interrupted"); 789 Thread.currentThread().interrupt(); 790 throw e; 791 } 792 return null; 793 } 794 795 @Override 796 public void after(Exchange exchange, Object data) throws Exception { 797 // noop 798 } 799 } 800 801}