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 }