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 org.apache.camel.Endpoint;
020    import org.apache.camel.Exchange;
021    import org.apache.camel.Expression;
022    import org.apache.camel.LoggingLevel;
023    import org.apache.camel.Predicate;
024    import org.apache.camel.Processor;
025    import org.apache.camel.processor.DeadLetterChannel;
026    import org.apache.camel.processor.ErrorHandlerSupport;
027    import org.apache.camel.processor.Logger;
028    import org.apache.camel.processor.RecipientList;
029    import org.apache.camel.processor.RedeliveryPolicy;
030    import org.apache.camel.processor.SendProcessor;
031    import org.apache.camel.processor.exceptionpolicy.ExceptionPolicyStrategy;
032    import org.apache.camel.spi.RouteContext;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import static org.apache.camel.builder.PredicateBuilder.toPredicate;
036    
037    /**
038     * A builder of a <a
039     * href="http://camel.apache.org/dead-letter-channel.html">Dead Letter
040     * Channel</a>
041     *
042     * @version $Revision: 774230 $
043     */
044    public class DeadLetterChannelBuilder extends ErrorHandlerBuilderSupport {
045        private Logger logger = new Logger(LogFactory.getLog(DeadLetterChannel.class), LoggingLevel.ERROR);
046        private ExceptionPolicyStrategy exceptionPolicyStrategy = ErrorHandlerSupport.createDefaultExceptionPolicyStrategy();
047        private RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
048        private Processor onRedelivery;
049        private Processor failureProcessor;
050        private Endpoint deadLetter;
051        private String deadLetterUri;
052        private Predicate handledPolicy;
053        private boolean useOriginalBody;
054    
055        /**
056         * Creates a default DeadLetterChannel with a default endpoint
057         */
058        public DeadLetterChannelBuilder() {
059            this("log:org.apache.camel.DeadLetterChannel?level=error");
060        }
061    
062        /**
063         * Creates a DeadLetterChannel using the given endpoint
064         *
065         * @param deadLetter the dead letter queue
066         */
067        public DeadLetterChannelBuilder(Endpoint deadLetter) {
068            setDeadLetter(deadLetter);
069        }
070    
071        /**
072         * Creates a DeadLetterChannel using the given endpoint
073         *
074         * @param uri the dead letter queue
075         */
076        public DeadLetterChannelBuilder(String uri) {
077            setDeadLetterUri(uri);
078        }
079    
080        public Processor createErrorHandler(RouteContext routeContext, Processor processor) throws Exception {
081            DeadLetterChannel answer = new DeadLetterChannel(processor, getFailureProcessor(), deadLetterUri, onRedelivery,
082                    getRedeliveryPolicy(), getLogger(), getExceptionPolicyStrategy(), getHandledPolicy(), isUseOriginalBody());
083            // must enable stream cache as DeadLetterChannel can do redeliveries and
084            // thus it needs to be able to read the stream again
085            configure(answer);
086            return answer;
087        }
088    
089        public boolean supportTransacted() {
090            return false;
091        }
092    
093        // Builder methods
094        // -------------------------------------------------------------------------
095        public DeadLetterChannelBuilder backOffMultiplier(double backOffMultiplier) {
096            getRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
097            return this;
098        }
099    
100        public DeadLetterChannelBuilder collisionAvoidancePercent(short collisionAvoidancePercent) {
101            getRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
102            return this;
103        }
104    
105        public DeadLetterChannelBuilder delay(long delay) {
106            getRedeliveryPolicy().delay(delay);
107            return this;
108        }
109    
110        public DeadLetterChannelBuilder delayPattern(String delayPattern) {
111            getRedeliveryPolicy().delayPattern(delayPattern);
112            return this;
113        }
114    
115        public DeadLetterChannelBuilder maximumRedeliveries(int maximumRedeliveries) {
116            getRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
117            return this;
118        }
119    
120        public DeadLetterChannelBuilder disableRedelivery() {
121            getRedeliveryPolicy().maximumRedeliveries(0);
122            return this;
123        }
124    
125        public DeadLetterChannelBuilder maximumRedeliveryDelay(long maximumRedeliveryDelay) {
126            getRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
127            return this;
128        }
129    
130        public DeadLetterChannelBuilder useCollisionAvoidance() {
131            getRedeliveryPolicy().useCollisionAvoidance();
132            return this;
133        }
134    
135        public DeadLetterChannelBuilder useExponentialBackOff() {
136            getRedeliveryPolicy().useExponentialBackOff();
137            return this;
138        }
139    
140        public DeadLetterChannelBuilder retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
141            getRedeliveryPolicy().setRetriesExhaustedLogLevel(retriesExhaustedLogLevel);
142            return this;
143        }
144    
145        public DeadLetterChannelBuilder retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
146            getRedeliveryPolicy().setRetryAttemptedLogLevel(retryAttemptedLogLevel);
147            return this;
148        }
149    
150        public DeadLetterChannelBuilder logStackTrace(boolean logStackTrace) {
151            getRedeliveryPolicy().setLogStackTrace(logStackTrace);
152            return this;
153        }
154    
155        /**
156         * Sets whether the exchange should be marked as handled or not.
157         *
158         * @param handled  handled or not
159         * @return the builder
160         */
161        public DeadLetterChannelBuilder handled(boolean handled) {
162            Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
163            return handled(expression);
164        }
165    
166        /**
167         * Sets whether the exchange should be marked as handled or not.
168         *
169         * @param handled  predicate that determines true or false
170         * @return the builder
171         */
172        public DeadLetterChannelBuilder handled(Predicate handled) {
173            this.setHandledPolicy(handled);
174            return this;
175        }
176    
177        /**
178         * Sets whether the exchange should be marked as handled or not.
179         *
180         * @param handled  expression that determines true or false
181         * @return the builder
182         */
183        public DeadLetterChannelBuilder handled(Expression handled) {
184            this.setHandledPolicy(toPredicate(handled));
185            return this;
186        }
187    
188        /**
189         * Sets the logger used for caught exceptions
190         *
191         * @param logger the logger
192         * @return the builder
193         */
194        public DeadLetterChannelBuilder logger(Logger logger) {
195            setLogger(logger);
196            return this;
197        }
198    
199        /**
200         * Sets the logging level of exceptions caught
201         *
202         * @param level the logging level
203         * @return the builder
204         */
205        public DeadLetterChannelBuilder loggingLevel(LoggingLevel level) {
206            getLogger().setLevel(level);
207            return this;
208        }
209    
210        /**
211         * Sets the log used for caught exceptions
212         *
213         * @param log the logger
214         * @return the builder
215         */
216        public DeadLetterChannelBuilder log(Log log) {
217            getLogger().setLog(log);
218            return this;
219        }
220    
221        /**
222         * Sets the log used for caught exceptions
223         *
224         * @param log the log name
225         * @return the builder
226         */
227        public DeadLetterChannelBuilder log(String log) {
228            return log(LogFactory.getLog(log));
229        }
230    
231        /**
232         * Sets the log used for caught exceptions
233         *
234         * @param log the log class
235         * @return the builder
236         */
237        public DeadLetterChannelBuilder log(Class log) {
238            return log(LogFactory.getLog(log));
239        }
240    
241        /**
242         * Sets the exception policy to use
243         *
244         * @return the builder
245         */
246        public DeadLetterChannelBuilder exceptionPolicyStrategy(ExceptionPolicyStrategy exceptionPolicyStrategy) {
247            setExceptionPolicyStrategy(exceptionPolicyStrategy);
248            return this;
249        }
250    
251        /**
252         * Sets a processor that should be processed <b>before</b> a redelivey attempt.
253         * <p/>
254         * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
255         *
256         * @return the builder
257         */
258        public DeadLetterChannelBuilder onRedelivery(Processor processor) {
259            setOnRedelivery(processor);
260            return this;
261        }
262    
263        /**
264         * Will use the original input body when an {@link Exchange} is moved to the dead letter queue.
265         * <p/>
266         * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link Exchange} is doomed for failure.
267         * <br/>
268         * Instead of using the current inprogress {@link Exchange} IN body we use the original IN body instead. This allows
269         * you to store the original input in the dead letter queue instead of the inprogress snapshot of the IN body.
270         * For instance if you route transform the IN body during routing and then failed. With the original exchange
271         * store in the dead letter queue it might be easier to manually re submit the {@link Exchange} again as the IN body
272         * is the same as when Camel received it. So you should be able to send the {@link Exchange} to the same input.
273         * <p/>
274         * By default this feature is off.
275         *
276         * @return the builder
277         */
278        public DeadLetterChannelBuilder useOriginalBody() {
279            setUseOriginalBody(true);
280            return this;
281        }
282    
283        // Properties
284        // -------------------------------------------------------------------------
285    
286        public Processor getFailureProcessor() {
287            if (failureProcessor == null) {
288                if (deadLetter != null) {
289                    failureProcessor = new SendProcessor(deadLetter);
290                } else {
291                    // use a recipient list since we only have an uri for the endpoint
292                    failureProcessor = new RecipientList(new Expression() {
293                        public Object evaluate(Exchange exchange) {
294                            return deadLetterUri;
295                        }
296    
297                        public <T> T evaluate(Exchange exchange, Class<T> type) {
298                            return exchange.getContext().getTypeConverter().convertTo(type, deadLetterUri);
299                        }
300                    });
301                }
302            }
303            return failureProcessor;
304        }
305    
306        public void setFailureProcessor(Processor failureProcessor) {
307            this.failureProcessor = failureProcessor;
308        }
309    
310        public String getDeadLetterUri() {
311            return deadLetterUri;
312        }
313    
314        public void setDeadLetterUri(String deadLetterUri) {
315            this.deadLetter = null;
316            this.deadLetterUri = deadLetterUri;
317        }
318    
319        public Endpoint getDeadLetter() {
320            return deadLetter;
321        }
322    
323        public void setDeadLetter(Endpoint deadLetter) {
324            this.deadLetter = deadLetter;
325            this.deadLetterUri = deadLetter.getEndpointUri();
326        }
327    
328        public RedeliveryPolicy getRedeliveryPolicy() {
329            return redeliveryPolicy;
330        }
331    
332        /**
333         * Sets the redelivery policy
334         */
335        public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
336            this.redeliveryPolicy = redeliveryPolicy;
337        }
338    
339        public Logger getLogger() {
340            return logger;
341        }
342    
343        public void setLogger(Logger logger) {
344            this.logger = logger;
345        }
346    
347        /**
348         * Sets the exception policy strategy to use for resolving the {@link org.apache.camel.model.OnExceptionDefinition}
349         * to use for a given thrown exception
350         */
351        public ExceptionPolicyStrategy getExceptionPolicyStrategy() {
352            return exceptionPolicyStrategy;
353        }
354    
355        public void setExceptionPolicyStrategy(ExceptionPolicyStrategy exceptionPolicyStrategy) {
356            this.exceptionPolicyStrategy = exceptionPolicyStrategy;
357        }
358    
359        public Processor getOnRedelivery() {
360            return onRedelivery;
361        }
362    
363        public void setOnRedelivery(Processor onRedelivery) {
364            this.onRedelivery = onRedelivery;
365        }
366    
367        public Predicate getHandledPolicy() {
368            return handledPolicy;
369        }
370    
371        public void setHandledPolicy(Predicate handled) {
372            this.handledPolicy = handled;
373        }
374    
375        /**
376         * Sets the handled using a boolean and thus easier to use for Spring XML configuration as well
377         */
378        public void setHandled(boolean handled) {
379            handled(handled);
380        }
381    
382        public boolean isUseOriginalBody() {
383            return useOriginalBody;
384        }
385    
386        public void setUseOriginalBody(boolean useOriginalBody) {
387            this.useOriginalBody = useOriginalBody;
388        }
389    
390        @Override
391        public String toString() {
392            return "DeadLetterChannelBuilder(" + deadLetterUri + ")";
393        }
394    }