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: 774230 $
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 = "useOriginalBody", required = false)
069 private Boolean useOriginalBodyPolicy = 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 back off multiplier
252 *
253 * @param backOffMultiplier the back off multiplier
254 * @return the builder
255 */
256 public OnExceptionDefinition backOffMultiplier(double backOffMultiplier) {
257 getOrCreateRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
258 return this;
259 }
260
261 /**
262 * Sets the collision avoidance factor
263 *
264 * @param collisionAvoidanceFactor the factor
265 * @return the builder
266 */
267 public OnExceptionDefinition collisionAvoidanceFactor(double collisionAvoidanceFactor) {
268 getOrCreateRedeliveryPolicy().collisionAvoidanceFactor(collisionAvoidanceFactor);
269 return this;
270 }
271
272 /**
273 * Sets the collision avoidance percentage
274 *
275 * @param collisionAvoidancePercent the percentage
276 * @return the builder
277 */
278 public OnExceptionDefinition collisionAvoidancePercent(short collisionAvoidancePercent) {
279 getOrCreateRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
280 return this;
281 }
282
283 /**
284 * Sets the fixed delay between redeliveries
285 *
286 * @param delay delay in millis
287 * @return the builder
288 */
289 public OnExceptionDefinition redeliveryDelay(long delay) {
290 getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
291 return this;
292 }
293
294 /**
295 * Sets the logging level to use when retries has exhausted
296 *
297 * @param retriesExhaustedLogLevel the logging level
298 * @return the builder
299 */
300 public OnExceptionDefinition retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
301 getOrCreateRedeliveryPolicy().retriesExhaustedLogLevel(retriesExhaustedLogLevel);
302 return this;
303 }
304
305 /**
306 * Sets the logging level to use for logging retry attempts
307 *
308 * @param retryAttemptedLogLevel the logging level
309 * @return the builder
310 */
311 public OnExceptionDefinition retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
312 getOrCreateRedeliveryPolicy().retryAttemptedLogLevel(retryAttemptedLogLevel);
313 return this;
314 }
315
316 /**
317 * Sets the maximum redeliveries
318 * <ul>
319 * <li>5 = default value</li>
320 * <li>0 = no redeliveries</li>
321 * <li>-1 = redeliver forever</li>
322 * </ul>
323 *
324 * @param maximumRedeliveries the value
325 * @return the builder
326 */
327 public OnExceptionDefinition maximumRedeliveries(int maximumRedeliveries) {
328 getOrCreateRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
329 return this;
330 }
331
332 /**
333 * Turn on collision avoidance.
334 *
335 * @return the builder
336 */
337 public OnExceptionDefinition useCollisionAvoidance() {
338 getOrCreateRedeliveryPolicy().useCollisionAvoidance();
339 return this;
340 }
341
342 /**
343 * Turn on exponential backk off
344 *
345 * @return the builder
346 */
347 public OnExceptionDefinition useExponentialBackOff() {
348 getOrCreateRedeliveryPolicy().useExponentialBackOff();
349 return this;
350 }
351
352 /**
353 * Sets the maximum delay between redelivery
354 *
355 * @param maximumRedeliveryDelay the delay in millis
356 * @return the builder
357 */
358 public OnExceptionDefinition maximumRedeliveryDelay(long maximumRedeliveryDelay) {
359 getOrCreateRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
360 return this;
361 }
362
363 /**
364 * Will use the original input body when an {@link org.apache.camel.Exchange} is moved to the dead letter queue.
365 * <p/>
366 * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange} is doomed for failure.
367 * <br/>
368 * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN body we use the original IN body instead. This allows
369 * you to store the original input in the dead letter queue instead of the inprogress snapshot of the IN body.
370 * For instance if you route transform the IN body during routing and then failed. With the original exchange
371 * 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
372 * 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.
373 * <p/>
374 * By default this feature is off.
375 *
376 * @return the builder
377 */
378 public OnExceptionDefinition useOriginalBody() {
379 setUseOriginalBodyPolicy(Boolean.TRUE);
380 return this;
381 }
382
383 /**
384 * Sets a processor that should be processed <b>before</b> a redelivey attempt.
385 * <p/>
386 * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
387 */
388 public OnExceptionDefinition onRedelivery(Processor processor) {
389 setOnRedelivery(processor);
390 return this;
391 }
392
393 // Properties
394 //-------------------------------------------------------------------------
395 public List<ProcessorDefinition> getOutputs() {
396 return outputs;
397 }
398
399 public void setOutputs(List<ProcessorDefinition> outputs) {
400 this.outputs = outputs;
401 }
402
403 public List<Class> getExceptionClasses() {
404 if (exceptionClasses == null) {
405 exceptionClasses = createExceptionClasses();
406 }
407 return exceptionClasses;
408 }
409
410 public void setExceptionClasses(List<Class> exceptionClasses) {
411 this.exceptionClasses = exceptionClasses;
412 }
413
414 public List<String> getExceptions() {
415 return exceptions;
416 }
417
418 public void setExceptions(List<String> exceptions) {
419 this.exceptions = exceptions;
420 }
421
422 public Processor getErrorHandler() {
423 return errorHandler;
424 }
425
426 public RedeliveryPolicyDefinition getRedeliveryPolicy() {
427 return redeliveryPolicy;
428 }
429
430 public void setRedeliveryPolicy(RedeliveryPolicyDefinition redeliveryPolicy) {
431 this.redeliveryPolicy = redeliveryPolicy;
432 }
433
434 public Predicate getHandledPolicy() {
435 return handledPolicy;
436 }
437
438 public void setHandled(ExpressionSubElementDefinition handled) {
439 this.handled = handled;
440 }
441
442 public ExpressionSubElementDefinition getHandled() {
443 return handled;
444 }
445
446 public void setHandledPolicy(Predicate handledPolicy) {
447 this.handledPolicy = handledPolicy;
448 }
449
450 public WhenDefinition getOnWhen() {
451 return onWhen;
452 }
453
454 public void setOnWhen(WhenDefinition onWhen) {
455 this.onWhen = onWhen;
456 }
457
458 public ExpressionSubElementDefinition getRetryUntil() {
459 return retryUntil;
460 }
461
462 public void setRetryUntil(ExpressionSubElementDefinition retryUntil) {
463 this.retryUntil = retryUntil;
464 }
465
466 public Predicate getRetryUntilPolicy() {
467 return retryUntilPolicy;
468 }
469
470 public void setRetryUntilPolicy(Predicate retryUntilPolicy) {
471 this.retryUntilPolicy = retryUntilPolicy;
472 }
473
474 public Processor getOnRedelivery() {
475 return onRedelivery;
476 }
477
478 public void setOnRedelivery(Processor onRedelivery) {
479 this.onRedelivery = onRedelivery;
480 }
481
482 public String getOnRedeliveryRef() {
483 return onRedeliveryRef;
484 }
485
486 public void setOnRedeliveryRef(String onRedeliveryRef) {
487 this.onRedeliveryRef = onRedeliveryRef;
488 }
489
490 public Boolean getUseOriginalBodyPolicy() {
491 return useOriginalBodyPolicy;
492 }
493
494 public void setUseOriginalBodyPolicy(Boolean useOriginalBodyPolicy) {
495 this.useOriginalBodyPolicy = useOriginalBodyPolicy;
496 }
497
498 // Implementation methods
499 //-------------------------------------------------------------------------
500 protected RedeliveryPolicyDefinition getOrCreateRedeliveryPolicy() {
501 if (redeliveryPolicy == null) {
502 redeliveryPolicy = new RedeliveryPolicyDefinition();
503 }
504 return redeliveryPolicy;
505 }
506
507 protected List<Class> createExceptionClasses() {
508 List<String> list = getExceptions();
509 List<Class> answer = new ArrayList<Class>(list.size());
510 for (String name : list) {
511 Class type = ObjectHelper.loadClass(name, getClass().getClassLoader());
512 answer.add(type);
513 }
514 return answer;
515 }
516
517
518 private void setHandledFromExpressionType(RouteContext routeContext) {
519 if (getHandled() != null && handledPolicy == null && routeContext != null) {
520 handled(getHandled().createPredicate(routeContext));
521 }
522 }
523
524 private void setRetryUntilFromExpressionType(RouteContext routeContext) {
525 if (getRetryUntil() != null && retryUntilPolicy == null && routeContext != null) {
526 retryUntil(getRetryUntil().createPredicate(routeContext));
527 }
528 }
529
530 }