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.builder;
018
019import java.util.concurrent.ScheduledExecutorService;
020
021import org.apache.camel.CamelContext;
022import org.apache.camel.Endpoint;
023import org.apache.camel.Expression;
024import org.apache.camel.LoggingLevel;
025import org.apache.camel.Predicate;
026import org.apache.camel.Processor;
027import org.apache.camel.processor.DefaultErrorHandler;
028import org.apache.camel.processor.RedeliveryPolicy;
029import org.apache.camel.spi.ExecutorServiceManager;
030import org.apache.camel.spi.Language;
031import org.apache.camel.spi.RouteContext;
032import org.apache.camel.spi.ThreadPoolProfile;
033import org.apache.camel.util.CamelLogger;
034import org.apache.camel.util.ExpressionToPredicateAdapter;
035import org.slf4j.LoggerFactory;
036
037/**
038 * The default error handler builder.
039 *
040 * @version 
041 */
042public class DefaultErrorHandlerBuilder extends ErrorHandlerBuilderSupport {
043
044    protected CamelLogger logger;
045    protected RedeliveryPolicy redeliveryPolicy;
046    protected Processor onRedelivery;
047    protected Predicate retryWhile;
048    protected String retryWhileRef;
049    protected Processor failureProcessor;
050    protected Endpoint deadLetter;
051    protected String deadLetterUri;
052    protected boolean deadLetterHandleNewException = true;
053    protected boolean useOriginalMessage;
054    protected boolean asyncDelayedRedelivery;
055    protected String executorServiceRef;
056    protected ScheduledExecutorService executorService;
057    protected Processor onPrepareFailure;
058    protected Processor onExceptionOccurred;
059
060    public DefaultErrorHandlerBuilder() {
061    }
062
063    public Processor createErrorHandler(RouteContext routeContext, Processor processor) throws Exception {
064        DefaultErrorHandler answer = new DefaultErrorHandler(routeContext.getCamelContext(), processor, getLogger(), getOnRedelivery(), 
065            getRedeliveryPolicy(), getExceptionPolicyStrategy(), getRetryWhilePolicy(routeContext.getCamelContext()),
066                getExecutorService(routeContext.getCamelContext()), getOnPrepareFailure(), getOnExceptionOccurred());
067        // configure error handler before we can use it
068        configure(routeContext, answer);
069        return answer;
070    }
071
072    public boolean supportTransacted() {
073        return false;
074    }
075
076    @Override
077    public ErrorHandlerBuilder cloneBuilder() {
078        DefaultErrorHandlerBuilder answer = new DefaultErrorHandlerBuilder();
079        cloneBuilder(answer);
080        return answer;
081    }
082
083    protected void cloneBuilder(DefaultErrorHandlerBuilder other) {
084        super.cloneBuilder(other);
085
086        if (logger != null) {
087            other.setLogger(logger);
088        }
089        if (redeliveryPolicy != null) {
090            other.setRedeliveryPolicy(redeliveryPolicy.copy());
091        }
092        if (onRedelivery != null) {
093            other.setOnRedelivery(onRedelivery);
094        }
095        if (retryWhile != null) {
096            other.setRetryWhile(retryWhile);
097        }
098        if (retryWhileRef != null) {
099            other.setRetryWhileRef(retryWhileRef);
100        }
101        if (failureProcessor != null) {
102            other.setFailureProcessor(failureProcessor);
103        }
104        if (deadLetter != null) {
105            other.setDeadLetter(deadLetter);
106        }
107        if (deadLetterUri != null) {
108            other.setDeadLetterUri(deadLetterUri);
109        }
110        if (onPrepareFailure != null) {
111            other.setOnPrepareFailure(onPrepareFailure);
112        }
113        if (onExceptionOccurred != null) {
114            other.setOnExceptionOccurred(onExceptionOccurred);
115        }
116        other.setDeadLetterHandleNewException(deadLetterHandleNewException);
117        other.setUseOriginalMessage(useOriginalMessage);
118        other.setAsyncDelayedRedelivery(asyncDelayedRedelivery);
119        other.setExecutorServiceRef(executorServiceRef);
120    }
121
122    // Builder methods
123    // -------------------------------------------------------------------------
124    public DefaultErrorHandlerBuilder backOffMultiplier(double backOffMultiplier) {
125        getRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
126        return this;
127    }
128
129    public DefaultErrorHandlerBuilder collisionAvoidancePercent(double collisionAvoidancePercent) {
130        getRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
131        return this;
132    }
133
134    /**
135     * @deprecated will be removed in the near future. Use {@link #redeliveryDelay(long)} instead
136     */
137    @Deprecated
138    public DefaultErrorHandlerBuilder redeliverDelay(long delay) {
139        getRedeliveryPolicy().redeliveryDelay(delay);
140        return this;
141    }
142
143    public DefaultErrorHandlerBuilder redeliveryDelay(long delay) {
144        getRedeliveryPolicy().redeliveryDelay(delay);
145        return this;
146    }
147
148    public DefaultErrorHandlerBuilder delayPattern(String delayPattern) {
149        getRedeliveryPolicy().delayPattern(delayPattern);
150        return this;
151    }
152
153    public DefaultErrorHandlerBuilder maximumRedeliveries(int maximumRedeliveries) {
154        getRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
155        return this;
156    }
157
158    public DefaultErrorHandlerBuilder disableRedelivery() {
159        getRedeliveryPolicy().maximumRedeliveries(0);
160        return this;
161    }
162
163    public DefaultErrorHandlerBuilder maximumRedeliveryDelay(long maximumRedeliveryDelay) {
164        getRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
165        return this;
166    }
167
168    public DefaultErrorHandlerBuilder useCollisionAvoidance() {
169        getRedeliveryPolicy().useCollisionAvoidance();
170        return this;
171    }
172
173    public DefaultErrorHandlerBuilder useExponentialBackOff() {
174        getRedeliveryPolicy().useExponentialBackOff();
175        return this;
176    }
177
178    public DefaultErrorHandlerBuilder retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
179        getRedeliveryPolicy().setRetriesExhaustedLogLevel(retriesExhaustedLogLevel);
180        return this;
181    }
182
183    public DefaultErrorHandlerBuilder retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
184        getRedeliveryPolicy().setRetryAttemptedLogLevel(retryAttemptedLogLevel);
185        return this;
186    }
187
188    public DefaultErrorHandlerBuilder logStackTrace(boolean logStackTrace) {
189        getRedeliveryPolicy().setLogStackTrace(logStackTrace);
190        return this;
191    }
192
193    public DefaultErrorHandlerBuilder logRetryStackTrace(boolean logRetryStackTrace) {
194        getRedeliveryPolicy().setLogRetryStackTrace(logRetryStackTrace);
195        return this;
196    }
197
198    public DefaultErrorHandlerBuilder logHandled(boolean logHandled) {
199        getRedeliveryPolicy().setLogHandled(logHandled);
200        return this;
201    }
202
203    public DefaultErrorHandlerBuilder logNewException(boolean logNewException) {
204        getRedeliveryPolicy().setLogNewException(logNewException);
205        return this;
206    }
207
208    public DefaultErrorHandlerBuilder logExhausted(boolean logExhausted) {
209        getRedeliveryPolicy().setLogExhausted(logExhausted);
210        return this;
211    }
212
213    public DefaultErrorHandlerBuilder logExhaustedMessageHistory(boolean logExhaustedMessageHistory) {
214        getRedeliveryPolicy().setLogExhaustedMessageHistory(logExhaustedMessageHistory);
215        return this;
216    }
217    
218    public DefaultErrorHandlerBuilder logExhaustedMessageBody(boolean logExhaustedMessageBody) {
219        getRedeliveryPolicy().setLogExhaustedMessageBody(logExhaustedMessageBody);
220        return this;
221    }
222
223    public DefaultErrorHandlerBuilder exchangeFormatterRef(String exchangeFormatterRef) {
224        getRedeliveryPolicy().setExchangeFormatterRef(exchangeFormatterRef);
225        return this;
226    }
227
228    /**
229     * Will allow asynchronous delayed redeliveries.
230     *
231     * @see org.apache.camel.processor.RedeliveryPolicy#setAsyncDelayedRedelivery(boolean)
232     * @return the builder
233     */
234    public DefaultErrorHandlerBuilder asyncDelayedRedelivery() {
235        getRedeliveryPolicy().setAsyncDelayedRedelivery(true);
236        return this;
237    }
238
239    /**
240     * Controls whether to allow redelivery while stopping/shutting down a route that uses error handling.
241     *
242     * @param allowRedeliveryWhileStopping <tt>true</tt> to allow redelivery, <tt>false</tt> to reject redeliveries
243     * @return the builder
244     */
245    public DefaultErrorHandlerBuilder allowRedeliveryWhileStopping(boolean allowRedeliveryWhileStopping) {
246        getRedeliveryPolicy().setAllowRedeliveryWhileStopping(allowRedeliveryWhileStopping);
247        return this;
248    }
249
250    /**
251     * Sets a reference to a thread pool to be used for redelivery.
252     *
253     * @param ref reference to a scheduled thread pool
254     * @return the builder.
255     */
256    public DefaultErrorHandlerBuilder executorServiceRef(String ref) {
257        setExecutorServiceRef(ref);
258        return this;
259    }
260
261    /**
262     * Sets the logger used for caught exceptions
263     *
264     * @param logger the logger
265     * @return the builder
266     */
267    public DefaultErrorHandlerBuilder logger(CamelLogger logger) {
268        setLogger(logger);
269        return this;
270    }
271
272    /**
273     * Sets the logging level of exceptions caught
274     *
275     * @param level the logging level
276     * @return the builder
277     */
278    public DefaultErrorHandlerBuilder loggingLevel(LoggingLevel level) {
279        getLogger().setLevel(level);
280        return this;
281    }
282
283    /**
284     * Sets the log used for caught exceptions
285     *
286     * @param log the logger
287     * @return the builder
288     */
289    public DefaultErrorHandlerBuilder log(org.slf4j.Logger log) {
290        getLogger().setLog(log);
291        return this;
292    }
293
294    /**
295     * Sets the log used for caught exceptions
296     *
297     * @param log the log name
298     * @return the builder
299     */
300    public DefaultErrorHandlerBuilder log(String log) {
301        return log(LoggerFactory.getLogger(log));
302    }
303
304    /**
305     * Sets the log used for caught exceptions
306     *
307     * @param log the log class
308     * @return the builder
309     */
310    public DefaultErrorHandlerBuilder log(Class<?> log) {
311        return log(LoggerFactory.getLogger(log));
312    }
313
314    /**
315     * Sets a processor that should be processed <b>before</b> a redelivery attempt.
316     * <p/>
317     * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
318     *
319     * @param processor the processor
320     * @return the builder
321     */
322    public DefaultErrorHandlerBuilder onRedelivery(Processor processor) {
323        setOnRedelivery(processor);
324        return this;
325    }
326
327    /**
328     * Sets the retry while expression.
329     * <p/>
330     * Will continue retrying until expression evaluates to <tt>false</tt>.
331     *
332     * @param retryWhile expression that determines when to stop retrying
333     * @return the builder
334     */
335    public DefaultErrorHandlerBuilder retryWhile(Expression retryWhile) {
336        setRetryWhile(ExpressionToPredicateAdapter.toPredicate(retryWhile));
337        return this;
338    }
339
340    /**
341     * Will use the original input {@link org.apache.camel.Message} when an {@link org.apache.camel.Exchange}
342     * is moved to the dead letter queue.
343     * <p/>
344     * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange}
345     * is doomed for failure.
346     * <br/>
347     * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN message we use the original
348     * IN message instead. This allows you to store the original input in the dead letter queue instead of the inprogress
349     * snapshot of the IN message.
350     * For instance if you route transform the IN body during routing and then failed. With the original exchange
351     * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange}
352     * again as the IN message is the same as when Camel received it.
353     * So you should be able to send the {@link org.apache.camel.Exchange} to the same input.
354     * <p/>
355     * By default this feature is off.
356     *
357     * @return the builder
358     */
359    public DefaultErrorHandlerBuilder useOriginalMessage() {
360        setUseOriginalMessage(true);
361        return this;
362    }
363
364    /**
365     * Whether the dead letter channel should handle (and ignore) any new exception that may been thrown during sending the
366     * message to the dead letter endpoint.
367     * <p/>
368     * The default value is <tt>true</tt> which means any such kind of exception is handled and ignored. Set this to <tt>false</tt>
369     * to let the exception be propagated back on the {@link org.apache.camel.Exchange}. This can be used in situations
370     * where you use transactions, and want to use Camel's dead letter channel to deal with exceptions during routing,
371     * but if the dead letter channel itself fails because of a new exception being thrown, then by setting this to <tt>false</tt>
372     * the new exceptions is propagated back and set on the {@link org.apache.camel.Exchange}, which allows the transaction
373     * to detect the exception, and rollback.
374     *
375     * @param handleNewException <tt>true</tt> to handle (and ignore), <tt>false</tt> to catch and propagated the exception on the {@link org.apache.camel.Exchange}
376     * @return the builder
377     */
378    public DefaultErrorHandlerBuilder deadLetterHandleNewException(boolean handleNewException) {
379        setDeadLetterHandleNewException(handleNewException);
380        return this;
381    }
382
383    /**
384     * @deprecated use {@link #deadLetterHandleNewException(boolean)}} with value <tt>false</tt>
385     */
386    @Deprecated
387    public DefaultErrorHandlerBuilder checkException() {
388        setDeadLetterHandleNewException(false);
389        return this;
390    }
391
392    /**
393     * Sets a custom {@link org.apache.camel.Processor} to prepare the {@link org.apache.camel.Exchange} before
394     * handled by the failure processor / dead letter channel. This allows for example to enrich the message
395     * before sending to a dead letter queue.
396     *
397     * @param processor the processor
398     * @return the builder
399     */
400    public DefaultErrorHandlerBuilder onPrepareFailure(Processor processor) {
401        setOnPrepareFailure(processor);
402        return this;
403    }
404
405    /**
406     * Sets a custom {@link org.apache.camel.Processor} to process the {@link org.apache.camel.Exchange} just after an exception was thrown.
407     * This allows to execute the processor at the same time the exception was thrown.
408     * <p/>
409     * Important: Any exception thrown from this processor will be ignored.
410     *
411     * @param processor the processor
412     * @return the builder
413     */
414    public DefaultErrorHandlerBuilder onExceptionOccurred(Processor processor) {
415        setOnExceptionOccurred(processor);
416        return this;
417    }
418
419    // Properties
420    // -------------------------------------------------------------------------
421
422    public Processor getFailureProcessor() {
423        return failureProcessor;
424    }
425
426    public void setFailureProcessor(Processor failureProcessor) {
427        this.failureProcessor = failureProcessor;
428    }
429
430    public RedeliveryPolicy getRedeliveryPolicy() {
431        if (redeliveryPolicy == null) {
432            redeliveryPolicy = createRedeliveryPolicy();
433        }
434        return redeliveryPolicy;
435    }
436
437    /**
438     * Sets the redelivery policy
439     */
440    public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
441        this.redeliveryPolicy = redeliveryPolicy;
442    }
443
444    public CamelLogger getLogger() {
445        if (logger == null) {
446            logger = createLogger();
447        }
448        return logger;
449    }
450
451    public void setLogger(CamelLogger logger) {
452        this.logger = logger;
453    }
454
455    public Processor getOnRedelivery() {
456        return onRedelivery;
457    }
458
459    public void setOnRedelivery(Processor onRedelivery) {
460        this.onRedelivery = onRedelivery;
461    }
462
463    public Predicate getRetryWhilePolicy(CamelContext context) {
464        Predicate answer = getRetryWhile();
465
466        if (getRetryWhileRef() != null) {
467            // its a bean expression
468            Language bean = context.resolveLanguage("bean");
469            answer = bean.createPredicate(getRetryWhileRef());
470        }
471
472        return answer;
473    }
474
475    public Predicate getRetryWhile() {
476        return retryWhile;
477    }
478
479    public void setRetryWhile(Predicate retryWhile) {
480        this.retryWhile = retryWhile;
481    }
482
483    public String getRetryWhileRef() {
484        return retryWhileRef;
485    }
486
487    public void setRetryWhileRef(String retryWhileRef) {
488        this.retryWhileRef = retryWhileRef;
489    }
490
491    public String getDeadLetterUri() {
492        return deadLetterUri;
493    }
494
495    public void setDeadLetterUri(String deadLetterUri) {
496        this.deadLetter = null;
497        this.deadLetterUri = deadLetterUri;
498    }
499
500    public Endpoint getDeadLetter() {
501        return deadLetter;
502    }
503
504    public void setDeadLetter(Endpoint deadLetter) {
505        this.deadLetter = deadLetter;
506        this.deadLetterUri = deadLetter.getEndpointUri();
507    }
508
509    public boolean isDeadLetterHandleNewException() {
510        return deadLetterHandleNewException;
511    }
512
513    public void setDeadLetterHandleNewException(boolean deadLetterHandleNewException) {
514        this.deadLetterHandleNewException = deadLetterHandleNewException;
515    }
516
517    public boolean isUseOriginalMessage() {
518        return useOriginalMessage;
519    }
520
521    public void setUseOriginalMessage(boolean useOriginalMessage) {
522        this.useOriginalMessage = useOriginalMessage;
523    }
524
525    public boolean isAsyncDelayedRedelivery() {
526        return asyncDelayedRedelivery;
527    }
528
529    public void setAsyncDelayedRedelivery(boolean asyncDelayedRedelivery) {
530        this.asyncDelayedRedelivery = asyncDelayedRedelivery;
531    }
532
533    public String getExecutorServiceRef() {
534        return executorServiceRef;
535    }
536
537    public void setExecutorServiceRef(String executorServiceRef) {
538        this.executorServiceRef = executorServiceRef;
539    }
540
541    public Processor getOnPrepareFailure() {
542        return onPrepareFailure;
543    }
544
545    public void setOnPrepareFailure(Processor onPrepareFailure) {
546        this.onPrepareFailure = onPrepareFailure;
547    }
548
549    public Processor getOnExceptionOccurred() {
550        return onExceptionOccurred;
551    }
552
553    public void setOnExceptionOccurred(Processor onExceptionOccurred) {
554        this.onExceptionOccurred = onExceptionOccurred;
555    }
556
557    protected RedeliveryPolicy createRedeliveryPolicy() {
558        RedeliveryPolicy policy = new RedeliveryPolicy();
559        policy.disableRedelivery();
560        return policy;
561    }
562
563    protected CamelLogger createLogger() {
564        return new CamelLogger(LoggerFactory.getLogger(DefaultErrorHandler.class), LoggingLevel.ERROR);
565    }
566
567    protected synchronized ScheduledExecutorService getExecutorService(CamelContext camelContext) {
568        if (executorService == null || executorService.isShutdown()) {
569            // camel context will shutdown the executor when it shutdown so no need to shut it down when stopping
570            if (executorServiceRef != null) {
571                executorService = camelContext.getRegistry().lookupByNameAndType(executorServiceRef, ScheduledExecutorService.class);
572                if (executorService == null) {
573                    ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
574                    ThreadPoolProfile profile = manager.getThreadPoolProfile(executorServiceRef);
575                    executorService = manager.newScheduledThreadPool(this, executorServiceRef, profile);
576                }
577                if (executorService == null) {
578                    throw new IllegalArgumentException("ExecutorServiceRef " + executorServiceRef + " not found in registry.");
579                }
580            } else {
581                // no explicit configured thread pool, so leave it up to the error handler to decide if it need
582                // a default thread pool from CamelContext#getErrorHandlerExecutorService
583                executorService = null;
584            }
585        }
586        return executorService;
587    }
588
589    @Override
590    public String toString() {
591        return "DefaultErrorHandlerBuilder";
592    }
593
594}