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