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