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