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}