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