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