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