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 }