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