use of org.apache.camel.AsyncCallback 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.....
}
}
use of org.apache.camel.AsyncCallback in project camel by apache.
the class TryProcessor method process.
protected boolean process(final Exchange exchange, final AsyncCallback callback, final Iterator<Processor> processors, final AsyncProcessor processor, final Object lastHandled) {
// this does the actual processing so log at trace level
LOG.trace("Processing exchangeId: {} >>> {}", exchange.getExchangeId(), exchange);
// implement asynchronous routing logic in callback so we can have the callback being
// triggered and then continue routing where we left
boolean sync = processor.process(exchange, new AsyncCallback() {
public void done(boolean doneSync) {
// we only have to handle async completion of the pipeline
if (doneSync) {
return;
}
// continue processing the try .. catch .. finally asynchronously
while (continueRouting(processors, exchange)) {
exchange.setProperty(Exchange.TRY_ROUTE_BLOCK, true);
ExchangeHelper.prepareOutToIn(exchange);
// process the next processor
AsyncProcessor processor = AsyncProcessorConverterHelper.convert(processors.next());
doneSync = process(exchange, callback, processors, processor, lastHandled);
if (!doneSync) {
LOG.trace("Processing exchangeId: {} is continued being processed asynchronously", exchange.getExchangeId());
// so we break out now, then the callback will be invoked which then continue routing from where we left here
return;
}
}
ExchangeHelper.prepareOutToIn(exchange);
exchange.removeProperty(Exchange.TRY_ROUTE_BLOCK);
exchange.setProperty(Exchange.EXCEPTION_HANDLED, lastHandled);
LOG.trace("Processing complete for exchangeId: {} >>> {}", exchange.getExchangeId(), exchange);
callback.done(false);
}
});
return sync;
}
use of org.apache.camel.AsyncCallback in project camel by apache.
the class RoutingSlip method processExchange.
protected boolean processExchange(final Endpoint endpoint, final Exchange exchange, final Exchange original, final AsyncCallback callback, final RoutingSlipIterator iter) {
// this does the actual processing so log at trace level
log.trace("Processing exchangeId: {} >>> {}", exchange.getExchangeId(), exchange);
boolean sync = producerCache.doInAsyncProducer(endpoint, exchange, null, callback, new AsyncProducerCallback() {
public boolean doInAsyncProducer(Producer producer, AsyncProcessor asyncProducer, final Exchange exchange, ExchangePattern exchangePattern, final AsyncCallback callback) {
// rework error handling to support fine grained error handling
RouteContext routeContext = exchange.getUnitOfWork() != null ? exchange.getUnitOfWork().getRouteContext() : null;
AsyncProcessor target = createErrorHandler(routeContext, exchange, asyncProducer, endpoint);
// set property which endpoint we send to
exchange.setProperty(Exchange.TO_ENDPOINT, endpoint.getEndpointUri());
exchange.setProperty(Exchange.SLIP_ENDPOINT, endpoint.getEndpointUri());
boolean answer = target.process(exchange, new AsyncCallback() {
public void done(boolean doneSync) {
// we only have to handle async completion of the routing slip
if (doneSync) {
callback.done(doneSync);
return;
}
// continue processing the routing slip asynchronously
Exchange current = prepareExchangeForRoutingSlip(exchange, endpoint);
while (iter.hasNext(current)) {
// we ignore some kind of exceptions and allow us to continue
if (isIgnoreInvalidEndpoints()) {
FailedToCreateProducerException e = current.getException(FailedToCreateProducerException.class);
if (e != null) {
if (log.isDebugEnabled()) {
log.debug("Endpoint uri is invalid: " + endpoint + ". This exception will be ignored.", e);
}
current.setException(null);
}
}
// check for error if so we should break out
if (!continueProcessing(current, "so breaking out of the routing slip", log)) {
break;
}
Endpoint endpoint;
try {
endpoint = resolveEndpoint(iter, exchange);
// if no endpoint was resolved then try the next
if (endpoint == null) {
continue;
}
} catch (Exception e) {
// error resolving endpoint so we should break out
exchange.setException(e);
break;
}
// prepare and process the routing slip
boolean sync = processExchange(endpoint, current, original, callback, iter);
current = prepareExchangeForRoutingSlip(current, endpoint);
if (!sync) {
log.trace("Processing exchangeId: {} is continued being processed asynchronously", original.getExchangeId());
return;
}
}
// logging nextExchange as it contains the exchange that might have altered the payload and since
// we are logging the completion if will be confusing if we log the original instead
// we could also consider logging the original and the nextExchange then we have *before* and *after* snapshots
log.trace("Processing complete for exchangeId: {} >>> {}", original.getExchangeId(), current);
// copy results back to the original exchange
ExchangeHelper.copyResults(original, current);
if (target instanceof DeadLetterChannel) {
Processor deadLetter = ((DeadLetterChannel) target).getDeadLetter();
try {
ServiceHelper.stopService(deadLetter);
} catch (Exception e) {
log.warn("Error stopping DeadLetterChannel error handler on routing slip. This exception is ignored.", e);
}
}
callback.done(false);
}
});
// stop error handler if we completed synchronously
if (answer) {
if (target instanceof DeadLetterChannel) {
Processor deadLetter = ((DeadLetterChannel) target).getDeadLetter();
try {
ServiceHelper.stopService(deadLetter);
} catch (Exception e) {
log.warn("Error stopping DeadLetterChannel error handler on routing slip. This exception is ignored.", e);
}
}
}
return answer;
}
});
return sync;
}
use of org.apache.camel.AsyncCallback in project camel by apache.
the class SendProcessor method process.
public boolean process(Exchange exchange, final AsyncCallback callback) {
if (!isStarted()) {
exchange.setException(new IllegalStateException("SendProcessor has not been started: " + this));
callback.done(true);
return true;
}
// we should preserve existing MEP so remember old MEP
// if you want to permanently to change the MEP then use .setExchangePattern in the DSL
final ExchangePattern existingPattern = exchange.getPattern();
counter++;
// if we have a producer then use that as its optimized
if (producer != null) {
// record timing for sending the exchange using the producer
final StopWatch watch = new StopWatch();
final Exchange target = configureExchange(exchange, pattern);
EventHelper.notifyExchangeSending(exchange.getContext(), target, destination);
LOG.debug(">>>> {} {}", destination, exchange);
boolean sync = true;
try {
sync = producer.process(exchange, new AsyncCallback() {
@Override
public void done(boolean doneSync) {
try {
// restore previous MEP
target.setPattern(existingPattern);
// emit event that the exchange was sent to the endpoint
long timeTaken = watch.stop();
EventHelper.notifyExchangeSent(target.getContext(), target, destination, timeTaken);
} finally {
callback.done(doneSync);
}
}
});
} catch (Throwable throwable) {
exchange.setException(throwable);
callback.done(sync);
}
return sync;
}
// send the exchange to the destination using the producer cache for the non optimized producers
return producerCache.doInAsyncProducer(destination, exchange, pattern, callback, new AsyncProducerCallback() {
public boolean doInAsyncProducer(Producer producer, AsyncProcessor asyncProducer, final Exchange exchange, ExchangePattern pattern, final AsyncCallback callback) {
final Exchange target = configureExchange(exchange, pattern);
LOG.debug(">>>> {} {}", destination, exchange);
return asyncProducer.process(target, new AsyncCallback() {
public void done(boolean doneSync) {
// restore previous MEP
target.setPattern(existingPattern);
// signal we are done
callback.done(doneSync);
}
});
}
});
}
use of org.apache.camel.AsyncCallback in project camel by apache.
the class TraceInterceptor method process.
@Override
public boolean process(final Exchange exchange, final AsyncCallback callback) {
// do not trace if tracing is disabled
if (!tracer.isEnabled() || (routeContext != null && !routeContext.isTracing())) {
return processor.process(exchange, callback);
}
// logging TraceEvents to avoid infinite looping
if (exchange.getProperty(Exchange.TRACE_EVENT, false, Boolean.class)) {
// but we must still process to allow routing of TraceEvents to eg a JPA endpoint
return processor.process(exchange, callback);
}
final boolean shouldLog = shouldLogNode(node) && shouldLogExchange(exchange);
// whether we should trace it or not, some nodes should be skipped as they are abstract
// intermediate steps for instance related to on completion
boolean trace = true;
boolean sync = true;
// okay this is a regular exchange being routed we might need to log and trace
try {
// before
if (shouldLog) {
// traced holds the information about the current traced route path
if (exchange.getUnitOfWork() != null) {
TracedRouteNodes traced = exchange.getUnitOfWork().getTracedRouteNodes();
if (node instanceof OnCompletionDefinition || node instanceof OnExceptionDefinition) {
// skip any of these as its just a marker definition
trace = false;
} else if (ProcessorDefinitionHelper.isFirstChildOfType(OnCompletionDefinition.class, node)) {
// special for on completion tracing
traceOnCompletion(traced, exchange);
} else if (ProcessorDefinitionHelper.isFirstChildOfType(OnExceptionDefinition.class, node)) {
// special for on exception
traceOnException(traced, exchange);
} else if (ProcessorDefinitionHelper.isFirstChildOfType(CatchDefinition.class, node)) {
// special for do catch
traceDoCatch(traced, exchange);
} else if (ProcessorDefinitionHelper.isFirstChildOfType(FinallyDefinition.class, node)) {
// special for do finally
traceDoFinally(traced, exchange);
} else if (ProcessorDefinitionHelper.isFirstChildOfType(AggregateDefinition.class, node)) {
// special for aggregate
traceAggregate(traced, exchange);
} else {
// regular so just add it
traced.addTraced(new DefaultRouteNode(node, super.getProcessor()));
}
} else {
LOG.trace("Cannot trace as this Exchange does not have an UnitOfWork: {}", exchange);
}
}
// log and trace the processor
Object state = null;
if (shouldLog && trace) {
logExchange(exchange);
// either call the in or generic trace method depending on OUT has been enabled or not
if (tracer.isTraceOutExchanges()) {
state = traceExchangeIn(exchange);
} else {
traceExchange(exchange);
}
}
final Object traceState = state;
// special for interceptor where we need to keep booking how far we have routed in the intercepted processors
if (node.getParent() instanceof InterceptDefinition && exchange.getUnitOfWork() != null) {
TracedRouteNodes traced = exchange.getUnitOfWork().getTracedRouteNodes();
traceIntercept((InterceptDefinition) node.getParent(), traced, exchange);
}
// process the exchange
sync = processor.process(exchange, new AsyncCallback() {
@Override
public void done(boolean doneSync) {
try {
// after (trace out)
if (shouldLog && tracer.isTraceOutExchanges()) {
logExchange(exchange);
traceExchangeOut(exchange, traceState);
}
} catch (Throwable e) {
// some exception occurred in trace logic
if (shouldLogException(exchange)) {
logException(exchange, e);
}
exchange.setException(e);
} finally {
// ensure callback is always invoked
callback.done(doneSync);
}
}
});
} catch (Throwable e) {
// some exception occurred in trace logic
if (shouldLogException(exchange)) {
logException(exchange, e);
}
exchange.setException(e);
}
return sync;
}
Aggregations