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