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.model;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.List;
022    
023    import javax.xml.bind.annotation.XmlAccessType;
024    import javax.xml.bind.annotation.XmlAccessorType;
025    import javax.xml.bind.annotation.XmlAttribute;
026    import javax.xml.bind.annotation.XmlElement;
027    import javax.xml.bind.annotation.XmlElementRef;
028    import javax.xml.bind.annotation.XmlRootElement;
029    import javax.xml.bind.annotation.XmlTransient;
030    
031    import org.apache.camel.CamelContext;
032    import org.apache.camel.Expression;
033    import org.apache.camel.LoggingLevel;
034    import org.apache.camel.Predicate;
035    import org.apache.camel.Processor;
036    import org.apache.camel.Route;
037    import org.apache.camel.builder.ErrorHandlerBuilder;
038    import org.apache.camel.builder.ExpressionBuilder;
039    import org.apache.camel.builder.ExpressionClause;
040    import org.apache.camel.processor.CatchProcessor;
041    import org.apache.camel.processor.RedeliveryPolicy;
042    import org.apache.camel.spi.RouteContext;
043    import org.apache.camel.util.ObjectHelper;
044    
045    import static org.apache.camel.builder.PredicateBuilder.toPredicate;
046    
047    /**
048     * Represents an XML <onException/> element
049     *
050     * @version $Revision: 792966 $
051     */
052    @XmlRootElement(name = "onException")
053    @XmlAccessorType(XmlAccessType.FIELD)
054    public class OnExceptionDefinition extends ProcessorDefinition<ProcessorDefinition> {
055    
056        @XmlElement(name = "exception")
057        private List<String> exceptions = new ArrayList<String>();
058        @XmlElement(name = "onWhen", required = false)
059        private WhenDefinition onWhen;
060        @XmlElement(name = "retryUntil", required = false)
061        private ExpressionSubElementDefinition retryUntil;
062        @XmlElement(name = "redeliveryPolicy", required = false)
063        private RedeliveryPolicyDefinition redeliveryPolicy;
064        @XmlElement(name = "handled", required = false)
065        private ExpressionSubElementDefinition handled;
066        @XmlAttribute(name = "onRedeliveryRef", required = false)
067        private String onRedeliveryRef;
068        @XmlAttribute(name = "useOriginalMessage", required = false)
069        private Boolean useOriginalMessagePolicy = Boolean.FALSE;
070        @XmlElementRef
071        private List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
072        @XmlTransient
073        private List<Class> exceptionClasses;
074        @XmlTransient
075        private Processor errorHandler;
076        @XmlTransient
077        private Predicate handledPolicy;
078        @XmlTransient
079        private Predicate retryUntilPolicy;
080        @XmlTransient
081        private Processor onRedelivery;
082    
083        public OnExceptionDefinition() {
084        }
085    
086        public OnExceptionDefinition(List<Class> exceptionClasses) {
087            this.exceptionClasses = exceptionClasses;
088        }
089    
090        public OnExceptionDefinition(Class exceptionType) {
091            exceptionClasses = new ArrayList<Class>();
092            exceptionClasses.add(exceptionType);
093        }
094    
095        @Override
096        public String getShortName() {
097            return "onException";
098        }
099    
100        @Override
101        public String toString() {
102            return "OnException[" + getExceptionClasses() + (onWhen != null ? " " + onWhen : "") + " -> " + getOutputs() + "]";
103        }
104        
105        /**
106         * Allows an exception handler to create a new redelivery policy for this exception type
107         * @param context the camel context
108         * @param parentPolicy the current redelivery policy
109         * @return a newly created redelivery policy, or return the original policy if no customization is required
110         * for this exception handler.
111         */
112        public RedeliveryPolicy createRedeliveryPolicy(CamelContext context, RedeliveryPolicy parentPolicy) {
113            if (redeliveryPolicy != null) {
114                return redeliveryPolicy.createRedeliveryPolicy(context, parentPolicy);
115            } else if (errorHandler != null) {
116                // lets create a new error handler that has no retries
117                RedeliveryPolicy answer = parentPolicy.copy();
118                answer.setMaximumRedeliveries(0);
119                return answer;
120            }
121            return parentPolicy;
122        }
123    
124        public void addRoutes(RouteContext routeContext, Collection<Route> routes) throws Exception {
125            setHandledFromExpressionType(routeContext);
126            setRetryUntilFromExpressionType(routeContext);
127            // lookup onRedelivery if ref is provided
128            if (ObjectHelper.isNotEmpty(onRedeliveryRef)) {
129                setOnRedelivery(routeContext.lookup(onRedeliveryRef, Processor.class));
130            }
131    
132            // lets attach this on exception to the route error handler
133            errorHandler = routeContext.createProcessor(this);
134            ErrorHandlerBuilder builder = routeContext.getRoute().getErrorHandlerBuilder();
135            builder.addErrorHandlers(this);
136        }
137    
138        @Override
139        public CatchProcessor createProcessor(RouteContext routeContext) throws Exception {
140            Processor childProcessor = routeContext.createProcessor(this);
141    
142            Predicate when = null;
143            if (onWhen != null) {
144                when = onWhen.getExpression().createPredicate(routeContext);
145            }
146    
147            Predicate handle = null;
148            if (handled != null) {
149                handle = handled.createPredicate(routeContext);
150            }
151    
152            return new CatchProcessor(getExceptionClasses(), childProcessor, when, handle);
153        }
154    
155    
156        // Fluent API
157        //-------------------------------------------------------------------------
158    
159        @Override
160        public OnExceptionDefinition onException(Class exceptionType) {
161            getExceptionClasses().add(exceptionType);
162            return this;
163        }
164    
165        /**
166         * Sets whether the exchange should be marked as handled or not.
167         *
168         * @param handled  handled or not
169         * @return the builder
170         */
171        public OnExceptionDefinition handled(boolean handled) {
172            Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
173            return handled(expression);
174        }
175        
176        /**
177         * Sets whether the exchange should be marked as handled or not.
178         *
179         * @param handled  predicate that determines true or false
180         * @return the builder
181         */
182        public OnExceptionDefinition handled(Predicate handled) {
183            setHandledPolicy(handled);
184            return this;
185        }
186        
187        /**
188         * Sets whether the exchange should be marked as handled or not.
189         *
190         * @param handled  expression that determines true or false
191         * @return the builder
192         */
193        public OnExceptionDefinition handled(Expression handled) {
194            setHandledPolicy(toPredicate(handled));
195            return this;
196        }
197    
198        /**
199         * Sets an additional predicate that should be true before the onException is triggered.
200         * <p/>
201         * To be used for fine grained controlling whether a thrown exception should be intercepted
202         * by this exception type or not.
203         *
204         * @param predicate  predicate that determines true or false
205         * @return the builder
206         */
207        public OnExceptionDefinition onWhen(Predicate predicate) {
208            setOnWhen(new WhenDefinition(predicate));
209            return this;
210        }
211    
212        /**
213         * Creates an expression to configure an additional predicate that should be true before the
214         * onException is triggered.
215         * <p/>
216         * To be used for fine grained controlling whether a thrown exception should be intercepted
217         * by this exception type or not.
218         *
219         * @return the expression clause to configure
220         */
221        public ExpressionClause<OnExceptionDefinition> onWhen() {
222            onWhen = new WhenDefinition();
223            ExpressionClause<OnExceptionDefinition> clause = new ExpressionClause<OnExceptionDefinition>(this);
224            onWhen.setExpression(clause);
225            return clause;
226        }
227    
228        /**
229         * Sets the retry until predicate.
230         *
231         * @param until predicate that determines when to stop retrying
232         * @return the builder
233         */
234        public OnExceptionDefinition retryUntil(Predicate until) {
235            setRetryUntilPolicy(until);
236            return this;
237        }
238    
239        /**
240         * Sets the retry until expression.
241         *
242         * @param until expression that determines when to stop retrying
243         * @return the builder
244         */
245        public OnExceptionDefinition retryUntil(Expression until) {
246            setRetryUntilPolicy(toPredicate(until));
247            return this;
248        }
249    
250        /**
251         * Sets the delay
252         *
253         * @param delay  the redeliver delay
254         * @return the builder
255         */
256        public OnExceptionDefinition redeliverDelay(long delay) {
257            getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
258            return this;
259        }
260    
261        /**
262         * Sets the back off multiplier
263         *
264         * @param backOffMultiplier  the back off multiplier
265         * @return the builder
266         */
267        public OnExceptionDefinition backOffMultiplier(double backOffMultiplier) {
268            getOrCreateRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
269            return this;
270        }
271    
272        /**
273         * Sets the collision avoidance factor
274         *
275         * @param collisionAvoidanceFactor  the factor
276         * @return the builder
277         */
278        public OnExceptionDefinition collisionAvoidanceFactor(double collisionAvoidanceFactor) {
279            getOrCreateRedeliveryPolicy().collisionAvoidanceFactor(collisionAvoidanceFactor);
280            return this;
281        }
282    
283        /**
284         * Sets the collision avoidance percentage
285         *
286         * @param collisionAvoidancePercent  the percentage
287         * @return the builder
288         */
289        public OnExceptionDefinition collisionAvoidancePercent(short collisionAvoidancePercent) {
290            getOrCreateRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
291            return this;
292        }
293    
294        /**
295         * Sets the fixed delay between redeliveries
296         *
297         * @param delay  delay in millis
298         * @return the builder
299         */
300        public OnExceptionDefinition redeliveryDelay(long delay) {
301            getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
302            return this;
303        }
304    
305        /**
306         * Sets the logging level to use when retries has exhausted
307         *
308         * @param retriesExhaustedLogLevel  the logging level
309         * @return the builder
310         */
311        public OnExceptionDefinition retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
312            getOrCreateRedeliveryPolicy().retriesExhaustedLogLevel(retriesExhaustedLogLevel);
313            return this;
314        }
315    
316        /**
317         * Sets the logging level to use for logging retry attempts
318         *
319         * @param retryAttemptedLogLevel  the logging level
320         * @return the builder
321         */
322        public OnExceptionDefinition retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
323            getOrCreateRedeliveryPolicy().retryAttemptedLogLevel(retryAttemptedLogLevel);
324            return this;
325        }
326    
327        /**
328         * Sets the maximum redeliveries
329         * <ul>
330         *   <li>5 = default value</li>
331         *   <li>0 = no redeliveries</li>
332         *   <li>-1 = redeliver forever</li>
333         * </ul>
334         *
335         * @param maximumRedeliveries  the value
336         * @return the builder
337         */
338        public OnExceptionDefinition maximumRedeliveries(int maximumRedeliveries) {
339            getOrCreateRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
340            return this;
341        }
342    
343        /**
344         * Turn on collision avoidance.
345         *
346         * @return the builder
347         */
348        public OnExceptionDefinition useCollisionAvoidance() {
349            getOrCreateRedeliveryPolicy().useCollisionAvoidance();
350            return this;
351        }
352    
353        /**
354         * Turn on exponential backk off
355         *
356         * @return the builder
357         */
358        public OnExceptionDefinition useExponentialBackOff() {
359            getOrCreateRedeliveryPolicy().useExponentialBackOff();
360            return this;
361        }
362    
363        /**
364         * Sets the maximum delay between redelivery
365         *
366         * @param maximumRedeliveryDelay  the delay in millis
367         * @return the builder
368         */
369        public OnExceptionDefinition maximumRedeliveryDelay(long maximumRedeliveryDelay) {
370            getOrCreateRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
371            return this;
372        }
373    
374        /**
375         * Will use the original input body when an {@link org.apache.camel.Exchange} is moved to the dead letter queue.
376         * <p/>
377         * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange} is doomed for failure.
378         * <br/>
379         * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN body we use the original IN body instead. This allows
380         * you to store the original input in the dead letter queue instead of the inprogress snapshot of the IN body.
381         * For instance if you route transform the IN body during routing and then failed. With the original exchange
382         * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange} again as the IN body
383         * is the same as when Camel received it. So you should be able to send the {@link org.apache.camel.Exchange} to the same input.
384         * <p/>
385         * By default this feature is off.
386         *
387         * @return the builder
388         */
389        public OnExceptionDefinition useOriginalBody() {
390            setUseOriginalMessagePolicy(Boolean.TRUE);
391            return this;
392        }
393    
394        /**
395         * Sets a processor that should be processed <b>before</b> a redelivey attempt.
396         * <p/>
397         * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
398         */
399        public OnExceptionDefinition onRedelivery(Processor processor) {
400            setOnRedelivery(processor);
401            return this;
402        }
403    
404        // Properties
405        //-------------------------------------------------------------------------
406        public List<ProcessorDefinition> getOutputs() {
407            return outputs;
408        }
409    
410        public void setOutputs(List<ProcessorDefinition> outputs) {
411            this.outputs = outputs;
412        }
413    
414        public List<Class> getExceptionClasses() {
415            if (exceptionClasses == null) {
416                exceptionClasses = createExceptionClasses();
417            }
418            return exceptionClasses;
419        }
420    
421        public void setExceptionClasses(List<Class> exceptionClasses) {
422            this.exceptionClasses = exceptionClasses;
423        }
424    
425        public List<String> getExceptions() {
426            return exceptions;
427        }
428    
429        public void setExceptions(List<String> exceptions) {
430            this.exceptions = exceptions;
431        }
432    
433        public Processor getErrorHandler() {
434            return errorHandler;
435        }
436    
437        public RedeliveryPolicyDefinition getRedeliveryPolicy() {
438            return redeliveryPolicy;
439        }
440    
441        public void setRedeliveryPolicy(RedeliveryPolicyDefinition redeliveryPolicy) {
442            this.redeliveryPolicy = redeliveryPolicy;
443        }
444    
445        public Predicate getHandledPolicy() {
446            return handledPolicy;
447        }
448    
449        public void setHandled(ExpressionSubElementDefinition handled) {
450            this.handled = handled;
451        }
452    
453        public ExpressionSubElementDefinition getHandled() {
454            return handled;
455        }    
456    
457        public void setHandledPolicy(Predicate handledPolicy) {
458            this.handledPolicy = handledPolicy;
459        }
460    
461        public WhenDefinition getOnWhen() {
462            return onWhen;
463        }
464    
465        public void setOnWhen(WhenDefinition onWhen) {
466            this.onWhen = onWhen;
467        }
468    
469        public ExpressionSubElementDefinition getRetryUntil() {
470            return retryUntil;
471        }
472    
473        public void setRetryUntil(ExpressionSubElementDefinition retryUntil) {
474            this.retryUntil = retryUntil;
475        }
476    
477        public Predicate getRetryUntilPolicy() {
478            return retryUntilPolicy;
479        }
480    
481        public void setRetryUntilPolicy(Predicate retryUntilPolicy) {
482            this.retryUntilPolicy = retryUntilPolicy;
483        }
484    
485        public Processor getOnRedelivery() {
486            return onRedelivery;
487        }
488    
489        public void setOnRedelivery(Processor onRedelivery) {
490            this.onRedelivery = onRedelivery;
491        }
492    
493        public String getOnRedeliveryRef() {
494            return onRedeliveryRef;
495        }
496    
497        public void setOnRedeliveryRef(String onRedeliveryRef) {
498            this.onRedeliveryRef = onRedeliveryRef;
499        }
500    
501        public Boolean getUseOriginalMessagePolicy() {
502            return useOriginalMessagePolicy;
503        }
504    
505        public void setUseOriginalMessagePolicy(Boolean useOriginalMessagePolicy) {
506            this.useOriginalMessagePolicy = useOriginalMessagePolicy;
507        }
508    
509        // Implementation methods
510        //-------------------------------------------------------------------------
511        protected RedeliveryPolicyDefinition getOrCreateRedeliveryPolicy() {
512            if (redeliveryPolicy == null) {
513                redeliveryPolicy = new RedeliveryPolicyDefinition();
514            }
515            return redeliveryPolicy;
516        }
517    
518        protected List<Class> createExceptionClasses() {
519            List<String> list = getExceptions();
520            List<Class> answer = new ArrayList<Class>(list.size());
521            for (String name : list) {
522                Class type = ObjectHelper.loadClass(name, getClass().getClassLoader());
523                answer.add(type);
524            }
525            return answer;
526        }
527    
528    
529        private void setHandledFromExpressionType(RouteContext routeContext) {
530            if (getHandled() != null && handledPolicy == null && routeContext != null) {
531                handled(getHandled().createPredicate(routeContext));
532            }
533        }
534    
535        private void setRetryUntilFromExpressionType(RouteContext routeContext) {
536            if (getRetryUntil() != null && retryUntilPolicy == null && routeContext != null) {
537                retryUntil(getRetryUntil().createPredicate(routeContext));
538            }
539        }
540    
541    }