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 */ 017package org.apache.camel.processor; 018 019import java.util.ArrayList; 020import java.util.List; 021import java.util.concurrent.Callable; 022import java.util.concurrent.RejectedExecutionException; 023import java.util.concurrent.ScheduledExecutorService; 024import java.util.concurrent.TimeUnit; 025 026import org.apache.camel.AsyncCallback; 027import org.apache.camel.AsyncProcessor; 028import org.apache.camel.CamelContext; 029import org.apache.camel.Exchange; 030import org.apache.camel.LoggingLevel; 031import org.apache.camel.Message; 032import org.apache.camel.Navigate; 033import org.apache.camel.Predicate; 034import org.apache.camel.Processor; 035import org.apache.camel.model.OnExceptionDefinition; 036import org.apache.camel.spi.ExchangeFormatter; 037import org.apache.camel.spi.ShutdownPrepared; 038import org.apache.camel.spi.SubUnitOfWorkCallback; 039import org.apache.camel.spi.UnitOfWork; 040import org.apache.camel.util.AsyncProcessorConverterHelper; 041import org.apache.camel.util.AsyncProcessorHelper; 042import org.apache.camel.util.CamelContextHelper; 043import org.apache.camel.util.CamelLogger; 044import org.apache.camel.util.EventHelper; 045import org.apache.camel.util.ExchangeHelper; 046import org.apache.camel.util.MessageHelper; 047import org.apache.camel.util.ObjectHelper; 048import org.apache.camel.util.ServiceHelper; 049import org.apache.camel.util.URISupport; 050 051/** 052 * Base redeliverable error handler that also supports a final dead letter queue in case 053 * all redelivery attempts fail. 054 * <p/> 055 * This implementation should contain all the error handling logic and the sub classes 056 * should only configure it according to what they support. 057 * 058 * @version 059 */ 060public abstract class RedeliveryErrorHandler extends ErrorHandlerSupport implements AsyncProcessor, ShutdownPrepared, Navigate<Processor> { 061 062 protected ScheduledExecutorService executorService; 063 protected final CamelContext camelContext; 064 protected final Processor deadLetter; 065 protected final String deadLetterUri; 066 protected final boolean deadLetterHandleNewException; 067 protected final Processor output; 068 protected final AsyncProcessor outputAsync; 069 protected final Processor redeliveryProcessor; 070 protected final RedeliveryPolicy redeliveryPolicy; 071 protected final Predicate retryWhilePolicy; 072 protected final CamelLogger logger; 073 protected final boolean useOriginalMessagePolicy; 074 protected boolean redeliveryEnabled; 075 protected volatile boolean preparingShutdown; 076 protected final ExchangeFormatter exchangeFormatter; 077 078 /** 079 * Contains the current redelivery data 080 */ 081 protected class RedeliveryData { 082 Exchange original; 083 boolean sync = true; 084 int redeliveryCounter; 085 long redeliveryDelay; 086 Predicate retryWhilePredicate; 087 boolean redeliverFromSync; 088 089 // default behavior which can be overloaded on a per exception basis 090 RedeliveryPolicy currentRedeliveryPolicy; 091 Processor deadLetterProcessor; 092 Processor failureProcessor; 093 Processor onRedeliveryProcessor; 094 Predicate handledPredicate; 095 Predicate continuedPredicate; 096 boolean useOriginalInMessage; 097 boolean handleNewException; 098 099 public RedeliveryData() { 100 // init with values from the error handler 101 this.retryWhilePredicate = retryWhilePolicy; 102 this.currentRedeliveryPolicy = redeliveryPolicy; 103 this.deadLetterProcessor = deadLetter; 104 this.onRedeliveryProcessor = redeliveryProcessor; 105 this.handledPredicate = getDefaultHandledPredicate(); 106 this.useOriginalInMessage = useOriginalMessagePolicy; 107 this.handleNewException = deadLetterHandleNewException; 108 } 109 } 110 111 /** 112 * Tasks which performs asynchronous redelivery attempts, and being triggered by a 113 * {@link java.util.concurrent.ScheduledExecutorService} to avoid having any threads blocking if a task 114 * has to be delayed before a redelivery attempt is performed. 115 */ 116 private class AsyncRedeliveryTask implements Callable<Boolean> { 117 118 private final Exchange exchange; 119 private final AsyncCallback callback; 120 private final RedeliveryData data; 121 122 public AsyncRedeliveryTask(Exchange exchange, AsyncCallback callback, RedeliveryData data) { 123 this.exchange = exchange; 124 this.callback = callback; 125 this.data = data; 126 } 127 128 public Boolean call() throws Exception { 129 // prepare for redelivery 130 prepareExchangeForRedelivery(exchange, data); 131 132 // letting onRedeliver be executed at first 133 deliverToOnRedeliveryProcessor(exchange, data); 134 135 if (log.isTraceEnabled()) { 136 log.trace("Redelivering exchangeId: {} -> {} for Exchange: {}", new Object[]{exchange.getExchangeId(), outputAsync, exchange}); 137 } 138 139 // emmit event we are doing redelivery 140 EventHelper.notifyExchangeRedelivery(exchange.getContext(), exchange, data.redeliveryCounter); 141 142 // process the exchange (also redelivery) 143 boolean sync; 144 if (data.redeliverFromSync) { 145 // this redelivery task was scheduled from synchronous, which we forced to be asynchronous from 146 // this error handler, which means we have to invoke the callback with false, to have the callback 147 // be notified when we are done 148 sync = outputAsync.process(exchange, new AsyncCallback() { 149 public void done(boolean doneSync) { 150 log.trace("Redelivering exchangeId: {} done sync: {}", exchange.getExchangeId(), doneSync); 151 152 // mark we are in sync mode now 153 data.sync = false; 154 155 // only process if the exchange hasn't failed 156 // and it has not been handled by the error processor 157 if (isDone(exchange)) { 158 callback.done(false); 159 return; 160 } 161 162 // error occurred so loop back around which we do by invoking the processAsyncErrorHandler 163 processAsyncErrorHandler(exchange, callback, data); 164 } 165 }); 166 } else { 167 // this redelivery task was scheduled from asynchronous, which means we should only 168 // handle when the asynchronous task was done 169 sync = outputAsync.process(exchange, new AsyncCallback() { 170 public void done(boolean doneSync) { 171 log.trace("Redelivering exchangeId: {} done sync: {}", exchange.getExchangeId(), doneSync); 172 173 // this callback should only handle the async case 174 if (doneSync) { 175 return; 176 } 177 178 // mark we are in async mode now 179 data.sync = false; 180 181 // only process if the exchange hasn't failed 182 // and it has not been handled by the error processor 183 if (isDone(exchange)) { 184 callback.done(doneSync); 185 return; 186 } 187 // error occurred so loop back around which we do by invoking the processAsyncErrorHandler 188 processAsyncErrorHandler(exchange, callback, data); 189 } 190 }); 191 } 192 193 return sync; 194 } 195 } 196 197 public RedeliveryErrorHandler(CamelContext camelContext, Processor output, CamelLogger logger, 198 Processor redeliveryProcessor, RedeliveryPolicy redeliveryPolicy, Processor deadLetter, 199 String deadLetterUri, boolean deadLetterHandleNewException, boolean useOriginalMessagePolicy, Predicate retryWhile, ScheduledExecutorService executorService) { 200 201 ObjectHelper.notNull(camelContext, "CamelContext", this); 202 ObjectHelper.notNull(redeliveryPolicy, "RedeliveryPolicy", this); 203 204 this.camelContext = camelContext; 205 this.redeliveryProcessor = redeliveryProcessor; 206 this.deadLetter = deadLetter; 207 this.output = output; 208 this.outputAsync = AsyncProcessorConverterHelper.convert(output); 209 this.redeliveryPolicy = redeliveryPolicy; 210 this.logger = logger; 211 this.deadLetterUri = deadLetterUri; 212 this.deadLetterHandleNewException = deadLetterHandleNewException; 213 this.useOriginalMessagePolicy = useOriginalMessagePolicy; 214 this.retryWhilePolicy = retryWhile; 215 this.executorService = executorService; 216 217 if (ObjectHelper.isNotEmpty(redeliveryPolicy.getExchangeFormatterRef())) { 218 ExchangeFormatter formatter = camelContext.getRegistry().lookupByNameAndType(redeliveryPolicy.getExchangeFormatterRef(), ExchangeFormatter.class); 219 if (formatter != null) { 220 this.exchangeFormatter = formatter; 221 } else { 222 throw new IllegalArgumentException("Cannot find the exchangeFormatter by using reference id " + redeliveryPolicy.getExchangeFormatterRef()); 223 } 224 } else { 225 // setup exchange formatter to be used for message history dump 226 DefaultExchangeFormatter formatter = new DefaultExchangeFormatter(); 227 formatter.setShowExchangeId(true); 228 formatter.setMultiline(true); 229 formatter.setShowHeaders(true); 230 formatter.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed); 231 try { 232 Integer maxChars = CamelContextHelper.parseInteger(camelContext, camelContext.getProperty(Exchange.LOG_DEBUG_BODY_MAX_CHARS)); 233 if (maxChars != null) { 234 formatter.setMaxChars(maxChars); 235 } 236 } catch (Exception e) { 237 throw ObjectHelper.wrapRuntimeCamelException(e); 238 } 239 this.exchangeFormatter = formatter; 240 } 241 } 242 243 public boolean supportTransacted() { 244 return false; 245 } 246 247 @Override 248 public boolean hasNext() { 249 return output != null; 250 } 251 252 @Override 253 public List<Processor> next() { 254 if (!hasNext()) { 255 return null; 256 } 257 List<Processor> answer = new ArrayList<Processor>(1); 258 answer.add(output); 259 return answer; 260 } 261 262 protected boolean isRunAllowed(RedeliveryData data) { 263 // if camel context is forcing a shutdown then do not allow running 264 boolean forceShutdown = camelContext.getShutdownStrategy().forceShutdown(this); 265 if (forceShutdown) { 266 log.trace("isRunAllowed() -> false (Run not allowed as ShutdownStrategy is forcing shutting down)"); 267 return false; 268 } 269 270 // redelivery policy can control if redelivery is allowed during stopping/shutdown 271 // but this only applies during a redelivery (counter must > 0) 272 if (data.redeliveryCounter > 0) { 273 if (data.currentRedeliveryPolicy.allowRedeliveryWhileStopping) { 274 log.trace("isRunAllowed() -> true (Run allowed as RedeliverWhileStopping is enabled)"); 275 return true; 276 } else if (preparingShutdown) { 277 // we are preparing for shutdown, now determine if we can still run 278 boolean answer = isRunAllowedOnPreparingShutdown(); 279 log.trace("isRunAllowed() -> {} (Run not allowed as we are preparing for shutdown)", answer); 280 return answer; 281 } 282 } 283 284 // we cannot run if we are stopping/stopped 285 boolean answer = !isStoppingOrStopped(); 286 log.trace("isRunAllowed() -> {} (Run allowed if we are not stopped/stopping)", answer); 287 return answer; 288 } 289 290 protected boolean isRunAllowedOnPreparingShutdown() { 291 return false; 292 } 293 294 protected boolean isRedeliveryAllowed(RedeliveryData data) { 295 // redelivery policy can control if redelivery is allowed during stopping/shutdown 296 // but this only applies during a redelivery (counter must > 0) 297 if (data.redeliveryCounter > 0) { 298 boolean stopping = isStoppingOrStopped(); 299 if (!preparingShutdown && !stopping) { 300 log.trace("isRedeliveryAllowed() -> true (we are not stopping/stopped)"); 301 return true; 302 } else { 303 // we are stopping or preparing to shutdown 304 if (data.currentRedeliveryPolicy.allowRedeliveryWhileStopping) { 305 log.trace("isRedeliveryAllowed() -> true (Redelivery allowed as RedeliverWhileStopping is enabled)"); 306 return true; 307 } else { 308 log.trace("isRedeliveryAllowed() -> false (Redelivery not allowed as RedeliverWhileStopping is disabled)"); 309 return false; 310 } 311 } 312 } 313 314 return true; 315 } 316 317 @Override 318 public void prepareShutdown(boolean forced) { 319 // prepare for shutdown, eg do not allow redelivery if configured 320 log.trace("Prepare shutdown on error handler {}", this); 321 preparingShutdown = true; 322 } 323 324 public void process(Exchange exchange) throws Exception { 325 if (output == null) { 326 // no output then just return 327 return; 328 } 329 AsyncProcessorHelper.process(this, exchange); 330 } 331 332 /** 333 * Process the exchange using redelivery error handling. 334 */ 335 public boolean process(final Exchange exchange, final AsyncCallback callback) { 336 final RedeliveryData data = new RedeliveryData(); 337 338 // do a defensive copy of the original Exchange, which is needed for redelivery so we can ensure the 339 // original Exchange is being redelivered, and not a mutated Exchange 340 data.original = defensiveCopyExchangeIfNeeded(exchange); 341 342 // use looping to have redelivery attempts 343 while (true) { 344 345 // can we still run 346 if (!isRunAllowed(data)) { 347 log.trace("Run not allowed, will reject executing exchange: {}", exchange); 348 if (exchange.getException() == null) { 349 exchange.setException(new RejectedExecutionException()); 350 } 351 // we cannot process so invoke callback 352 callback.done(data.sync); 353 return data.sync; 354 } 355 356 // did previous processing cause an exception? 357 boolean handle = shouldHandleException(exchange); 358 if (handle) { 359 handleException(exchange, data); 360 } 361 362 // compute if we are exhausted, and whether redelivery is allowed 363 boolean exhausted = isExhausted(exchange, data); 364 boolean redeliverAllowed = isRedeliveryAllowed(data); 365 366 // if we are exhausted or redelivery is not allowed, then deliver to failure processor (eg such as DLC) 367 if (!redeliverAllowed || exhausted) { 368 Processor target = null; 369 boolean deliver = true; 370 371 // the unit of work may have an optional callback associated we need to leverage 372 SubUnitOfWorkCallback uowCallback = exchange.getUnitOfWork().getSubUnitOfWorkCallback(); 373 if (uowCallback != null) { 374 // signal to the callback we are exhausted 375 uowCallback.onExhausted(exchange); 376 // do not deliver to the failure processor as its been handled by the callback instead 377 deliver = false; 378 } 379 380 if (deliver) { 381 // should deliver to failure processor (either from onException or the dead letter channel) 382 target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; 383 } 384 // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a fair 385 // bit of work for exhausted exchanges (its only the target processor which may be null if handled by a savepoint) 386 boolean isDeadLetterChannel = isDeadLetterChannel() && (target == null || target == data.deadLetterProcessor); 387 boolean sync = deliverToFailureProcessor(target, isDeadLetterChannel, exchange, data, callback); 388 // we are breaking out 389 return sync; 390 } 391 392 if (data.redeliveryCounter > 0) { 393 // calculate delay 394 data.redeliveryDelay = determineRedeliveryDelay(exchange, data.currentRedeliveryPolicy, data.redeliveryDelay, data.redeliveryCounter); 395 396 if (data.redeliveryDelay > 0) { 397 // okay there is a delay so create a scheduled task to have it executed in the future 398 399 if (data.currentRedeliveryPolicy.isAsyncDelayedRedelivery() && !exchange.isTransacted()) { 400 401 // we are doing a redelivery then a thread pool must be configured (see the doStart method) 402 ObjectHelper.notNull(executorService, "Redelivery is enabled but ExecutorService has not been configured.", this); 403 404 // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can used a scheduler to 405 // have it being executed in the future, or immediately 406 // we are continuing asynchronously 407 408 // mark we are routing async from now and that this redelivery task came from a synchronous routing 409 data.sync = false; 410 data.redeliverFromSync = true; 411 AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); 412 413 // schedule the redelivery task 414 if (log.isTraceEnabled()) { 415 log.trace("Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); 416 } 417 executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); 418 419 return false; 420 } else { 421 // async delayed redelivery was disabled or we are transacted so we must be synchronous 422 // as the transaction manager requires to execute in the same thread context 423 try { 424 data.currentRedeliveryPolicy.sleep(data.redeliveryDelay); 425 } catch (InterruptedException e) { 426 // we was interrupted so break out 427 exchange.setException(e); 428 // mark the exchange to stop continue routing when interrupted 429 // as we do not want to continue routing (for example a task has been cancelled) 430 exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); 431 callback.done(data.sync); 432 return data.sync; 433 } 434 } 435 } 436 437 // prepare for redelivery 438 prepareExchangeForRedelivery(exchange, data); 439 440 // letting onRedeliver be executed 441 deliverToOnRedeliveryProcessor(exchange, data); 442 443 // emmit event we are doing redelivery 444 EventHelper.notifyExchangeRedelivery(exchange.getContext(), exchange, data.redeliveryCounter); 445 } 446 447 // process the exchange (also redelivery) 448 boolean sync = outputAsync.process(exchange, new AsyncCallback() { 449 public void done(boolean sync) { 450 // this callback should only handle the async case 451 if (sync) { 452 return; 453 } 454 455 // mark we are in async mode now 456 data.sync = false; 457 458 // if we are done then notify callback and exit 459 if (isDone(exchange)) { 460 callback.done(sync); 461 return; 462 } 463 464 // error occurred so loop back around which we do by invoking the processAsyncErrorHandler 465 // method which takes care of this in a asynchronous manner 466 processAsyncErrorHandler(exchange, callback, data); 467 } 468 }); 469 470 if (!sync) { 471 // the remainder of the Exchange is being processed asynchronously so we should return 472 return false; 473 } 474 // we continue to route synchronously 475 476 // if we are done then notify callback and exit 477 boolean done = isDone(exchange); 478 if (done) { 479 callback.done(true); 480 return true; 481 } 482 483 // error occurred so loop back around..... 484 } 485 } 486 487 /** 488 * <p>Determines the redelivery delay time by first inspecting the Message header {@link Exchange#REDELIVERY_DELAY} 489 * and if not present, defaulting to {@link RedeliveryPolicy#calculateRedeliveryDelay(long, int)}</p> 490 * 491 * <p>In order to prevent manipulation of the RedeliveryData state, the values of {@link RedeliveryData#redeliveryDelay} 492 * and {@link RedeliveryData#redeliveryCounter} are copied in.</p> 493 * 494 * @param exchange The current exchange in question. 495 * @param redeliveryPolicy The RedeliveryPolicy to use in the calculation. 496 * @param redeliveryDelay The default redelivery delay from RedeliveryData 497 * @param redeliveryCounter The redeliveryCounter 498 * @return The time to wait before the next redelivery. 499 */ 500 protected long determineRedeliveryDelay(Exchange exchange, RedeliveryPolicy redeliveryPolicy, long redeliveryDelay, int redeliveryCounter) { 501 Message message = exchange.getIn(); 502 Long delay = message.getHeader(Exchange.REDELIVERY_DELAY, Long.class); 503 if (delay == null) { 504 delay = redeliveryPolicy.calculateRedeliveryDelay(redeliveryDelay, redeliveryCounter); 505 log.debug("Redelivery delay calculated as {}", delay); 506 } else { 507 log.debug("Redelivery delay is {} from Message Header [{}]", delay, Exchange.REDELIVERY_DELAY); 508 } 509 return delay; 510 } 511 512 /** 513 * This logic is only executed if we have to retry redelivery asynchronously, which have to be done from the callback. 514 * <p/> 515 * And therefore the logic is a bit different than the synchronous <tt>processErrorHandler</tt> method which can use 516 * a loop based redelivery technique. However this means that these two methods in overall have to be in <b>sync</b> 517 * in terms of logic. 518 */ 519 protected void processAsyncErrorHandler(final Exchange exchange, final AsyncCallback callback, final RedeliveryData data) { 520 // can we still run 521 if (!isRunAllowed(data)) { 522 log.trace("Run not allowed, will reject executing exchange: {}", exchange); 523 if (exchange.getException() == null) { 524 exchange.setException(new RejectedExecutionException()); 525 } 526 callback.done(data.sync); 527 return; 528 } 529 530 // did previous processing cause an exception? 531 boolean handle = shouldHandleException(exchange); 532 if (handle) { 533 handleException(exchange, data); 534 } 535 536 // compute if we are exhausted or not 537 boolean exhausted = isExhausted(exchange, data); 538 if (exhausted) { 539 Processor target = null; 540 boolean deliver = true; 541 542 // the unit of work may have an optional callback associated we need to leverage 543 UnitOfWork uow = exchange.getUnitOfWork(); 544 if (uow != null) { 545 SubUnitOfWorkCallback uowCallback = uow.getSubUnitOfWorkCallback(); 546 if (uowCallback != null) { 547 // signal to the callback we are exhausted 548 uowCallback.onExhausted(exchange); 549 // do not deliver to the failure processor as its been handled by the callback instead 550 deliver = false; 551 } 552 } 553 554 if (deliver) { 555 // should deliver to failure processor (either from onException or the dead letter channel) 556 target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; 557 } 558 // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a fair 559 // bit of work for exhausted exchanges (its only the target processor which may be null if handled by a savepoint) 560 boolean isDeadLetterChannel = isDeadLetterChannel() && target == data.deadLetterProcessor; 561 deliverToFailureProcessor(target, isDeadLetterChannel, exchange, data, callback); 562 // we are breaking out 563 return; 564 } 565 566 if (data.redeliveryCounter > 0) { 567 // we are doing a redelivery then a thread pool must be configured (see the doStart method) 568 ObjectHelper.notNull(executorService, "Redelivery is enabled but ExecutorService has not been configured.", this); 569 570 // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can used a scheduler to 571 // have it being executed in the future, or immediately 572 // Note: the data.redeliverFromSync should be kept as is, in case it was enabled previously 573 // to ensure the callback will continue routing from where we left 574 AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); 575 576 // calculate the redelivery delay 577 data.redeliveryDelay = determineRedeliveryDelay(exchange, data.currentRedeliveryPolicy, data.redeliveryDelay, data.redeliveryCounter); 578 579 if (data.redeliveryDelay > 0) { 580 // schedule the redelivery task 581 if (log.isTraceEnabled()) { 582 log.trace("Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); 583 } 584 executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); 585 } else { 586 // execute the task immediately 587 executorService.submit(task); 588 } 589 } 590 } 591 592 /** 593 * Performs a defensive copy of the exchange if needed 594 * 595 * @param exchange the exchange 596 * @return the defensive copy, or <tt>null</tt> if not needed (redelivery is not enabled). 597 */ 598 protected Exchange defensiveCopyExchangeIfNeeded(Exchange exchange) { 599 // only do a defensive copy if redelivery is enabled 600 if (redeliveryEnabled) { 601 return ExchangeHelper.createCopy(exchange, true); 602 } else { 603 return null; 604 } 605 } 606 607 /** 608 * Strategy whether the exchange has an exception that we should try to handle. 609 * <p/> 610 * Standard implementations should just look for an exception. 611 */ 612 protected boolean shouldHandleException(Exchange exchange) { 613 return exchange.getException() != null; 614 } 615 616 /** 617 * Strategy to determine if the exchange is done so we can continue 618 */ 619 protected boolean isDone(Exchange exchange) { 620 boolean answer = isCancelledOrInterrupted(exchange); 621 622 // only done if the exchange hasn't failed 623 // and it has not been handled by the failure processor 624 // or we are exhausted 625 if (!answer) { 626 answer = exchange.getException() == null 627 || ExchangeHelper.isFailureHandled(exchange) 628 || ExchangeHelper.isRedeliveryExhausted(exchange); 629 } 630 631 log.trace("Is exchangeId: {} done? {}", exchange.getExchangeId(), answer); 632 return answer; 633 } 634 635 /** 636 * Strategy to determine if the exchange was cancelled or interrupted 637 */ 638 protected boolean isCancelledOrInterrupted(Exchange exchange) { 639 boolean answer = false; 640 641 if (ExchangeHelper.isInterrupted(exchange)) { 642 // mark the exchange to stop continue routing when interrupted 643 // as we do not want to continue routing (for example a task has been cancelled) 644 exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); 645 answer = true; 646 } 647 648 log.trace("Is exchangeId: {} interrupted? {}", exchange.getExchangeId(), answer); 649 return answer; 650 } 651 652 /** 653 * Returns the output processor 654 */ 655 public Processor getOutput() { 656 return output; 657 } 658 659 /** 660 * Returns the dead letter that message exchanges will be sent to if the 661 * redelivery attempts fail 662 */ 663 public Processor getDeadLetter() { 664 return deadLetter; 665 } 666 667 public String getDeadLetterUri() { 668 return deadLetterUri; 669 } 670 671 public boolean isUseOriginalMessagePolicy() { 672 return useOriginalMessagePolicy; 673 } 674 675 public boolean isDeadLetterHandleNewException() { 676 return deadLetterHandleNewException; 677 } 678 679 public RedeliveryPolicy getRedeliveryPolicy() { 680 return redeliveryPolicy; 681 } 682 683 public CamelLogger getLogger() { 684 return logger; 685 } 686 687 protected Predicate getDefaultHandledPredicate() { 688 // Default is not not handle errors 689 return null; 690 } 691 692 protected void prepareExchangeForContinue(Exchange exchange, RedeliveryData data) { 693 Exception caught = exchange.getException(); 694 695 // we continue so clear any exceptions 696 exchange.setException(null); 697 // clear rollback flags 698 exchange.setProperty(Exchange.ROLLBACK_ONLY, null); 699 // reset cached streams so they can be read again 700 MessageHelper.resetStreamCache(exchange.getIn()); 701 702 // its continued then remove traces of redelivery attempted and caught exception 703 exchange.getIn().removeHeader(Exchange.REDELIVERED); 704 exchange.getIn().removeHeader(Exchange.REDELIVERY_COUNTER); 705 exchange.getIn().removeHeader(Exchange.REDELIVERY_MAX_COUNTER); 706 exchange.removeProperty(Exchange.FAILURE_HANDLED); 707 // keep the Exchange.EXCEPTION_CAUGHT as property so end user knows the caused exception 708 709 // create log message 710 String msg = "Failed delivery for " + ExchangeHelper.logIds(exchange); 711 msg = msg + ". Exhausted after delivery attempt: " + data.redeliveryCounter + " caught: " + caught; 712 msg = msg + ". Handled and continue routing."; 713 714 // log that we failed but want to continue 715 logFailedDelivery(false, false, false, true, exchange, msg, data, null); 716 } 717 718 protected void prepareExchangeForRedelivery(Exchange exchange, RedeliveryData data) { 719 if (!redeliveryEnabled) { 720 throw new IllegalStateException("Redelivery is not enabled on " + this + ". Make sure you have configured the error handler properly."); 721 } 722 // there must be a defensive copy of the exchange 723 ObjectHelper.notNull(data.original, "Defensive copy of Exchange is null", this); 724 725 // okay we will give it another go so clear the exception so we can try again 726 exchange.setException(null); 727 728 // clear rollback flags 729 exchange.setProperty(Exchange.ROLLBACK_ONLY, null); 730 731 // TODO: We may want to store these as state on RedeliveryData so we keep them in case end user messes with Exchange 732 // and then put these on the exchange when doing a redelivery / fault processor 733 734 // preserve these headers 735 Integer redeliveryCounter = exchange.getIn().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 736 Integer redeliveryMaxCounter = exchange.getIn().getHeader(Exchange.REDELIVERY_MAX_COUNTER, Integer.class); 737 Boolean redelivered = exchange.getIn().getHeader(Exchange.REDELIVERED, Boolean.class); 738 739 // we are redelivering so copy from original back to exchange 740 exchange.getIn().copyFrom(data.original.getIn()); 741 exchange.setOut(null); 742 // reset cached streams so they can be read again 743 MessageHelper.resetStreamCache(exchange.getIn()); 744 745 // put back headers 746 if (redeliveryCounter != null) { 747 exchange.getIn().setHeader(Exchange.REDELIVERY_COUNTER, redeliveryCounter); 748 } 749 if (redeliveryMaxCounter != null) { 750 exchange.getIn().setHeader(Exchange.REDELIVERY_MAX_COUNTER, redeliveryMaxCounter); 751 } 752 if (redelivered != null) { 753 exchange.getIn().setHeader(Exchange.REDELIVERED, redelivered); 754 } 755 } 756 757 protected void handleException(Exchange exchange, RedeliveryData data) { 758 Exception e = exchange.getException(); 759 760 // store the original caused exception in a property, so we can restore it later 761 exchange.setProperty(Exchange.EXCEPTION_CAUGHT, e); 762 763 // find the error handler to use (if any) 764 OnExceptionDefinition exceptionPolicy = getExceptionPolicy(exchange, e); 765 if (exceptionPolicy != null) { 766 data.currentRedeliveryPolicy = exceptionPolicy.createRedeliveryPolicy(exchange.getContext(), data.currentRedeliveryPolicy); 767 data.handledPredicate = exceptionPolicy.getHandledPolicy(); 768 data.continuedPredicate = exceptionPolicy.getContinuedPolicy(); 769 data.retryWhilePredicate = exceptionPolicy.getRetryWhilePolicy(); 770 data.useOriginalInMessage = exceptionPolicy.getUseOriginalMessagePolicy() != null && exceptionPolicy.getUseOriginalMessagePolicy(); 771 772 // route specific failure handler? 773 Processor processor = null; 774 UnitOfWork uow = exchange.getUnitOfWork(); 775 if (uow != null && uow.getRouteContext() != null) { 776 String routeId = uow.getRouteContext().getRoute().getId(); 777 processor = exceptionPolicy.getErrorHandler(routeId); 778 } else if (!exceptionPolicy.getErrorHandlers().isEmpty()) { 779 // note this should really not happen, but we have this code as a fail safe 780 // to be backwards compatible with the old behavior 781 log.warn("Cannot determine current route from Exchange with id: {}, will fallback and use first error handler.", exchange.getExchangeId()); 782 processor = exceptionPolicy.getErrorHandlers().iterator().next(); 783 } 784 if (processor != null) { 785 data.failureProcessor = processor; 786 } 787 788 // route specific on redelivery? 789 processor = exceptionPolicy.getOnRedelivery(); 790 if (processor != null) { 791 data.onRedeliveryProcessor = processor; 792 } 793 } 794 795 // only log if not failure handled or not an exhausted unit of work 796 if (!ExchangeHelper.isFailureHandled(exchange) && !ExchangeHelper.isUnitOfWorkExhausted(exchange)) { 797 String msg = "Failed delivery for " + ExchangeHelper.logIds(exchange) 798 + ". On delivery attempt: " + data.redeliveryCounter + " caught: " + e; 799 logFailedDelivery(true, false, false, false, exchange, msg, data, e); 800 } 801 802 data.redeliveryCounter = incrementRedeliveryCounter(exchange, e, data); 803 } 804 805 /** 806 * Gives an optional configure redelivery processor a chance to process before the Exchange 807 * will be redelivered. This can be used to alter the Exchange. 808 */ 809 protected void deliverToOnRedeliveryProcessor(final Exchange exchange, final RedeliveryData data) { 810 if (data.onRedeliveryProcessor == null) { 811 return; 812 } 813 814 if (log.isTraceEnabled()) { 815 log.trace("Redelivery processor {} is processing Exchange: {} before its redelivered", 816 data.onRedeliveryProcessor, exchange); 817 } 818 819 // run this synchronously as its just a Processor 820 try { 821 data.onRedeliveryProcessor.process(exchange); 822 } catch (Throwable e) { 823 exchange.setException(e); 824 } 825 log.trace("Redelivery processor done"); 826 } 827 828 /** 829 * All redelivery attempts failed so move the exchange to the dead letter queue 830 */ 831 protected boolean deliverToFailureProcessor(final Processor processor, final boolean isDeadLetterChannel, final Exchange exchange, 832 final RedeliveryData data, final AsyncCallback callback) { 833 boolean sync = true; 834 835 Exception caught = exchange.getException(); 836 837 // we did not success with the redelivery so now we let the failure processor handle it 838 // clear exception as we let the failure processor handle it 839 exchange.setException(null); 840 841 final boolean shouldHandle = shouldHandle(exchange, data); 842 final boolean shouldContinue = shouldContinue(exchange, data); 843 844 // regard both handled or continued as being handled 845 boolean handled = false; 846 847 // always handle if dead letter channel 848 boolean handleOrContinue = isDeadLetterChannel || shouldHandle || shouldContinue; 849 if (handleOrContinue) { 850 // its handled then remove traces of redelivery attempted 851 exchange.getIn().removeHeader(Exchange.REDELIVERED); 852 exchange.getIn().removeHeader(Exchange.REDELIVERY_COUNTER); 853 exchange.getIn().removeHeader(Exchange.REDELIVERY_MAX_COUNTER); 854 exchange.removeProperty(Exchange.REDELIVERY_EXHAUSTED); 855 856 // and remove traces of rollback only and uow exhausted markers 857 exchange.removeProperty(Exchange.ROLLBACK_ONLY); 858 exchange.removeProperty(Exchange.UNIT_OF_WORK_EXHAUSTED); 859 860 handled = true; 861 } else { 862 // must decrement the redelivery counter as we didn't process the redelivery but is 863 // handling by the failure handler. So we must -1 to not let the counter be out-of-sync 864 decrementRedeliveryCounter(exchange); 865 } 866 867 // is the a failure processor to process the Exchange 868 if (processor != null) { 869 870 // prepare original IN body if it should be moved instead of current body 871 if (data.useOriginalInMessage) { 872 log.trace("Using the original IN message instead of current"); 873 Message original = exchange.getUnitOfWork().getOriginalInMessage(); 874 exchange.setIn(original); 875 if (exchange.hasOut()) { 876 log.trace("Removing the out message to avoid some uncertain behavior"); 877 exchange.setOut(null); 878 } 879 } 880 881 // reset cached streams so they can be read again 882 MessageHelper.resetStreamCache(exchange.getIn()); 883 884 log.trace("Failure processor {} is processing Exchange: {}", processor, exchange); 885 886 // store the last to endpoint as the failure endpoint 887 exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); 888 // and store the route id so we know in which route we failed 889 UnitOfWork uow = exchange.getUnitOfWork(); 890 if (uow != null && uow.getRouteContext() != null) { 891 exchange.setProperty(Exchange.FAILURE_ROUTE_ID, uow.getRouteContext().getRoute().getId()); 892 } 893 894 // the failure processor could also be asynchronous 895 AsyncProcessor afp = AsyncProcessorConverterHelper.convert(processor); 896 sync = afp.process(exchange, new AsyncCallback() { 897 public void done(boolean sync) { 898 log.trace("Failure processor done: {} processing Exchange: {}", processor, exchange); 899 try { 900 prepareExchangeAfterFailure(exchange, data, isDeadLetterChannel, shouldHandle, shouldContinue); 901 // fire event as we had a failure processor to handle it, which there is a event for 902 boolean deadLetterChannel = processor == data.deadLetterProcessor; 903 EventHelper.notifyExchangeFailureHandled(exchange.getContext(), exchange, processor, deadLetterChannel, deadLetterUri); 904 } finally { 905 // if the fault was handled asynchronously, this should be reflected in the callback as well 906 data.sync &= sync; 907 callback.done(data.sync); 908 } 909 } 910 }); 911 } else { 912 try { 913 // no processor but we need to prepare after failure as well 914 prepareExchangeAfterFailure(exchange, data, isDeadLetterChannel, shouldHandle, shouldContinue); 915 } finally { 916 // callback we are done 917 callback.done(data.sync); 918 } 919 } 920 921 // create log message 922 String msg = "Failed delivery for " + ExchangeHelper.logIds(exchange); 923 msg = msg + ". Exhausted after delivery attempt: " + data.redeliveryCounter + " caught: " + caught; 924 if (processor != null) { 925 msg = msg + ". Processed by failure processor: " + processor; 926 } 927 928 // log that we failed delivery as we are exhausted 929 logFailedDelivery(false, false, handled, false, exchange, msg, data, null); 930 931 return sync; 932 } 933 934 protected void prepareExchangeAfterFailure(final Exchange exchange, final RedeliveryData data, final boolean isDeadLetterChannel, 935 final boolean shouldHandle, final boolean shouldContinue) { 936 937 Exception newException = exchange.getException(); 938 939 // we could not process the exchange so we let the failure processor handled it 940 ExchangeHelper.setFailureHandled(exchange); 941 942 // honor if already set a handling 943 boolean alreadySet = exchange.getProperty(Exchange.ERRORHANDLER_HANDLED) != null; 944 if (alreadySet) { 945 boolean handled = exchange.getProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.class); 946 log.trace("This exchange has already been marked for handling: {}", handled); 947 if (!handled) { 948 // exception not handled, put exception back in the exchange 949 exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)); 950 // and put failure endpoint back as well 951 exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); 952 } 953 return; 954 } 955 956 // dead letter channel is special 957 if (shouldContinue) { 958 log.trace("This exchange is continued: {}", exchange); 959 // okay we want to continue then prepare the exchange for that as well 960 prepareExchangeForContinue(exchange, data); 961 } else if (shouldHandle) { 962 log.trace("This exchange is handled so its marked as not failed: {}", exchange); 963 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.TRUE); 964 } else { 965 // okay the redelivery policy are not explicit set to true, so we should allow to check for some 966 // special situations when using dead letter channel 967 if (isDeadLetterChannel) { 968 969 // use the handled option from the DLC 970 boolean handled = data.handleNewException; 971 972 // when using DLC then log new exception whether its being handled or not, as otherwise it may appear as 973 // the DLC swallow new exceptions by default (which is by design to ensure the DLC always complete, 974 // to avoid causing endless poison messages that fails forever) 975 if (newException != null && data.currentRedeliveryPolicy.isLogNewException()) { 976 String uri = URISupport.sanitizeUri(deadLetterUri); 977 String msg = "New exception occurred during processing by the DeadLetterChannel[" + uri + "] due " + newException.getMessage(); 978 if (handled) { 979 msg += ". The new exception is being handled as deadLetterHandleNewException=true."; 980 } else { 981 msg += ". The new exception is not handled as deadLetterHandleNewException=false."; 982 } 983 logFailedDelivery(false, true, handled, false, exchange, msg, data, newException); 984 } 985 986 if (handled) { 987 log.trace("This exchange is handled so its marked as not failed: {}", exchange); 988 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.TRUE); 989 return; 990 } 991 } 992 993 // not handled by default 994 prepareExchangeAfterFailureNotHandled(exchange); 995 } 996 } 997 998 private void prepareExchangeAfterFailureNotHandled(Exchange exchange) { 999 log.trace("This exchange is not handled or continued so its marked as failed: {}", exchange); 1000 // exception not handled, put exception back in the exchange 1001 exchange.setProperty(Exchange.ERRORHANDLER_HANDLED, Boolean.FALSE); 1002 exchange.setException(exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)); 1003 // and put failure endpoint back as well 1004 exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); 1005 // and store the route id so we know in which route we failed 1006 UnitOfWork uow = exchange.getUnitOfWork(); 1007 if (uow != null && uow.getRouteContext() != null) { 1008 exchange.setProperty(Exchange.FAILURE_ROUTE_ID, uow.getRouteContext().getRoute().getId()); 1009 } 1010 } 1011 1012 private void logFailedDelivery(boolean shouldRedeliver, boolean newException, boolean handled, boolean continued, 1013 Exchange exchange, String message, RedeliveryData data, Throwable e) { 1014 if (logger == null) { 1015 return; 1016 } 1017 1018 if (!exchange.isRollbackOnly()) { 1019 if (newException && !data.currentRedeliveryPolicy.isLogNewException()) { 1020 // do not log new exception 1021 return; 1022 } 1023 1024 // if we should not rollback, then check whether logging is enabled 1025 if (!newException && handled && !data.currentRedeliveryPolicy.isLogHandled()) { 1026 // do not log handled 1027 return; 1028 } 1029 1030 if (!newException && continued && !data.currentRedeliveryPolicy.isLogContinued()) { 1031 // do not log handled 1032 return; 1033 } 1034 1035 if (!newException && shouldRedeliver && !data.currentRedeliveryPolicy.isLogRetryAttempted()) { 1036 // do not log retry attempts 1037 return; 1038 } 1039 1040 if (!newException && !shouldRedeliver && !data.currentRedeliveryPolicy.isLogExhausted()) { 1041 // do not log exhausted 1042 return; 1043 } 1044 } 1045 1046 LoggingLevel newLogLevel; 1047 boolean logStackTrace; 1048 if (exchange.isRollbackOnly()) { 1049 newLogLevel = data.currentRedeliveryPolicy.getRetriesExhaustedLogLevel(); 1050 logStackTrace = data.currentRedeliveryPolicy.isLogStackTrace(); 1051 } else if (shouldRedeliver) { 1052 newLogLevel = data.currentRedeliveryPolicy.getRetryAttemptedLogLevel(); 1053 logStackTrace = data.currentRedeliveryPolicy.isLogRetryStackTrace(); 1054 } else { 1055 newLogLevel = data.currentRedeliveryPolicy.getRetriesExhaustedLogLevel(); 1056 logStackTrace = data.currentRedeliveryPolicy.isLogStackTrace(); 1057 } 1058 if (e == null) { 1059 e = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); 1060 } 1061 1062 if (newException) { 1063 // log at most WARN level 1064 if (newLogLevel == LoggingLevel.ERROR) { 1065 newLogLevel = LoggingLevel.WARN; 1066 } 1067 String msg = message; 1068 if (msg == null) { 1069 msg = "New exception " + ExchangeHelper.logIds(exchange); 1070 // special for logging the new exception 1071 Throwable cause = e; 1072 if (cause != null) { 1073 msg = msg + " due: " + cause.getMessage(); 1074 } 1075 } 1076 1077 if (e != null && logStackTrace) { 1078 logger.log(msg, e, newLogLevel); 1079 } else { 1080 logger.log(msg, newLogLevel); 1081 } 1082 } else if (exchange.isRollbackOnly()) { 1083 String msg = "Rollback " + ExchangeHelper.logIds(exchange); 1084 Throwable cause = exchange.getException() != null ? exchange.getException() : exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class); 1085 if (cause != null) { 1086 msg = msg + " due: " + cause.getMessage(); 1087 } 1088 1089 // should we include message history 1090 if (!shouldRedeliver && data.currentRedeliveryPolicy.isLogExhaustedMessageHistory()) { 1091 String routeStackTrace = MessageHelper.dumpMessageHistoryStacktrace(exchange, exchangeFormatter, false); 1092 if (routeStackTrace != null) { 1093 msg = msg + "\n" + routeStackTrace; 1094 } 1095 } 1096 1097 if (newLogLevel == LoggingLevel.ERROR) { 1098 // log intended rollback on maximum WARN level (no ERROR) 1099 logger.log(msg, LoggingLevel.WARN); 1100 } else { 1101 // otherwise use the desired logging level 1102 logger.log(msg, newLogLevel); 1103 } 1104 } else { 1105 String msg = message; 1106 // should we include message history 1107 if (!shouldRedeliver && data.currentRedeliveryPolicy.isLogExhaustedMessageHistory()) { 1108 String routeStackTrace = MessageHelper.dumpMessageHistoryStacktrace(exchange, exchangeFormatter, e != null && logStackTrace); 1109 if (routeStackTrace != null) { 1110 msg = msg + "\n" + routeStackTrace; 1111 } 1112 } 1113 1114 if (e != null && logStackTrace) { 1115 logger.log(msg, e, newLogLevel); 1116 } else { 1117 logger.log(msg, newLogLevel); 1118 } 1119 } 1120 } 1121 1122 /** 1123 * Determines whether the exchange is exhausted (or anyway marked to not continue such as rollback). 1124 * <p/> 1125 * If the exchange is exhausted, then we will not continue processing, but let the 1126 * failure processor deal with the exchange. 1127 * 1128 * @param exchange the current exchange 1129 * @param data the redelivery data 1130 * @return <tt>false</tt> to continue/redeliver, or <tt>true</tt> to exhaust. 1131 */ 1132 private boolean isExhausted(Exchange exchange, RedeliveryData data) { 1133 // if marked as rollback only then do not continue/redeliver 1134 boolean exhausted = exchange.getProperty(Exchange.REDELIVERY_EXHAUSTED, false, Boolean.class); 1135 if (exhausted) { 1136 log.trace("This exchange is marked as redelivery exhausted: {}", exchange); 1137 return true; 1138 } 1139 1140 // if marked as rollback only then do not continue/redeliver 1141 boolean rollbackOnly = exchange.getProperty(Exchange.ROLLBACK_ONLY, false, Boolean.class); 1142 if (rollbackOnly) { 1143 log.trace("This exchange is marked as rollback only, so forcing it to be exhausted: {}", exchange); 1144 return true; 1145 } 1146 // its the first original call so continue 1147 if (data.redeliveryCounter == 0) { 1148 return false; 1149 } 1150 // its a potential redelivery so determine if we should redeliver or not 1151 boolean redeliver = data.currentRedeliveryPolicy.shouldRedeliver(exchange, data.redeliveryCounter, data.retryWhilePredicate); 1152 return !redeliver; 1153 } 1154 1155 /** 1156 * Determines whether or not to continue if we are exhausted. 1157 * 1158 * @param exchange the current exchange 1159 * @param data the redelivery data 1160 * @return <tt>true</tt> to continue, or <tt>false</tt> to exhaust. 1161 */ 1162 private boolean shouldContinue(Exchange exchange, RedeliveryData data) { 1163 if (data.continuedPredicate != null) { 1164 return data.continuedPredicate.matches(exchange); 1165 } 1166 // do not continue by default 1167 return false; 1168 } 1169 1170 /** 1171 * Determines whether or not to handle if we are exhausted. 1172 * 1173 * @param exchange the current exchange 1174 * @param data the redelivery data 1175 * @return <tt>true</tt> to handle, or <tt>false</tt> to exhaust. 1176 */ 1177 private boolean shouldHandle(Exchange exchange, RedeliveryData data) { 1178 if (data.handledPredicate != null) { 1179 return data.handledPredicate.matches(exchange); 1180 } 1181 // do not handle by default 1182 return false; 1183 } 1184 1185 /** 1186 * Increments the redelivery counter and adds the redelivered flag if the 1187 * message has been redelivered 1188 */ 1189 private int incrementRedeliveryCounter(Exchange exchange, Throwable e, RedeliveryData data) { 1190 Message in = exchange.getIn(); 1191 Integer counter = in.getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 1192 int next = 1; 1193 if (counter != null) { 1194 next = counter + 1; 1195 } 1196 in.setHeader(Exchange.REDELIVERY_COUNTER, next); 1197 in.setHeader(Exchange.REDELIVERED, Boolean.TRUE); 1198 // if maximum redeliveries is used, then provide that information as well 1199 if (data.currentRedeliveryPolicy.getMaximumRedeliveries() > 0) { 1200 in.setHeader(Exchange.REDELIVERY_MAX_COUNTER, data.currentRedeliveryPolicy.getMaximumRedeliveries()); 1201 } 1202 return next; 1203 } 1204 1205 /** 1206 * Prepares the redelivery counter and boolean flag for the failure handle processor 1207 */ 1208 private void decrementRedeliveryCounter(Exchange exchange) { 1209 Message in = exchange.getIn(); 1210 Integer counter = in.getHeader(Exchange.REDELIVERY_COUNTER, Integer.class); 1211 if (counter != null) { 1212 int prev = counter - 1; 1213 in.setHeader(Exchange.REDELIVERY_COUNTER, prev); 1214 // set boolean flag according to counter 1215 in.setHeader(Exchange.REDELIVERED, prev > 0 ? Boolean.TRUE : Boolean.FALSE); 1216 } else { 1217 // not redelivered 1218 in.setHeader(Exchange.REDELIVERY_COUNTER, 0); 1219 in.setHeader(Exchange.REDELIVERED, Boolean.FALSE); 1220 } 1221 } 1222 1223 /** 1224 * Determines if redelivery is enabled by checking if any of the redelivery policy 1225 * settings may allow redeliveries. 1226 * 1227 * @return <tt>true</tt> if redelivery is possible, <tt>false</tt> otherwise 1228 * @throws Exception can be thrown 1229 */ 1230 private boolean determineIfRedeliveryIsEnabled() throws Exception { 1231 // determine if redeliver is enabled either on error handler 1232 if (getRedeliveryPolicy().getMaximumRedeliveries() != 0) { 1233 // must check for != 0 as (-1 means redeliver forever) 1234 return true; 1235 } 1236 if (retryWhilePolicy != null) { 1237 return true; 1238 } 1239 1240 // or on the exception policies 1241 if (!exceptionPolicies.isEmpty()) { 1242 // walk them to see if any of them have a maximum redeliveries > 0 or retry until set 1243 for (OnExceptionDefinition def : exceptionPolicies.values()) { 1244 1245 String ref = def.getRedeliveryPolicyRef(); 1246 if (ref != null) { 1247 // lookup in registry if ref provided 1248 RedeliveryPolicy policy = CamelContextHelper.mandatoryLookup(camelContext, ref, RedeliveryPolicy.class); 1249 if (policy.getMaximumRedeliveries() != 0) { 1250 // must check for != 0 as (-1 means redeliver forever) 1251 return true; 1252 } 1253 } else if (def.getRedeliveryPolicy() != null) { 1254 Integer max = CamelContextHelper.parseInteger(camelContext, def.getRedeliveryPolicy().getMaximumRedeliveries()); 1255 if (max != null && max != 0) { 1256 // must check for != 0 as (-1 means redeliver forever) 1257 return true; 1258 } 1259 } 1260 1261 if (def.getRetryWhilePolicy() != null || def.getRetryWhile() != null) { 1262 return true; 1263 } 1264 } 1265 } 1266 1267 return false; 1268 } 1269 1270 @Override 1271 protected void doStart() throws Exception { 1272 ServiceHelper.startServices(output, outputAsync, deadLetter); 1273 1274 // determine if redeliver is enabled or not 1275 redeliveryEnabled = determineIfRedeliveryIsEnabled(); 1276 if (log.isDebugEnabled()) { 1277 log.debug("Redelivery enabled: {} on error handler: {}", redeliveryEnabled, this); 1278 } 1279 1280 // we only need thread pool if redelivery is enabled 1281 if (redeliveryEnabled) { 1282 if (executorService == null) { 1283 // use default shared executor service 1284 executorService = camelContext.getErrorHandlerExecutorService(); 1285 } 1286 if (log.isTraceEnabled()) { 1287 log.trace("Using ExecutorService: {} for redeliveries on error handler: {}", executorService, this); 1288 } 1289 } 1290 1291 // reset flag when starting 1292 preparingShutdown = false; 1293 } 1294 1295 @Override 1296 protected void doStop() throws Exception { 1297 // noop, do not stop any services which we only do when shutting down 1298 // as the error handler can be context scoped, and should not stop in case 1299 // a route stops 1300 } 1301 1302 @Override 1303 protected void doShutdown() throws Exception { 1304 ServiceHelper.stopAndShutdownServices(deadLetter, output, outputAsync); 1305 } 1306}