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. The route, in particular the consumer's component,
230     * must support the Asynchronous Routing Engine (e.g. seda)
231     *
232     * @see org.apache.camel.processor.RedeliveryPolicy#setAsyncDelayedRedelivery(boolean)
233     * @return the builder
234     */
235    public DefaultErrorHandlerBuilder asyncDelayedRedelivery() {
236        getRedeliveryPolicy().setAsyncDelayedRedelivery(true);
237        return this;
238    }
239
240    /**
241     * Controls whether to allow redelivery while stopping/shutting down a route that uses error handling.
242     *
243     * @param allowRedeliveryWhileStopping <tt>true</tt> to allow redelivery, <tt>false</tt> to reject redeliveries
244     * @return the builder
245     */
246    public DefaultErrorHandlerBuilder allowRedeliveryWhileStopping(boolean allowRedeliveryWhileStopping) {
247        getRedeliveryPolicy().setAllowRedeliveryWhileStopping(allowRedeliveryWhileStopping);
248        return this;
249    }
250
251    /**
252     * Sets a reference to a thread pool to be used for redelivery.
253     *
254     * @param ref reference to a scheduled thread pool
255     * @return the builder.
256     */
257    public DefaultErrorHandlerBuilder executorServiceRef(String ref) {
258        setExecutorServiceRef(ref);
259        return this;
260    }
261
262    /**
263     * Sets the logger used for caught exceptions
264     *
265     * @param logger the logger
266     * @return the builder
267     */
268    public DefaultErrorHandlerBuilder logger(CamelLogger logger) {
269        setLogger(logger);
270        return this;
271    }
272
273    /**
274     * Sets the logging level of exceptions caught
275     *
276     * @param level the logging level
277     * @return the builder
278     */
279    public DefaultErrorHandlerBuilder loggingLevel(LoggingLevel level) {
280        getLogger().setLevel(level);
281        return this;
282    }
283
284    /**
285     * Sets the log used for caught exceptions
286     *
287     * @param log the logger
288     * @return the builder
289     */
290    public DefaultErrorHandlerBuilder log(org.slf4j.Logger log) {
291        getLogger().setLog(log);
292        return this;
293    }
294
295    /**
296     * Sets the log used for caught exceptions
297     *
298     * @param log the log name
299     * @return the builder
300     */
301    public DefaultErrorHandlerBuilder log(String log) {
302        return log(LoggerFactory.getLogger(log));
303    }
304
305    /**
306     * Sets the log used for caught exceptions
307     *
308     * @param log the log class
309     * @return the builder
310     */
311    public DefaultErrorHandlerBuilder log(Class<?> log) {
312        return log(LoggerFactory.getLogger(log));
313    }
314
315    /**
316     * Sets a processor that should be processed <b>before</b> a redelivery attempt.
317     * <p/>
318     * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
319     *
320     * @param processor the processor
321     * @return the builder
322     */
323    public DefaultErrorHandlerBuilder onRedelivery(Processor processor) {
324        setOnRedelivery(processor);
325        return this;
326    }
327
328    /**
329     * Sets the retry while expression.
330     * <p/>
331     * Will continue retrying until expression evaluates to <tt>false</tt>.
332     *
333     * @param retryWhile expression that determines when to stop retrying
334     * @return the builder
335     */
336    public DefaultErrorHandlerBuilder retryWhile(Expression retryWhile) {
337        setRetryWhile(ExpressionToPredicateAdapter.toPredicate(retryWhile));
338        return this;
339    }
340
341    /**
342     * Will use the original input {@link org.apache.camel.Message} when an {@link org.apache.camel.Exchange}
343     * is moved to the dead letter queue.
344     * <p/>
345     * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange}
346     * is doomed for failure.
347     * <br/>
348     * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN message we use the original
349     * IN message instead. This allows you to store the original input in the dead letter queue instead of the inprogress
350     * snapshot of the IN message.
351     * For instance if you route transform the IN body during routing and then failed. With the original exchange
352     * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange}
353     * again as the IN message is the same as when Camel received it.
354     * So you should be able to send the {@link org.apache.camel.Exchange} to the same input.
355     * <p/>
356     * By default this feature is off.
357     *
358     * @return the builder
359     */
360    public DefaultErrorHandlerBuilder useOriginalMessage() {
361        setUseOriginalMessage(true);
362        return this;
363    }
364
365    /**
366     * Whether the dead letter channel should handle (and ignore) any new exception that may been thrown during sending the
367     * message to the dead letter endpoint.
368     * <p/>
369     * The default value is <tt>true</tt> which means any such kind of exception is handled and ignored. Set this to <tt>false</tt>
370     * to let the exception be propagated back on the {@link org.apache.camel.Exchange}. This can be used in situations
371     * where you use transactions, and want to use Camel's dead letter channel to deal with exceptions during routing,
372     * but if the dead letter channel itself fails because of a new exception being thrown, then by setting this to <tt>false</tt>
373     * the new exceptions is propagated back and set on the {@link org.apache.camel.Exchange}, which allows the transaction
374     * to detect the exception, and rollback.
375     *
376     * @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}
377     * @return the builder
378     */
379    public DefaultErrorHandlerBuilder deadLetterHandleNewException(boolean handleNewException) {
380        setDeadLetterHandleNewException(handleNewException);
381        return this;
382    }
383
384    /**
385     * @deprecated use {@link #deadLetterHandleNewException(boolean)}} with value <tt>false</tt>
386     */
387    @Deprecated
388    public DefaultErrorHandlerBuilder checkException() {
389        setDeadLetterHandleNewException(false);
390        return this;
391    }
392
393    /**
394     * Sets a custom {@link org.apache.camel.Processor} to prepare the {@link org.apache.camel.Exchange} before
395     * handled by the failure processor / dead letter channel. This allows for example to enrich the message
396     * before sending to a dead letter queue.
397     *
398     * @param processor the processor
399     * @return the builder
400     */
401    public DefaultErrorHandlerBuilder onPrepareFailure(Processor processor) {
402        setOnPrepareFailure(processor);
403        return this;
404    }
405
406    /**
407     * Sets a custom {@link org.apache.camel.Processor} to process the {@link org.apache.camel.Exchange} just after an exception was thrown.
408     * This allows to execute the processor at the same time the exception was thrown.
409     * <p/>
410     * Important: Any exception thrown from this processor will be ignored.
411     *
412     * @param processor the processor
413     * @return the builder
414     */
415    public DefaultErrorHandlerBuilder onExceptionOccurred(Processor processor) {
416        setOnExceptionOccurred(processor);
417        return this;
418    }
419
420    // Properties
421    // -------------------------------------------------------------------------
422
423    public Processor getFailureProcessor() {
424        return failureProcessor;
425    }
426
427    public void setFailureProcessor(Processor failureProcessor) {
428        this.failureProcessor = failureProcessor;
429    }
430
431    public RedeliveryPolicy getRedeliveryPolicy() {
432        if (redeliveryPolicy == null) {
433            redeliveryPolicy = createRedeliveryPolicy();
434        }
435        return redeliveryPolicy;
436    }
437
438    /**
439     * Sets the redelivery policy
440     */
441    public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
442        this.redeliveryPolicy = redeliveryPolicy;
443    }
444
445    public CamelLogger getLogger() {
446        if (logger == null) {
447            logger = createLogger();
448        }
449        return logger;
450    }
451
452    public void setLogger(CamelLogger logger) {
453        this.logger = logger;
454    }
455
456    public Processor getOnRedelivery() {
457        return onRedelivery;
458    }
459
460    public void setOnRedelivery(Processor onRedelivery) {
461        this.onRedelivery = onRedelivery;
462    }
463
464    public Predicate getRetryWhilePolicy(CamelContext context) {
465        Predicate answer = getRetryWhile();
466
467        if (getRetryWhileRef() != null) {
468            // its a bean expression
469            Language bean = context.resolveLanguage("bean");
470            answer = bean.createPredicate(getRetryWhileRef());
471        }
472
473        return answer;
474    }
475
476    public Predicate getRetryWhile() {
477        return retryWhile;
478    }
479
480    public void setRetryWhile(Predicate retryWhile) {
481        this.retryWhile = retryWhile;
482    }
483
484    public String getRetryWhileRef() {
485        return retryWhileRef;
486    }
487
488    public void setRetryWhileRef(String retryWhileRef) {
489        this.retryWhileRef = retryWhileRef;
490    }
491
492    public String getDeadLetterUri() {
493        return deadLetterUri;
494    }
495
496    public void setDeadLetterUri(String deadLetterUri) {
497        this.deadLetter = null;
498        this.deadLetterUri = deadLetterUri;
499    }
500
501    public Endpoint getDeadLetter() {
502        return deadLetter;
503    }
504
505    public void setDeadLetter(Endpoint deadLetter) {
506        this.deadLetter = deadLetter;
507        this.deadLetterUri = deadLetter.getEndpointUri();
508    }
509
510    public boolean isDeadLetterHandleNewException() {
511        return deadLetterHandleNewException;
512    }
513
514    public void setDeadLetterHandleNewException(boolean deadLetterHandleNewException) {
515        this.deadLetterHandleNewException = deadLetterHandleNewException;
516    }
517
518    public boolean isUseOriginalMessage() {
519        return useOriginalMessage;
520    }
521
522    public void setUseOriginalMessage(boolean useOriginalMessage) {
523        this.useOriginalMessage = useOriginalMessage;
524    }
525
526    public boolean isAsyncDelayedRedelivery() {
527        return asyncDelayedRedelivery;
528    }
529
530    public void setAsyncDelayedRedelivery(boolean asyncDelayedRedelivery) {
531        this.asyncDelayedRedelivery = asyncDelayedRedelivery;
532    }
533
534    public String getExecutorServiceRef() {
535        return executorServiceRef;
536    }
537
538    public void setExecutorServiceRef(String executorServiceRef) {
539        this.executorServiceRef = executorServiceRef;
540    }
541
542    public Processor getOnPrepareFailure() {
543        return onPrepareFailure;
544    }
545
546    public void setOnPrepareFailure(Processor onPrepareFailure) {
547        this.onPrepareFailure = onPrepareFailure;
548    }
549
550    public Processor getOnExceptionOccurred() {
551        return onExceptionOccurred;
552    }
553
554    public void setOnExceptionOccurred(Processor onExceptionOccurred) {
555        this.onExceptionOccurred = onExceptionOccurred;
556    }
557
558    protected RedeliveryPolicy createRedeliveryPolicy() {
559        RedeliveryPolicy policy = new RedeliveryPolicy();
560        policy.disableRedelivery();
561        return policy;
562    }
563
564    protected CamelLogger createLogger() {
565        return new CamelLogger(LoggerFactory.getLogger(DefaultErrorHandler.class), LoggingLevel.ERROR);
566    }
567
568    protected synchronized ScheduledExecutorService getExecutorService(CamelContext camelContext) {
569        if (executorService == null || executorService.isShutdown()) {
570            // camel context will shutdown the executor when it shutdown so no need to shut it down when stopping
571            if (executorServiceRef != null) {
572                executorService = camelContext.getRegistry().lookupByNameAndType(executorServiceRef, ScheduledExecutorService.class);
573                if (executorService == null) {
574                    ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
575                    ThreadPoolProfile profile = manager.getThreadPoolProfile(executorServiceRef);
576                    executorService = manager.newScheduledThreadPool(this, executorServiceRef, profile);
577                }
578                if (executorService == null) {
579                    throw new IllegalArgumentException("ExecutorServiceRef " + executorServiceRef + " not found in registry.");
580                }
581            } else {
582                // no explicit configured thread pool, so leave it up to the error handler to decide if it need
583                // a default thread pool from CamelContext#getErrorHandlerExecutorService
584                executorService = null;
585            }
586        }
587        return executorService;
588    }
589
590    @Override
591    public String toString() {
592        return "DefaultErrorHandlerBuilder";
593    }
594
595}