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