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