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