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 }