Search in sources :

Example 1 with SubUnitOfWorkCallback

use of org.apache.camel.spi.SubUnitOfWorkCallback in project camel by apache.

the class DefaultUnitOfWork method done.

public void done(Exchange exchange) {
    log.trace("UnitOfWork done for ExchangeId: {} with {}", exchange.getExchangeId(), exchange);
    boolean failed = exchange.isFailed();
    // at first done the synchronizations
    UnitOfWorkHelper.doneSynchronizations(exchange, synchronizations, log);
    // notify uow callback if in use
    try {
        SubUnitOfWorkCallback uowCallback = getSubUnitOfWorkCallback();
        if (uowCallback != null) {
            uowCallback.onDone(exchange);
        }
    } catch (Throwable e) {
        // must catch exceptions to ensure synchronizations is also invoked
        log.warn("Exception occurred during savepoint onDone. This exception will be ignored.", e);
    }
    // unregister from inflight registry, before signalling we are done
    if (exchange.getContext() != null) {
        exchange.getContext().getInflightRepository().remove(exchange);
    }
    // then fire event to signal the exchange is done
    try {
        if (failed) {
            EventHelper.notifyExchangeFailed(exchange.getContext(), exchange);
        } else {
            EventHelper.notifyExchangeDone(exchange.getContext(), exchange);
        }
    } catch (Throwable e) {
        // must catch exceptions to ensure synchronizations is also invoked
        log.warn("Exception occurred during event notification. This exception will be ignored.", e);
    }
}
Also used : SubUnitOfWorkCallback(org.apache.camel.spi.SubUnitOfWorkCallback)

Example 2 with SubUnitOfWorkCallback

use of org.apache.camel.spi.SubUnitOfWorkCallback in project camel by apache.

the class RedeliveryErrorHandler method processAsyncErrorHandler.

/**
     * 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(data)) {
        log.trace("Run not allowed, will reject executing exchange: {}", exchange);
        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, isDeadLetterChannel());
        onExceptionOccurred(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
        UnitOfWork uow = exchange.getUnitOfWork();
        if (uow != null) {
            SubUnitOfWorkCallback uowCallback = uow.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 isDeadLetterChannel = isDeadLetterChannel() && target == data.deadLetterProcessor;
        deliverToFailureProcessor(target, isDeadLetterChannel, exchange, data, callback);
        // we are breaking out
        return;
    }
    if (data.redeliveryCounter > 0) {
        // we are doing a redelivery then a thread pool must be configured (see the doStart method)
        ObjectHelper.notNull(executorService, "Redelivery is enabled but ExecutorService has not been configured.", this);
        // 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 = determineRedeliveryDelay(exchange, data.currentRedeliveryPolicy, 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);
        }
    }
}
Also used : UnitOfWork(org.apache.camel.spi.UnitOfWork) Processor(org.apache.camel.Processor) AsyncProcessor(org.apache.camel.AsyncProcessor) SubUnitOfWorkCallback(org.apache.camel.spi.SubUnitOfWorkCallback) RejectedExecutionException(java.util.concurrent.RejectedExecutionException)

Example 3 with SubUnitOfWorkCallback

use of org.apache.camel.spi.SubUnitOfWorkCallback in project camel by apache.

the class RedeliveryErrorHandler method process.

/**
     * Process the exchange using redelivery error handling.
     */
public boolean process(final Exchange exchange, final AsyncCallback callback) {
    final RedeliveryData data = new RedeliveryData();
    // 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(data)) {
            log.trace("Run not allowed, will reject executing exchange: {}", exchange);
            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, isDeadLetterChannel());
            onExceptionOccurred(exchange, data);
        }
        // compute if we are exhausted, and whether redelivery is allowed
        boolean exhausted = isExhausted(exchange, data);
        boolean redeliverAllowed = isRedeliveryAllowed(data);
        // if we are exhausted or redelivery is not allowed, then deliver to failure processor (eg such as DLC)
        if (!redeliverAllowed || 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 isDeadLetterChannel = isDeadLetterChannel() && (target == null || target == data.deadLetterProcessor);
            boolean sync = deliverToFailureProcessor(target, isDeadLetterChannel, exchange, data, callback);
            // we are breaking out
            return sync;
        }
        if (data.redeliveryCounter > 0) {
            // calculate delay
            data.redeliveryDelay = determineRedeliveryDelay(exchange, data.currentRedeliveryPolicy, data.redeliveryDelay, data.redeliveryCounter);
            if (data.redeliveryDelay > 0) {
                if (data.currentRedeliveryPolicy.isAsyncDelayedRedelivery() && !exchange.isTransacted()) {
                    // we are doing a redelivery then a thread pool must be configured (see the doStart method)
                    ObjectHelper.notNull(executorService, "Redelivery is enabled but ExecutorService has not been configured.", this);
                    // 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 {
                    // as the transaction manager requires to execute in the same thread context
                    try {
                        // we are doing synchronous redelivery and use thread sleep, so we keep track using a counter how many are sleeping
                        redeliverySleepCounter.incrementAndGet();
                        RedeliverSleepTask task = new RedeliverSleepTask(data.currentRedeliveryPolicy, data.redeliveryDelay);
                        boolean complete = task.sleep();
                        redeliverySleepCounter.decrementAndGet();
                        if (!complete) {
                            // the task was rejected
                            exchange.setException(new RejectedExecutionException("Redelivery not allowed while stopping"));
                            // mark the exchange as redelivery exhausted so the failure processor / dead letter channel can process the exchange
                            exchange.setProperty(Exchange.REDELIVERY_EXHAUSTED, Boolean.TRUE);
                            // jump to start of loop which then detects that we are failed and exhausted
                            continue;
                        }
                    } catch (InterruptedException e) {
                        redeliverySleepCounter.decrementAndGet();
                        // 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 = outputAsync.process(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.....
    }
}
Also used : Processor(org.apache.camel.Processor) AsyncProcessor(org.apache.camel.AsyncProcessor) AsyncCallback(org.apache.camel.AsyncCallback) SubUnitOfWorkCallback(org.apache.camel.spi.SubUnitOfWorkCallback) RejectedExecutionException(java.util.concurrent.RejectedExecutionException)

Aggregations

SubUnitOfWorkCallback (org.apache.camel.spi.SubUnitOfWorkCallback)3 RejectedExecutionException (java.util.concurrent.RejectedExecutionException)2 AsyncProcessor (org.apache.camel.AsyncProcessor)2 Processor (org.apache.camel.Processor)2 AsyncCallback (org.apache.camel.AsyncCallback)1 UnitOfWork (org.apache.camel.spi.UnitOfWork)1