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     */
017    package org.apache.camel.builder;
018    
019    import java.util.concurrent.ScheduledExecutorService;
020    
021    import org.apache.camel.CamelContext;
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.Expression;
024    import org.apache.camel.LoggingLevel;
025    import org.apache.camel.Predicate;
026    import org.apache.camel.Processor;
027    import org.apache.camel.processor.DefaultErrorHandler;
028    import org.apache.camel.processor.RedeliveryPolicy;
029    import org.apache.camel.spi.ExecutorServiceManager;
030    import org.apache.camel.spi.Language;
031    import org.apache.camel.spi.RouteContext;
032    import org.apache.camel.spi.ThreadPoolProfile;
033    import org.apache.camel.util.CamelLogger;
034    import org.apache.camel.util.ExpressionToPredicateAdapter;
035    import org.slf4j.LoggerFactory;
036    
037    /**
038     * The default error handler builder.
039     *
040     * @version 
041     */
042    public 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        /**
198         * Will allow asynchronous delayed redeliveries.
199         *
200         * @see org.apache.camel.processor.RedeliveryPolicy#setAsyncDelayedRedelivery(boolean)
201         * @return the builder
202         */
203        public DefaultErrorHandlerBuilder asyncDelayedRedelivery() {
204            getRedeliveryPolicy().setAsyncDelayedRedelivery(true);
205            return this;
206        }
207    
208        /**
209         * Sets a reference to a thread pool to be used for redelivery.
210         *
211         * @param ref reference to a scheduled thread pool
212         * @return the builder.
213         */
214        public DefaultErrorHandlerBuilder executorServiceRef(String ref) {
215            setExecutorServiceRef(ref);
216            return this;
217        }
218    
219        /**
220         * Sets the logger used for caught exceptions
221         *
222         * @param logger the logger
223         * @return the builder
224         */
225        public DefaultErrorHandlerBuilder logger(CamelLogger logger) {
226            setLogger(logger);
227            return this;
228        }
229    
230        /**
231         * Sets the logging level of exceptions caught
232         *
233         * @param level the logging level
234         * @return the builder
235         */
236        public DefaultErrorHandlerBuilder loggingLevel(LoggingLevel level) {
237            getLogger().setLevel(level);
238            return this;
239        }
240    
241        /**
242         * Sets the log used for caught exceptions
243         *
244         * @param log the logger
245         * @return the builder
246         */
247        public DefaultErrorHandlerBuilder log(org.slf4j.Logger log) {
248            getLogger().setLog(log);
249            return this;
250        }
251    
252        /**
253         * Sets the log used for caught exceptions
254         *
255         * @param log the log name
256         * @return the builder
257         */
258        public DefaultErrorHandlerBuilder log(String log) {
259            return log(LoggerFactory.getLogger(log));
260        }
261    
262        /**
263         * Sets the log used for caught exceptions
264         *
265         * @param log the log class
266         * @return the builder
267         */
268        public DefaultErrorHandlerBuilder log(Class<?> log) {
269            return log(LoggerFactory.getLogger(log));
270        }
271    
272        /**
273         * Sets a processor that should be processed <b>before</b> a redelivery attempt.
274         * <p/>
275         * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
276         *
277         * @param processor the processor
278         * @return the builder
279         */
280        public DefaultErrorHandlerBuilder onRedelivery(Processor processor) {
281            setOnRedelivery(processor);
282            return this;
283        }
284    
285        /**
286         * Sets the retry while expression.
287         * <p/>
288         * Will continue retrying until expression evaluates to <tt>false</tt>.
289         *
290         * @param retryWhile expression that determines when to stop retrying
291         * @return the builder
292         */
293        public DefaultErrorHandlerBuilder retryWhile(Expression retryWhile) {
294            setRetryWhile(ExpressionToPredicateAdapter.toPredicate(retryWhile));
295            return this;
296        }
297    
298        /**
299         * Will use the original input {@link org.apache.camel.Message} when an {@link org.apache.camel.Exchange}
300         * is moved to the dead letter queue.
301         * <p/>
302         * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange}
303         * is doomed for failure.
304         * <br/>
305         * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN message we use the original
306         * IN message instead. This allows you to store the original input in the dead letter queue instead of the inprogress
307         * snapshot of the IN message.
308         * For instance if you route transform the IN body during routing and then failed. With the original exchange
309         * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange}
310         * again as the IN message is the same as when Camel received it.
311         * So you should be able to send the {@link org.apache.camel.Exchange} to the same input.
312         * <p/>
313         * By default this feature is off.
314         *
315         * @return the builder
316         */
317        public DefaultErrorHandlerBuilder useOriginalMessage() {
318            setUseOriginalMessage(true);
319            return this;
320        }
321    
322        // Properties
323        // -------------------------------------------------------------------------
324    
325        public Processor getFailureProcessor() {
326            return failureProcessor;
327        }
328    
329        public void setFailureProcessor(Processor failureProcessor) {
330            this.failureProcessor = failureProcessor;
331        }
332    
333        public RedeliveryPolicy getRedeliveryPolicy() {
334            if (redeliveryPolicy == null) {
335                redeliveryPolicy = createRedeliveryPolicy();
336            }
337            return redeliveryPolicy;
338        }
339    
340        /**
341         * Sets the redelivery policy
342         */
343        public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
344            this.redeliveryPolicy = redeliveryPolicy;
345        }
346    
347        public CamelLogger getLogger() {
348            if (logger == null) {
349                logger = createLogger();
350            }
351            return logger;
352        }
353    
354        public void setLogger(CamelLogger logger) {
355            this.logger = logger;
356        }
357    
358        public Processor getOnRedelivery() {
359            return onRedelivery;
360        }
361    
362        public void setOnRedelivery(Processor onRedelivery) {
363            this.onRedelivery = onRedelivery;
364        }
365    
366        public Predicate getRetryWhilePolicy(CamelContext context) {
367            Predicate answer = getRetryWhile();
368    
369            if (getRetryWhileRef() != null) {
370                // its a bean expression
371                Language bean = context.resolveLanguage("bean");
372                answer = bean.createPredicate(getRetryWhileRef());
373            }
374    
375            return answer;
376        }
377    
378        public Predicate getRetryWhile() {
379            return retryWhile;
380        }
381    
382        public void setRetryWhile(Predicate retryWhile) {
383            this.retryWhile = retryWhile;
384        }
385    
386        public String getRetryWhileRef() {
387            return retryWhileRef;
388        }
389    
390        public void setRetryWhileRef(String retryWhileRef) {
391            this.retryWhileRef = retryWhileRef;
392        }
393    
394        public String getDeadLetterUri() {
395            return deadLetterUri;
396        }
397    
398        public void setDeadLetterUri(String deadLetterUri) {
399            this.deadLetter = null;
400            this.deadLetterUri = deadLetterUri;
401        }
402    
403        public Endpoint getDeadLetter() {
404            return deadLetter;
405        }
406    
407        public void setDeadLetter(Endpoint deadLetter) {
408            this.deadLetter = deadLetter;
409            this.deadLetterUri = deadLetter.getEndpointUri();
410        }
411    
412        public boolean isUseOriginalMessage() {
413            return useOriginalMessage;
414        }
415    
416        public void setUseOriginalMessage(boolean useOriginalMessage) {
417            this.useOriginalMessage = useOriginalMessage;
418        }
419    
420        public boolean isAsyncDelayedRedelivery() {
421            return asyncDelayedRedelivery;
422        }
423    
424        public void setAsyncDelayedRedelivery(boolean asyncDelayedRedelivery) {
425            this.asyncDelayedRedelivery = asyncDelayedRedelivery;
426        }
427    
428        public String getExecutorServiceRef() {
429            return executorServiceRef;
430        }
431    
432        public void setExecutorServiceRef(String executorServiceRef) {
433            this.executorServiceRef = executorServiceRef;
434        }
435    
436        protected RedeliveryPolicy createRedeliveryPolicy() {
437            RedeliveryPolicy policy = new RedeliveryPolicy();
438            policy.disableRedelivery();
439            policy.setRedeliveryDelay(0);
440            return policy;
441        }
442    
443        protected CamelLogger createLogger() {
444            return new CamelLogger(LoggerFactory.getLogger(DefaultErrorHandler.class), LoggingLevel.ERROR);
445        }
446    
447        protected synchronized ScheduledExecutorService getExecutorService(CamelContext camelContext) {
448            if (executorService == null || executorService.isShutdown()) {
449                // camel context will shutdown the executor when it shutdown so no need to shut it down when stopping
450                if (executorServiceRef != null) {
451                    executorService = camelContext.getRegistry().lookup(executorServiceRef, ScheduledExecutorService.class);
452                    if (executorService == null) {
453                        ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
454                        ThreadPoolProfile profile = manager.getThreadPoolProfile(executorServiceRef);
455                        executorService = manager.newScheduledThreadPool(this, executorServiceRef, profile);
456                    }
457                    if (executorService == null) {
458                        throw new IllegalArgumentException("ExecutorServiceRef " + executorServiceRef + " not found in registry.");
459                    }
460                } else {
461                    // no explicit configured thread pool, so leave it up to the error handler to decide if it need
462                    // a default thread pool from CamelContext#getErrorHandlerExecutorService
463                    executorService = null;
464                }
465            }
466            return executorService;
467        }
468    
469        @Override
470        public String toString() {
471            return "DefaultErrorHandlerBuilder";
472        }
473    
474    }