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.ExpressionClause;
039    import org.apache.camel.language.constant.ConstantLanguage;
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: 751373 $
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        @XmlElementRef
069        private List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
070        @XmlTransient
071        private List<Class> exceptionClasses;
072        @XmlTransient
073        private Processor errorHandler;
074        @XmlTransient
075        private Predicate handledPolicy;
076        @XmlTransient
077        private Predicate retryUntilPolicy;
078        @XmlTransient
079        private Processor onRedelivery;
080    
081        public OnExceptionDefinition() {
082        }
083    
084        public OnExceptionDefinition(List<Class> exceptionClasses) {
085            this.exceptionClasses = exceptionClasses;
086        }
087    
088        public OnExceptionDefinition(Class exceptionType) {
089            exceptionClasses = new ArrayList<Class>();
090            exceptionClasses.add(exceptionType);
091        }
092    
093        @Override
094        public String getShortName() {
095            return "onException";
096        }
097    
098        @Override
099        public String toString() {
100            return "OnException[" + getExceptionClasses() + (onWhen != null ? " " + onWhen : "") + " -> " + getOutputs() + "]";
101        }
102        
103        /**
104         * Allows an exception handler to create a new redelivery policy for this exception type
105         * @param context the camel context
106         * @param parentPolicy the current redelivery policy
107         * @return a newly created redelivery policy, or return the original policy if no customization is required
108         * for this exception handler.
109         */
110        public RedeliveryPolicy createRedeliveryPolicy(CamelContext context, RedeliveryPolicy parentPolicy) {
111            if (redeliveryPolicy != null) {
112                return redeliveryPolicy.createRedeliveryPolicy(context, parentPolicy);
113            } else if (errorHandler != null) {
114                // lets create a new error handler that has no retries
115                RedeliveryPolicy answer = parentPolicy.copy();
116                answer.setMaximumRedeliveries(0);
117                return answer;
118            }
119            return parentPolicy;
120        }
121    
122        public void addRoutes(RouteContext routeContext, Collection<Route> routes) throws Exception {
123            setHandledFromExpressionType(routeContext);
124            setRetryUntilFromExpressionType(routeContext);
125            // lets attach a processor to an error handler
126            errorHandler = routeContext.createProcessor(this);
127            ErrorHandlerBuilder builder = routeContext.getRoute().getErrorHandlerBuilder();
128            builder.addErrorHandlers(this);
129    
130            // lookup onRedelivery if ref is provided
131            if (ObjectHelper.isNotEmpty(onRedeliveryRef)) {
132                onRedelivery = routeContext.lookup(onRedeliveryRef, Processor.class);
133            }
134        }
135    
136        @Override
137        public CatchProcessor createProcessor(RouteContext routeContext) throws Exception {
138            Processor childProcessor = routeContext.createProcessor(this);
139            return new CatchProcessor(getExceptionClasses(), childProcessor);
140        }
141    
142    
143        // Fluent API
144        //-------------------------------------------------------------------------
145    
146        @Override
147        public OnExceptionDefinition onException(Class exceptionType) {
148            getExceptionClasses().add(exceptionType);
149            return this;
150        }
151    
152        /**
153         * Sets whether the exchange should be marked as handled or not.
154         *
155         * @param handled  handled or not
156         * @return the builder
157         */
158        public OnExceptionDefinition handled(boolean handled) {
159            ConstantLanguage constant = new ConstantLanguage();
160            return handled(constant.createPredicate(Boolean.toString(handled)));
161        }
162        
163        /**
164         * Sets whether the exchange should be marked as handled or not.
165         *
166         * @param handled  predicate that determines true or false
167         * @return the builder
168         */
169        public OnExceptionDefinition handled(Predicate handled) {
170            setHandledPolicy(handled);
171            return this;
172        }
173        
174        /**
175         * Sets whether the exchange should be marked as handled or not.
176         *
177         * @param handled  expression that determines true or false
178         * @return the builder
179         */
180        public OnExceptionDefinition handled(Expression handled) {
181            setHandledPolicy(toPredicate(handled));
182            return this;
183        }
184    
185        /**
186         * Sets an additional predicate that should be true before the onException is triggered.
187         * <p/>
188         * To be used for fine grained controlling whether a thrown exception should be intercepted
189         * by this exception type or not.
190         *
191         * @param predicate  predicate that determines true or false
192         * @return the builder
193         */
194        public OnExceptionDefinition onWhen(Predicate predicate) {
195            setOnWhen(new WhenDefinition(predicate));
196            return this;
197        }
198    
199        /**
200         * Creates an expression to configure an additional predicate that should be true before the
201         * onException is triggered.
202         * <p/>
203         * To be used for fine grained controlling whether a thrown exception should be intercepted
204         * by this exception type or not.
205         *
206         * @return the expression clause to configure
207         */
208        public ExpressionClause<OnExceptionDefinition> onWhen() {
209            onWhen = new WhenDefinition();
210            ExpressionClause<OnExceptionDefinition> clause = new ExpressionClause<OnExceptionDefinition>(this);
211            onWhen.setExpression(clause);
212            return clause;
213        }
214    
215        /**
216         * Sets the retry until predicate.
217         *
218         * @param until predicate that determines when to stop retrying
219         * @return the builder
220         */
221        public OnExceptionDefinition retryUntil(Predicate until) {
222            setRetryUntilPolicy(until);
223            return this;
224        }
225    
226        /**
227         * Sets the retry until expression.
228         *
229         * @param until expression that determines when to stop retrying
230         * @return the builder
231         */
232        public OnExceptionDefinition retryUntil(Expression until) {
233            setRetryUntilPolicy(toPredicate(until));
234            return this;
235        }
236    
237        /**
238         * Sets the back off multiplier
239         *
240         * @param backOffMultiplier  the back off multiplier
241         * @return the builder
242         */
243        public OnExceptionDefinition backOffMultiplier(double backOffMultiplier) {
244            getOrCreateRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
245            return this;
246        }
247    
248        /**
249         * Sets the collision avoidance factor
250         *
251         * @param collisionAvoidanceFactor  the factor
252         * @return the builder
253         */
254        public OnExceptionDefinition collisionAvoidanceFactor(double collisionAvoidanceFactor) {
255            getOrCreateRedeliveryPolicy().collisionAvoidanceFactor(collisionAvoidanceFactor);
256            return this;
257        }
258    
259        /**
260         * Sets the collision avoidance percentage
261         *
262         * @param collisionAvoidancePercent  the percentage
263         * @return the builder
264         */
265        public OnExceptionDefinition collisionAvoidancePercent(short collisionAvoidancePercent) {
266            getOrCreateRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
267            return this;
268        }
269    
270        /**
271         * Sets the fixed delay between redeliveries
272         *
273         * @param delay  delay in millis
274         * @return the builder
275         */
276        public OnExceptionDefinition redeliveryDelay(long delay) {
277            getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
278            return this;
279        }
280    
281        /**
282         * Sets the logging level to use when retries has exhausted
283         *
284         * @param retriesExhaustedLogLevel  the logging level
285         * @return the builder
286         */
287        public OnExceptionDefinition retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
288            getOrCreateRedeliveryPolicy().retriesExhaustedLogLevel(retriesExhaustedLogLevel);
289            return this;
290        }
291    
292        /**
293         * Sets the logging level to use for logging retry attempts
294         *
295         * @param retryAttemptedLogLevel  the logging level
296         * @return the builder
297         */
298        public OnExceptionDefinition retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
299            getOrCreateRedeliveryPolicy().retryAttemptedLogLevel(retryAttemptedLogLevel);
300            return this;
301        }
302    
303        /**
304         * Sets the maximum redeliveries
305         * <ul>
306         *   <li>5 = default value</li>
307         *   <li>0 = no redeliveries</li>
308         *   <li>-1 = redeliver forever</li>
309         * </ul>
310         *
311         * @param maximumRedeliveries  the value
312         * @return the builder
313         */
314        public OnExceptionDefinition maximumRedeliveries(int maximumRedeliveries) {
315            getOrCreateRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
316            return this;
317        }
318    
319        /**
320         * Turn on collision avoidance.
321         *
322         * @return the builder
323         */
324        public OnExceptionDefinition useCollisionAvoidance() {
325            getOrCreateRedeliveryPolicy().useCollisionAvoidance();
326            return this;
327        }
328    
329        /**
330         * Turn on exponential backk off
331         *
332         * @return the builder
333         */
334        public OnExceptionDefinition useExponentialBackOff() {
335            getOrCreateRedeliveryPolicy().useExponentialBackOff();
336            return this;
337        }
338    
339        /**
340         * Sets the maximum delay between redelivery
341         *
342         * @param maximumRedeliveryDelay  the delay in millis
343         * @return the builder
344         */
345        public OnExceptionDefinition maximumRedeliveryDelay(long maximumRedeliveryDelay) {
346            getOrCreateRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
347            return this;
348        }
349    
350        /**
351         * Sets a processor that should be processed <b>before</b> a redelivey attempt.
352         * <p/>
353         * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
354         */
355        public OnExceptionDefinition onRedelivery(Processor processor) {
356            setOnRedelivery(processor);
357            return this;
358        }
359    
360        // Properties
361        //-------------------------------------------------------------------------
362        public List<ProcessorDefinition> getOutputs() {
363            return outputs;
364        }
365    
366        public void setOutputs(List<ProcessorDefinition> outputs) {
367            this.outputs = outputs;
368        }
369    
370        public List<Class> getExceptionClasses() {
371            if (exceptionClasses == null) {
372                exceptionClasses = createExceptionClasses();
373            }
374            return exceptionClasses;
375        }
376    
377        public void setExceptionClasses(List<Class> exceptionClasses) {
378            this.exceptionClasses = exceptionClasses;
379        }
380    
381        public List<String> getExceptions() {
382            return exceptions;
383        }
384    
385        public void setExceptions(List<String> exceptions) {
386            this.exceptions = exceptions;
387        }
388    
389        public Processor getErrorHandler() {
390            return errorHandler;
391        }
392    
393        public RedeliveryPolicyDefinition getRedeliveryPolicy() {
394            return redeliveryPolicy;
395        }
396    
397        public void setRedeliveryPolicy(RedeliveryPolicyDefinition redeliveryPolicy) {
398            this.redeliveryPolicy = redeliveryPolicy;
399        }
400    
401        public Predicate getHandledPolicy() {
402            return handledPolicy;
403        }
404    
405        public void setHandled(ExpressionSubElementDefinition handled) {
406            this.handled = handled;
407        }
408    
409        public ExpressionSubElementDefinition getHandled() {
410            return handled;
411        }    
412    
413        public void setHandledPolicy(Predicate handledPolicy) {
414            this.handledPolicy = handledPolicy;
415        }
416    
417        public WhenDefinition getOnWhen() {
418            return onWhen;
419        }
420    
421        public void setOnWhen(WhenDefinition onWhen) {
422            this.onWhen = onWhen;
423        }
424    
425        public ExpressionSubElementDefinition getRetryUntil() {
426            return retryUntil;
427        }
428    
429        public void setRetryUntil(ExpressionSubElementDefinition retryUntil) {
430            this.retryUntil = retryUntil;
431        }
432    
433        public Predicate getRetryUntilPolicy() {
434            return retryUntilPolicy;
435        }
436    
437        public void setRetryUntilPolicy(Predicate retryUntilPolicy) {
438            this.retryUntilPolicy = retryUntilPolicy;
439        }
440    
441        public Processor getOnRedelivery() {
442            return onRedelivery;
443        }
444    
445        public void setOnRedelivery(Processor onRedelivery) {
446            this.onRedelivery = onRedelivery;
447        }
448    
449        public String getOnRedeliveryRef() {
450            return onRedeliveryRef;
451        }
452    
453        public void setOnRedeliveryRef(String onRedeliveryRef) {
454            this.onRedeliveryRef = onRedeliveryRef;
455        }
456    
457    
458        // Implementation methods
459        //-------------------------------------------------------------------------
460        protected RedeliveryPolicyDefinition getOrCreateRedeliveryPolicy() {
461            if (redeliveryPolicy == null) {
462                redeliveryPolicy = new RedeliveryPolicyDefinition();
463            }
464            return redeliveryPolicy;
465        }
466    
467        protected List<Class> createExceptionClasses() {
468            List<String> list = getExceptions();
469            List<Class> answer = new ArrayList<Class>(list.size());
470            for (String name : list) {
471                Class type = ObjectHelper.loadClass(name, getClass().getClassLoader());
472                answer.add(type);
473            }
474            return answer;
475        }
476    
477    
478        private void setHandledFromExpressionType(RouteContext routeContext) {
479            if (getHandled() != null && handledPolicy == null && routeContext != null) {
480                handled(getHandled().createPredicate(routeContext));
481            }
482        }
483    
484        private void setRetryUntilFromExpressionType(RouteContext routeContext) {
485            if (getRetryUntil() != null && retryUntilPolicy == null && routeContext != null) {
486                retryUntil(getRetryUntil().createPredicate(routeContext));
487            }
488        }
489    
490    }