/** * This logic is only executed if we have to retry redelivery asynchronously, which have to be * done from the callback. * * <p>And therefore the logic is a bit different than the synchronous <tt>processErrorHandler</tt> * method which can use a loop based redelivery technique. However this means that these two * methods in overall have to be in <b>sync</b> in terms of logic. */ protected void processAsyncErrorHandler( final Exchange exchange, final AsyncCallback callback, final RedeliveryData data) { // can we still run if (!isRunAllowed()) { if (exchange.getException() == null) { exchange.setException(new RejectedExecutionException()); } callback.done(data.sync); return; } // did previous processing cause an exception? boolean handle = shouldHandleException(exchange); if (handle) { handleException(exchange, data); } // compute if we are exhausted or not boolean exhausted = isExhausted(exchange, data); if (exhausted) { Processor target = null; boolean deliver = true; // the unit of work may have an optional callback associated we need to leverage SubUnitOfWorkCallback uowCallback = exchange.getUnitOfWork().getSubUnitOfWorkCallback(); if (uowCallback != null) { // signal to the callback we are exhausted uowCallback.onExhausted(exchange); // do not deliver to the failure processor as its been handled by the callback instead deliver = false; } if (deliver) { // should deliver to failure processor (either from onException or the dead letter channel) target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; } // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a fair // bit of work for exhausted exchanges (its only the target processor which may be null if // handled by a savepoint) deliverToFailureProcessor(target, exchange, data, callback); // we are breaking out return; } if (data.redeliveryCounter > 0) { // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can used // a scheduler to // have it being executed in the future, or immediately // Note: the data.redeliverFromSync should be kept as is, in case it was enabled previously // to ensure the callback will continue routing from where we left AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); // calculate the redelivery delay data.redeliveryDelay = data.currentRedeliveryPolicy.calculateRedeliveryDelay( data.redeliveryDelay, data.redeliveryCounter); if (data.redeliveryDelay > 0) { // schedule the redelivery task if (log.isTraceEnabled()) { log.trace( "Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); } executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); } else { // execute the task immediately executorService.submit(task); } } }
/** Process the exchange using redelivery error handling. */ protected boolean processErrorHandler( final Exchange exchange, final AsyncCallback callback, final RedeliveryData data) { // do a defensive copy of the original Exchange, which is needed for redelivery so we can ensure // the // original Exchange is being redelivered, and not a mutated Exchange data.original = defensiveCopyExchangeIfNeeded(exchange); // use looping to have redelivery attempts while (true) { // can we still run if (!isRunAllowed()) { if (exchange.getException() == null) { exchange.setException(new RejectedExecutionException()); } // we cannot process so invoke callback callback.done(data.sync); return data.sync; } // did previous processing cause an exception? boolean handle = shouldHandleException(exchange); if (handle) { handleException(exchange, data); } // compute if we are exhausted or not boolean exhausted = isExhausted(exchange, data); if (exhausted) { Processor target = null; boolean deliver = true; // the unit of work may have an optional callback associated we need to leverage SubUnitOfWorkCallback uowCallback = exchange.getUnitOfWork().getSubUnitOfWorkCallback(); if (uowCallback != null) { // signal to the callback we are exhausted uowCallback.onExhausted(exchange); // do not deliver to the failure processor as its been handled by the callback instead deliver = false; } if (deliver) { // should deliver to failure processor (either from onException or the dead letter // channel) target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; } // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a // fair // bit of work for exhausted exchanges (its only the target processor which may be null if // handled by a savepoint) boolean sync = deliverToFailureProcessor(target, exchange, data, callback); // we are breaking out return sync; } if (data.redeliveryCounter > 0) { // calculate delay data.redeliveryDelay = data.currentRedeliveryPolicy.calculateRedeliveryDelay( data.redeliveryDelay, data.redeliveryCounter); if (data.redeliveryDelay > 0) { // okay there is a delay so create a scheduled task to have it executed in the future if (data.currentRedeliveryPolicy.isAsyncDelayedRedelivery() && !exchange.isTransacted()) { // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can // used a scheduler to // have it being executed in the future, or immediately // we are continuing asynchronously // mark we are routing async from now and that this redelivery task came from a // synchronous routing data.sync = false; data.redeliverFromSync = true; AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); // schedule the redelivery task if (log.isTraceEnabled()) { log.trace( "Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); } executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); return false; } else { // async delayed redelivery was disabled or we are transacted so we must be synchronous // as the transaction manager requires to execute in the same thread context try { data.currentRedeliveryPolicy.sleep(data.redeliveryDelay); } catch (InterruptedException e) { // we was interrupted so break out exchange.setException(e); // mark the exchange to stop continue routing when interrupted // as we do not want to continue routing (for example a task has been cancelled) exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); callback.done(data.sync); return data.sync; } } } // prepare for redelivery prepareExchangeForRedelivery(exchange, data); // letting onRedeliver be executed deliverToOnRedeliveryProcessor(exchange, data); // emmit event we are doing redelivery EventHelper.notifyExchangeRedelivery( exchange.getContext(), exchange, data.redeliveryCounter); } // process the exchange (also redelivery) boolean sync = AsyncProcessorHelper.process( outputAsync, exchange, new AsyncCallback() { public void done(boolean sync) { // this callback should only handle the async case if (sync) { return; } // mark we are in async mode now data.sync = false; // if we are done then notify callback and exit if (isDone(exchange)) { callback.done(sync); return; } // error occurred so loop back around which we do by invoking the // processAsyncErrorHandler // method which takes care of this in a asynchronous manner processAsyncErrorHandler(exchange, callback, data); } }); if (!sync) { // the remainder of the Exchange is being processed asynchronously so we should return return false; } // we continue to route synchronously // if we are done then notify callback and exit boolean done = isDone(exchange); if (done) { callback.done(true); return true; } // error occurred so loop back around..... } }