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.model;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import javax.xml.bind.annotation.XmlAccessType;
025import javax.xml.bind.annotation.XmlAccessorType;
026import javax.xml.bind.annotation.XmlAttribute;
027import javax.xml.bind.annotation.XmlElement;
028import javax.xml.bind.annotation.XmlElementRef;
029import javax.xml.bind.annotation.XmlRootElement;
030import javax.xml.bind.annotation.XmlTransient;
031
032import org.apache.camel.CamelContext;
033import org.apache.camel.Expression;
034import org.apache.camel.LoggingLevel;
035import org.apache.camel.Predicate;
036import org.apache.camel.Processor;
037import org.apache.camel.Route;
038import org.apache.camel.builder.ErrorHandlerBuilder;
039import org.apache.camel.builder.ExpressionBuilder;
040import org.apache.camel.processor.CatchProcessor;
041import org.apache.camel.processor.FatalFallbackErrorHandler;
042import org.apache.camel.processor.RedeliveryPolicy;
043import org.apache.camel.spi.ClassResolver;
044import org.apache.camel.spi.Metadata;
045import org.apache.camel.spi.RouteContext;
046import org.apache.camel.util.CamelContextHelper;
047import org.apache.camel.util.ExpressionToPredicateAdapter;
048import org.apache.camel.util.ObjectHelper;
049
050/**
051 * Route to be executed when an exception is thrown
052 *
053 * @version 
054 */
055@Metadata(label = "error")
056@XmlRootElement(name = "onException")
057@XmlAccessorType(XmlAccessType.FIELD)
058public class OnExceptionDefinition extends ProcessorDefinition<OnExceptionDefinition> {
059    @XmlElement(name = "exception", required = true)
060    private List<String> exceptions = new ArrayList<String>();
061    @XmlElement(name = "onWhen")
062    private WhenDefinition onWhen;
063    @XmlElement(name = "retryWhile")
064    private ExpressionSubElementDefinition retryWhile;
065    @XmlElement(name = "redeliveryPolicy")
066    private RedeliveryPolicyDefinition redeliveryPolicyType;
067    @XmlAttribute(name = "redeliveryPolicyRef")
068    private String redeliveryPolicyRef;
069    @XmlElement(name = "handled")
070    private ExpressionSubElementDefinition handled;
071    @XmlElement(name = "continued")
072    private ExpressionSubElementDefinition continued;
073    @XmlAttribute(name = "onRedeliveryRef")
074    private String onRedeliveryRef;
075    @XmlAttribute(name = "onExceptionOccurredRef")
076    private String onExceptionbOccurredRef;
077    @XmlAttribute(name = "useOriginalMessage")
078    private Boolean useOriginalMessagePolicy;
079    @XmlElementRef
080    private List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>();
081    @XmlTransient
082    private List<Class<? extends Throwable>> exceptionClasses;
083    @XmlTransient
084    private Predicate handledPolicy;
085    @XmlTransient
086    private Predicate continuedPolicy;
087    @XmlTransient
088    private Predicate retryWhilePolicy;
089    @XmlTransient
090    private Processor onRedelivery;
091    @XmlTransient
092    private Processor onExceptionOccurred;
093    @XmlTransient
094    private Boolean routeScoped;
095    // TODO: in Camel 3.0 the OnExceptionDefinition should not contain state and ErrorHandler processors
096    @XmlTransient
097    private final Map<String, Processor> errorHandlers = new HashMap<String, Processor>();
098    @XmlTransient
099    private RedeliveryPolicy redeliveryPolicy;
100
101    public OnExceptionDefinition() {
102    }
103
104    public OnExceptionDefinition(List<Class<? extends Throwable>> exceptionClasses) {
105        this.exceptionClasses = exceptionClasses;
106    }
107
108    public OnExceptionDefinition(Class<? extends Throwable> exceptionType) {
109        exceptionClasses = new ArrayList<Class<? extends Throwable>>();
110        exceptionClasses.add(exceptionType);
111    }
112
113    public void setRouteScoped(boolean routeScoped) {
114        this.routeScoped = routeScoped;
115    }
116
117    public boolean isRouteScoped() {
118        // is context scoped by default
119        return routeScoped != null ? routeScoped : false;
120    }
121
122    @Override
123    public String toString() {
124        return "OnException[" + description() + " -> " + getOutputs() + "]";
125    }
126    
127    protected String description() {
128        return getExceptionClasses() + (onWhen != null ? " " + onWhen : "");
129    }
130
131    @Override
132    public String getLabel() {
133        return "onException[" + description() + "]";
134    }
135    
136    @Override
137    public boolean isAbstract() {
138        return true;
139    }
140
141    @Override
142    public boolean isTopLevelOnly() {
143        return true;
144    }
145
146    /**
147     * Allows an exception handler to create a new redelivery policy for this exception type
148     *
149     * @param context      the camel context
150     * @param parentPolicy the current redelivery policy, is newer <tt>null</tt>
151     * @return a newly created redelivery policy, or return the original policy if no customization is required
152     *         for this exception handler.
153     */
154    public RedeliveryPolicy createRedeliveryPolicy(CamelContext context, RedeliveryPolicy parentPolicy) {
155        if (redeliveryPolicy != null) {
156            return redeliveryPolicy;
157        } else if (redeliveryPolicyRef != null) {
158            return CamelContextHelper.mandatoryLookup(context, redeliveryPolicyRef, RedeliveryPolicy.class);
159        } else if (redeliveryPolicyType != null) {
160            return redeliveryPolicyType.createRedeliveryPolicy(context, parentPolicy);
161        } else if (!outputs.isEmpty() && parentPolicy.getMaximumRedeliveries() != 0) {
162            // if we have outputs, then do not inherit parent maximumRedeliveries
163            // as you would have to explicit configure maximumRedeliveries on this onException to use it
164            // this is the behavior Camel has always had
165            RedeliveryPolicy answer = parentPolicy.copy();
166            answer.setMaximumRedeliveries(0);
167            return answer;
168        } else {
169            return parentPolicy;
170        }
171    }
172
173    public void addRoutes(RouteContext routeContext, Collection<Route> routes) throws Exception {
174        // assign whether this was a route scoped onException or not
175        // we need to know this later when setting the parent, as only route scoped should have parent
176        // Note: this logic can possible be removed when the Camel routing engine decides at runtime
177        // to apply onException in a more dynamic fashion than current code base
178        // and therefore is in a better position to decide among context/route scoped OnException at runtime
179        if (routeScoped == null) {
180            routeScoped = super.getParent() != null;
181        }
182
183        setHandledFromExpressionType(routeContext);
184        setContinuedFromExpressionType(routeContext);
185        setRetryWhileFromExpressionType(routeContext);
186        setOnRedeliveryFromRedeliveryRef(routeContext);
187        setOnExceptionOccurredFromOnExceptionOccurredRef(routeContext);
188
189        // load exception classes
190        if (exceptions != null && !exceptions.isEmpty()) {
191            exceptionClasses = createExceptionClasses(routeContext.getCamelContext().getClassResolver());
192        }
193
194        // must validate configuration before creating processor
195        validateConfiguration();
196
197        // lets attach this on exception to the route error handler
198        Processor child = createOutputsProcessor(routeContext);
199        if (child != null) {
200            // wrap in our special safe fallback error handler if OnException have child output
201            Processor errorHandler = new FatalFallbackErrorHandler(child);
202            String id = routeContext.getRoute().getId();
203            errorHandlers.put(id, errorHandler);
204        }
205        // lookup the error handler builder
206        ErrorHandlerBuilder builder = (ErrorHandlerBuilder)routeContext.getRoute().getErrorHandlerBuilder();
207        // and add this as error handlers
208        builder.addErrorHandlers(routeContext, this);
209    }
210
211    @Override
212    public CatchProcessor createProcessor(RouteContext routeContext) throws Exception {
213        // load exception classes
214        if (exceptions != null && !exceptions.isEmpty()) {
215            exceptionClasses = createExceptionClasses(routeContext.getCamelContext().getClassResolver());
216        }
217
218        // must validate configuration before creating processor
219        validateConfiguration();
220
221        Processor childProcessor = this.createChildProcessor(routeContext, false);
222
223        Predicate when = null;
224        if (onWhen != null) {
225            when = onWhen.getExpression().createPredicate(routeContext);
226        }
227
228        Predicate handle = null;
229        if (handled != null) {
230            handle = handled.createPredicate(routeContext);
231        }
232
233        return new CatchProcessor(getExceptionClasses(), childProcessor, when, handle);
234    }
235
236    protected void validateConfiguration() {
237        if (isInheritErrorHandler() != null && isInheritErrorHandler()) {
238            throw new IllegalArgumentException(this + " cannot have the inheritErrorHandler option set to true");
239        }
240
241        List<Class<? extends Throwable>> exceptions = getExceptionClasses();
242        if (exceptions == null || exceptions.isEmpty()) {
243            throw new IllegalArgumentException("At least one exception must be configured on " + this);
244        }
245
246        // only one of handled or continued is allowed
247        if (getHandledPolicy() != null && getContinuedPolicy() != null) {
248            throw new IllegalArgumentException("Only one of handled or continued is allowed to be configured on: " + this);
249        }
250
251        // validate that at least some option is set as you cannot just have onException(Exception.class);
252        if (outputs == null || getOutputs().isEmpty()) {
253            // no outputs so there should be some sort of configuration
254            if (handledPolicy == null && continuedPolicy == null && retryWhilePolicy == null
255                    && redeliveryPolicyType == null && useOriginalMessagePolicy == null
256                    && onRedelivery == null && onExceptionOccurred == null) {
257                throw new IllegalArgumentException(this + " is not configured.");
258            }
259        }
260    }
261
262    // Fluent API
263    //-------------------------------------------------------------------------
264
265    @Override
266    public OnExceptionDefinition onException(Class<? extends Throwable> exceptionType) {
267        getExceptionClasses().add(exceptionType);
268        return this;
269    }
270
271    /**
272     * Sets whether the exchange should be marked as handled or not.
273     *
274     * @param handled handled or not
275     * @return the builder
276     */
277    public OnExceptionDefinition handled(boolean handled) {
278        Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
279        return handled(expression);
280    }
281
282    /**
283     * Sets whether the exchange should be marked as handled or not.
284     *
285     * @param handled predicate that determines true or false
286     * @return the builder
287     */
288    public OnExceptionDefinition handled(Predicate handled) {
289        setHandledPolicy(handled);
290        return this;
291    }
292
293    /**
294     * Sets whether the exchange should be marked as handled or not.
295     *
296     * @param handled expression that determines true or false
297     * @return the builder
298     */
299    public OnExceptionDefinition handled(Expression handled) {
300        setHandledPolicy(ExpressionToPredicateAdapter.toPredicate(handled));
301        return this;
302    }
303
304    /**
305     * Sets whether the exchange should handle and continue routing from the point of failure.
306     * <p/>
307     * If this option is enabled then its considered handled as well.
308     *
309     * @param continued continued or not
310     * @return the builder
311     */
312    public OnExceptionDefinition continued(boolean continued) {
313        Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(continued));
314        return continued(expression);
315    }
316
317    /**
318     * Sets whether the exchange should be marked as handled or not.
319     * <p/>
320     * If this option is enabled then its considered handled as well.
321     *
322     * @param continued predicate that determines true or false
323     * @return the builder
324     */
325    public OnExceptionDefinition continued(Predicate continued) {
326        setContinuedPolicy(continued);
327        return this;
328    }
329
330    /**
331     * Sets whether the exchange should be marked as handled or not.
332     * <p/>
333     * If this option is enabled then its considered handled as well.
334     *
335     * @param continued expression that determines true or false
336     * @return the builder
337     */
338    public OnExceptionDefinition continued(Expression continued) {
339        setContinuedPolicy(ExpressionToPredicateAdapter.toPredicate(continued));
340        return this;
341    }
342
343    /**
344     * Sets an additional predicate that should be true before the onException is triggered.
345     * <p/>
346     * To be used for fine grained controlling whether a thrown exception should be intercepted
347     * by this exception type or not.
348     *
349     * @param predicate predicate that determines true or false
350     * @return the builder
351     */
352    public OnExceptionDefinition onWhen(Predicate predicate) {
353        setOnWhen(new WhenDefinition(predicate));
354        return this;
355    }
356
357    /**
358     * Sets the retry while predicate.
359     * <p/>
360     * Will continue retrying until predicate returns <tt>false</tt>.
361     *
362     * @param retryWhile predicate that determines when to stop retrying
363     * @return the builder
364     */
365    public OnExceptionDefinition retryWhile(Predicate retryWhile) {
366        setRetryWhilePolicy(retryWhile);
367        return this;
368    }
369
370    /**
371     * Sets the initial redelivery delay
372     *
373     * @param delay the initial redelivery delay
374     * @return the builder
375     * @deprecated will be removed in the near future. Instead use {@link #redeliveryDelay(String)}
376     */
377    @Deprecated
378    public OnExceptionDefinition redeliverDelay(long delay) {
379        getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
380        return this;
381    }
382
383    /**
384     * Sets the back off multiplier
385     *
386     * @param backOffMultiplier the back off multiplier
387     * @return the builder
388     */
389    public OnExceptionDefinition backOffMultiplier(double backOffMultiplier) {
390        getOrCreateRedeliveryPolicy().useExponentialBackOff();
391        getOrCreateRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
392        return this;
393    }
394
395    /**
396     * Sets the back off multiplier (supports property placeholders)
397     *
398     * @param backOffMultiplier the back off multiplier
399     * @return the builder
400     */
401    public OnExceptionDefinition backOffMultiplier(String backOffMultiplier) {
402        getOrCreateRedeliveryPolicy().useExponentialBackOff();
403        getOrCreateRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
404        return this;
405    }
406
407    /**
408     * Sets the collision avoidance factor
409     *
410     * @param collisionAvoidanceFactor the factor
411     * @return the builder
412     */
413    public OnExceptionDefinition collisionAvoidanceFactor(double collisionAvoidanceFactor) {
414        getOrCreateRedeliveryPolicy().useCollisionAvoidance();
415        getOrCreateRedeliveryPolicy().collisionAvoidanceFactor(collisionAvoidanceFactor);
416        return this;
417    }
418
419    /**
420     * Sets the collision avoidance factor (supports property placeholders)
421     *
422     * @param collisionAvoidanceFactor the factor
423     * @return the builder
424     */
425    public OnExceptionDefinition collisionAvoidanceFactor(String collisionAvoidanceFactor) {
426        getOrCreateRedeliveryPolicy().useCollisionAvoidance();
427        getOrCreateRedeliveryPolicy().collisionAvoidanceFactor(collisionAvoidanceFactor);
428        return this;
429    }
430
431    /**
432     * Sets the collision avoidance percentage
433     *
434     * @param collisionAvoidancePercent the percentage
435     * @return the builder
436     */
437    public OnExceptionDefinition collisionAvoidancePercent(double collisionAvoidancePercent) {
438        getOrCreateRedeliveryPolicy().useCollisionAvoidance();
439        getOrCreateRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
440        return this;
441    }
442
443    /**
444     * Sets the initial redelivery delay
445     *
446     * @param delay delay in millis
447     * @return the builder
448     */
449    public OnExceptionDefinition redeliveryDelay(long delay) {
450        getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
451        return this;
452    }
453
454    /**
455     * Sets the initial redelivery delay (supports property placeholders)
456     *
457     * @param delay delay in millis
458     * @return the builder
459     */
460    public OnExceptionDefinition redeliveryDelay(String delay) {
461        getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
462        return this;
463    }
464
465    /**
466     * Allow synchronous delayed redelivery.
467     *
468     * @see org.apache.camel.processor.RedeliveryPolicy#setAsyncDelayedRedelivery(boolean)
469     * @return the builder
470     */
471    public OnExceptionDefinition asyncDelayedRedelivery() {
472        getOrCreateRedeliveryPolicy().asyncDelayedRedelivery();
473        return this;
474    }
475
476    /**
477     * Sets the logging level to use when retries has exhausted
478     *
479     * @param retriesExhaustedLogLevel the logging level
480     * @return the builder
481     */
482    public OnExceptionDefinition retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
483        getOrCreateRedeliveryPolicy().retriesExhaustedLogLevel(retriesExhaustedLogLevel);
484        return this;
485    }
486
487    /**
488     * Sets the logging level to use for logging retry attempts
489     *
490     * @param retryAttemptedLogLevel the logging level
491     * @return the builder
492     */
493    public OnExceptionDefinition retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
494        getOrCreateRedeliveryPolicy().retryAttemptedLogLevel(retryAttemptedLogLevel);
495        return this;
496    }
497
498    /**
499     * Sets whether to log stacktrace for failed messages.
500     */
501    public OnExceptionDefinition logStackTrace(boolean logStackTrace) {
502        getOrCreateRedeliveryPolicy().logStackTrace(logStackTrace);
503        return this;
504    }
505
506    /**
507     * Sets whether to log stacktrace for failed messages (supports property placeholders)
508     */
509    public OnExceptionDefinition logStackTrace(String logStackTrace) {
510        getOrCreateRedeliveryPolicy().logStackTrace(logStackTrace);
511        return this;
512    }
513
514    /**
515     * Sets whether to log stacktrace for failed redelivery attempts
516     */
517    public OnExceptionDefinition logRetryStackTrace(boolean logRetryStackTrace) {
518        getOrCreateRedeliveryPolicy().logRetryStackTrace(logRetryStackTrace);
519        return this;
520    }
521
522    /**
523     * Sets whether to log stacktrace for failed redelivery attempts (supports property placeholders)
524     */
525    public OnExceptionDefinition logRetryStackTrace(String logRetryStackTrace) {
526        getOrCreateRedeliveryPolicy().logRetryStackTrace(logRetryStackTrace);
527        return this;
528    }
529
530    /**
531     * Sets whether to log errors even if its handled
532     */
533    public OnExceptionDefinition logHandled(boolean logHandled) {
534        getOrCreateRedeliveryPolicy().logHandled(logHandled);
535        return this;
536    }
537
538    /**
539     * Sets whether to log errors even if its handled (supports property placeholders)
540     */
541    public OnExceptionDefinition logHandled(String logHandled) {
542        getOrCreateRedeliveryPolicy().logHandled(logHandled);
543        return this;
544    }
545
546    /**
547     * Sets whether new exceptions should be logged or not (supports property placeholders).
548     * Can be used to include or reduce verbose.
549     * <p/>
550     * A new exception is an exception that was thrown while handling a previous exception.
551     */
552    public OnExceptionDefinition logNewException(boolean logNewException) {
553        getOrCreateRedeliveryPolicy().logNewException(logNewException);
554        return this;
555    }
556
557    /**
558     * Sets whether new exceptions should be logged or not (supports property placeholders).
559     * Can be used to include or reduce verbose.
560     * <p/>
561     * A new exception is an exception that was thrown while handling a previous exception.
562     */
563    public OnExceptionDefinition logNewException(String logNewException) {
564        getOrCreateRedeliveryPolicy().logNewException(logNewException);
565        return this;
566    }
567
568    /**
569     * Sets whether to log errors even if its continued
570     */
571    public OnExceptionDefinition logContinued(boolean logContinued) {
572        getOrCreateRedeliveryPolicy().logContinued(logContinued);
573        return this;
574    }
575
576    /**
577     * Sets whether to log errors even if its continued (supports property placeholders)
578     */
579    public OnExceptionDefinition logContinued(String logContinued) {
580        getOrCreateRedeliveryPolicy().logContinued(logContinued);
581        return this;
582    }
583
584    /**
585     * Sets whether to log retry attempts
586     */
587    public OnExceptionDefinition logRetryAttempted(boolean logRetryAttempted) {
588        getOrCreateRedeliveryPolicy().logRetryAttempted(logRetryAttempted);
589        return this;
590    }
591
592    /**
593     * Sets whether to log retry attempts (supports property placeholders)
594     */
595    public OnExceptionDefinition logRetryAttempted(String logRetryAttempted) {
596        getOrCreateRedeliveryPolicy().logRetryAttempted(logRetryAttempted);
597        return this;
598    }
599
600    /**
601     * Sets whether to log exhausted exceptions
602     */
603    public OnExceptionDefinition logExhausted(boolean logExhausted) {
604        getOrCreateRedeliveryPolicy().logExhausted(logExhausted);
605        return this;
606    }
607
608    /**
609     * Sets whether to log exhausted exceptions (supports property placeholders)
610     */
611    public OnExceptionDefinition logExhausted(String logExhausted) {
612        getOrCreateRedeliveryPolicy().logExhausted(logExhausted);
613        return this;
614    }
615
616    /**
617     * Sets whether to log exhausted exceptions with message history
618     */
619    public OnExceptionDefinition logExhaustedMessageHistory(boolean logExhaustedMessageHistory) {
620        getOrCreateRedeliveryPolicy().logExhaustedMessageHistory(logExhaustedMessageHistory);
621        return this;
622    }
623
624    /**
625     * Sets whether to log exhausted exceptions with message history
626     */
627    public OnExceptionDefinition logExhaustedMessageHistory(String logExhaustedMessageHistory) {
628        getOrCreateRedeliveryPolicy().logExhaustedMessageHistory(logExhaustedMessageHistory);
629        return this;
630    }
631
632    /**
633     * Sets whether to log exhausted message body with message history.
634     * Requires <tt>logExhaustedMessageHistory</tt> to be enabled.
635     */
636    public OnExceptionDefinition logExhaustedMessageBody(boolean logExhaustedMessageBody) {
637        getOrCreateRedeliveryPolicy().logExhaustedMessageBody(logExhaustedMessageBody);
638        return this;
639    }
640
641    /**
642     * Sets whether to log exhausted message body with message history.
643     * Requires <tt>logExhaustedMessageHistory</tt> to be enabled.
644     */
645    public OnExceptionDefinition logExhaustedMessageBody(String logExhaustedMessageBody) {
646        getOrCreateRedeliveryPolicy().logExhaustedMessageBody(logExhaustedMessageBody);
647        return this;
648    }
649
650    /**
651     * Sets the maximum redeliveries
652     * <ul>
653     * <li>5 = default value</li>
654     * <li>0 = no redeliveries</li>
655     * <li>-1 = redeliver forever</li>
656     * </ul>
657     *
658     * @param maximumRedeliveries the value
659     * @return the builder
660     */
661    public OnExceptionDefinition maximumRedeliveries(int maximumRedeliveries) {
662        getOrCreateRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
663        return this;
664    }
665
666    /**
667     * Sets the maximum redeliveries (supports property placeholders)
668     * <ul>
669     * <li>5 = default value</li>
670     * <li>0 = no redeliveries</li>
671     * <li>-1 = redeliver forever</li>
672     * </ul>
673     *
674     * @param maximumRedeliveries the value
675     * @return the builder
676     */
677    public OnExceptionDefinition maximumRedeliveries(String maximumRedeliveries) {
678        getOrCreateRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
679        return this;
680    }
681
682    /**
683     * Turn on collision avoidance.
684     *
685     * @return the builder
686     */
687    public OnExceptionDefinition useCollisionAvoidance() {
688        getOrCreateRedeliveryPolicy().useCollisionAvoidance();
689        return this;
690    }
691
692    /**
693     * Turn on exponential backk off
694     *
695     * @return the builder
696     */
697    public OnExceptionDefinition useExponentialBackOff() {
698        getOrCreateRedeliveryPolicy().useExponentialBackOff();
699        return this;
700    }
701
702    /**
703     * Sets the maximum delay between redelivery
704     *
705     * @param maximumRedeliveryDelay the delay in millis
706     * @return the builder
707     */
708    public OnExceptionDefinition maximumRedeliveryDelay(long maximumRedeliveryDelay) {
709        getOrCreateRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
710        return this;
711    }
712
713    /**
714     * Sets the maximum delay between redelivery (supports property placeholders)
715     *
716     * @param maximumRedeliveryDelay the delay in millis
717     * @return the builder
718     */
719    public OnExceptionDefinition maximumRedeliveryDelay(String maximumRedeliveryDelay) {
720        getOrCreateRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
721        return this;
722    }
723
724    /**
725     * Set the {@link RedeliveryPolicy} to be used.
726     *
727     * @param redeliveryPolicy the redelivery policy
728     * @return the builder
729     */
730    public OnExceptionDefinition redeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
731        this.redeliveryPolicy = redeliveryPolicy;
732        return this;
733    }
734
735    /**
736     * Sets a reference to a {@link RedeliveryPolicy} to lookup in the {@link org.apache.camel.spi.Registry} to be used.
737     *
738     * @param redeliveryPolicyRef reference to use for lookup
739     * @return the builder
740     */
741    public OnExceptionDefinition redeliveryPolicyRef(String redeliveryPolicyRef) {
742        setRedeliveryPolicyRef(redeliveryPolicyRef);
743        return this;
744    }
745
746    /**
747     * Sets the delay pattern with delay intervals.
748     *
749     * @param delayPattern the delay pattern
750     * @return the builder
751     */
752    public OnExceptionDefinition delayPattern(String delayPattern) {
753        getOrCreateRedeliveryPolicy().setDelayPattern(delayPattern);
754        return this;
755    }
756
757    /**
758     * @deprecated this method will be removed in Camel 3.0, please use {@link #useOriginalMessage()}
759     * @see #useOriginalMessage()
760     */
761    @Deprecated
762    public OnExceptionDefinition useOriginalBody() {
763        setUseOriginalMessagePolicy(Boolean.TRUE);
764        return this;
765    }
766
767    /**
768     * Will use the original input message when an {@link org.apache.camel.Exchange} is moved to the dead letter queue.
769     * <p/>
770     * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange} is doomed for failure.
771     * <br/>
772     * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN body we use the original IN body instead. This allows
773     * you to store the original input in the dead letter queue instead of the inprogress snapshot of the IN body.
774     * For instance if you route transform the IN body during routing and then failed. With the original exchange
775     * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange} again as the IN body
776     * is the same as when Camel received it. So you should be able to send the {@link org.apache.camel.Exchange} to the same input.
777     * <p/>
778     * By default this feature is off.
779     *
780     * @return the builder
781     */
782    public OnExceptionDefinition useOriginalMessage() {
783        setUseOriginalMessagePolicy(Boolean.TRUE);
784        return this;
785    }
786
787    /**
788     * Sets a processor that should be processed <b>before</b> a redelivery attempt.
789     * <p/>
790     * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
791     */
792    public OnExceptionDefinition onRedelivery(Processor processor) {
793        setOnRedelivery(processor);
794        return this;
795    }
796
797    /**
798     * Sets a reference to a processor that should be processed <b>before</b> a redelivery attempt.
799     * <p/>
800     * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
801     *
802     * @param ref  reference to the processor
803     */
804    public OnExceptionDefinition onRedeliveryRef(String ref) {
805        setOnRedeliveryRef(ref);
806        return this;
807    }
808
809    /**
810     * Sets a processor that should be processed <b>just after</b> an exception occurred.
811     * Can be used to perform custom logging about the occurred exception at the exact time it happened.
812     * <p/>
813     * Important: Any exception thrown from this processor will be ignored.
814     */
815    public OnExceptionDefinition onExceptionOccurred(Processor processor) {
816        setOnExceptionOccurred(processor);
817        return this;
818    }
819
820    /**
821     * Sets a reference to a processor that should be processed <b>just after</b> an exception occurred.
822     * Can be used to perform custom logging about the occurred exception at the exact time it happened.
823     * <p/>
824     * Important: Any exception thrown from this processor will be ignored.
825     *
826     * @param ref  reference to the processor
827     */
828    public OnExceptionDefinition onExceptionOccurredRef(String ref) {
829        setOnExceptionbOccurredRef(ref);
830        return this;
831    }
832
833    // Properties
834    //-------------------------------------------------------------------------
835    @Override
836    public List<ProcessorDefinition<?>> getOutputs() {
837        return outputs;
838    }
839
840    public void setOutputs(List<ProcessorDefinition<?>> outputs) {
841        this.outputs = outputs;
842    }
843
844    public boolean isOutputSupported() {
845        return true;
846    }
847
848    public List<Class<? extends Throwable>> getExceptionClasses() {
849        return exceptionClasses;
850    }
851
852    public void setExceptionClasses(List<Class<? extends Throwable>> exceptionClasses) {
853        this.exceptionClasses = exceptionClasses;
854    }
855
856    public List<String> getExceptions() {
857        return exceptions;
858    }
859
860    /**
861     * A set of exceptions to react upon.
862     */
863    public void setExceptions(List<String> exceptions) {
864        this.exceptions = exceptions;
865    }
866
867    public Processor getErrorHandler(String routeId) {
868        return errorHandlers.get(routeId);
869    }
870    
871    public Collection<Processor> getErrorHandlers() {
872        return errorHandlers.values();
873    }
874
875    public RedeliveryPolicyDefinition getRedeliveryPolicy() {
876        return redeliveryPolicyType;
877    }
878
879    public void setRedeliveryPolicy(RedeliveryPolicyDefinition redeliveryPolicy) {
880        this.redeliveryPolicyType = redeliveryPolicy;
881    }
882
883    public RedeliveryPolicyDefinition getRedeliveryPolicyType() {
884        return redeliveryPolicyType;
885    }
886
887    public void setRedeliveryPolicyType(RedeliveryPolicyDefinition redeliveryPolicyType) {
888        this.redeliveryPolicyType = redeliveryPolicyType;
889    }
890
891    public String getRedeliveryPolicyRef() {
892        return redeliveryPolicyRef;
893    }
894
895    public void setRedeliveryPolicyRef(String redeliveryPolicyRef) {
896        this.redeliveryPolicyRef = redeliveryPolicyRef;
897    }
898
899    public Predicate getHandledPolicy() {
900        return handledPolicy;
901    }
902
903    public void setHandled(ExpressionSubElementDefinition handled) {
904        this.handled = handled;
905    }
906
907    public ExpressionSubElementDefinition getContinued() {
908        return continued;
909    }
910
911    public void setContinued(ExpressionSubElementDefinition continued) {
912        this.continued = continued;
913    }
914
915    public ExpressionSubElementDefinition getHandled() {
916        return handled;
917    }
918
919    public void setHandledPolicy(Predicate handledPolicy) {
920        this.handledPolicy = handledPolicy;
921    }
922
923    public Predicate getContinuedPolicy() {
924        return continuedPolicy;
925    }
926
927    public void setContinuedPolicy(Predicate continuedPolicy) {
928        this.continuedPolicy = continuedPolicy;
929    }
930
931    public WhenDefinition getOnWhen() {
932        return onWhen;
933    }
934
935    public void setOnWhen(WhenDefinition onWhen) {
936        this.onWhen = onWhen;
937    }
938
939    public ExpressionSubElementDefinition getRetryWhile() {
940        return retryWhile;
941    }
942
943    public void setRetryWhile(ExpressionSubElementDefinition retryWhile) {
944        this.retryWhile = retryWhile;
945    }
946
947    public Predicate getRetryWhilePolicy() {
948        return retryWhilePolicy;
949    }
950
951    public void setRetryWhilePolicy(Predicate retryWhilePolicy) {
952        this.retryWhilePolicy = retryWhilePolicy;
953    }
954
955    public Processor getOnRedelivery() {
956        return onRedelivery;
957    }
958
959    public void setOnRedelivery(Processor onRedelivery) {
960        this.onRedelivery = onRedelivery;
961    }
962
963    public String getOnRedeliveryRef() {
964        return onRedeliveryRef;
965    }
966
967    public void setOnRedeliveryRef(String onRedeliveryRef) {
968        this.onRedeliveryRef = onRedeliveryRef;
969    }
970
971    public Processor getOnExceptionOccurred() {
972        return onExceptionOccurred;
973    }
974
975    public void setOnExceptionOccurred(Processor onExceptionOccurred) {
976        this.onExceptionOccurred = onExceptionOccurred;
977    }
978
979    public String getOnExceptionbOccurredRef() {
980        return onExceptionbOccurredRef;
981    }
982
983    public void setOnExceptionbOccurredRef(String onExceptionbOccurredRef) {
984        this.onExceptionbOccurredRef = onExceptionbOccurredRef;
985    }
986
987    public Boolean getUseOriginalMessagePolicy() {
988        return useOriginalMessagePolicy;
989    }
990
991    public void setUseOriginalMessagePolicy(Boolean useOriginalMessagePolicy) {
992        this.useOriginalMessagePolicy = useOriginalMessagePolicy;
993    }
994
995    // Implementation methods
996    //-------------------------------------------------------------------------
997
998    protected boolean isAsyncDelayedRedelivery(CamelContext context) {
999        if (getRedeliveryPolicy() != null) {
1000            return getRedeliveryPolicy().isAsyncDelayedRedelivery(context);
1001        }
1002        return false;
1003    }
1004
1005    protected RedeliveryPolicyDefinition getOrCreateRedeliveryPolicy() {
1006        if (redeliveryPolicyType == null) {
1007            redeliveryPolicyType = new RedeliveryPolicyDefinition();
1008        }
1009        return redeliveryPolicyType;
1010    }
1011
1012    protected List<Class<? extends Throwable>> createExceptionClasses(ClassResolver resolver) throws ClassNotFoundException {
1013        List<String> list = getExceptions();
1014        List<Class<? extends Throwable>> answer = new ArrayList<Class<? extends Throwable>>(list.size());
1015        for (String name : list) {
1016            Class<? extends Throwable> type = resolver.resolveMandatoryClass(name, Throwable.class);
1017            answer.add(type);
1018        }
1019        return answer;
1020    }
1021
1022    private void setHandledFromExpressionType(RouteContext routeContext) {
1023        if (getHandled() != null && handledPolicy == null && routeContext != null) {
1024            handled(getHandled().createPredicate(routeContext));
1025        }
1026    }
1027
1028    private void setContinuedFromExpressionType(RouteContext routeContext) {
1029        if (getContinued() != null && continuedPolicy == null && routeContext != null) {
1030            continued(getContinued().createPredicate(routeContext));
1031        }
1032    }
1033
1034    private void setRetryWhileFromExpressionType(RouteContext routeContext) {
1035        if (getRetryWhile() != null && retryWhilePolicy == null && routeContext != null) {
1036            retryWhile(getRetryWhile().createPredicate(routeContext));
1037        }
1038    }
1039
1040    private void setOnRedeliveryFromRedeliveryRef(RouteContext routeContext) {
1041        // lookup onRedelivery if ref is provided
1042        if (ObjectHelper.isNotEmpty(onRedeliveryRef)) {
1043            // if ref is provided then use mandatory lookup to fail if not found
1044            Processor onRedelivery = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), onRedeliveryRef, Processor.class);
1045            setOnRedelivery(onRedelivery);
1046        }
1047    }
1048
1049    private void setOnExceptionOccurredFromOnExceptionOccurredRef(RouteContext routeContext) {
1050        // lookup onRedelivery if ref is provided
1051        if (ObjectHelper.isNotEmpty(onExceptionbOccurredRef)) {
1052            // if ref is provided then use mandatory lookup to fail if not found
1053            Processor onExceptionOccurred = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), onExceptionbOccurredRef, Processor.class);
1054            setOnExceptionOccurred(onExceptionOccurred);
1055        }
1056    }
1057
1058}