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