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