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