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