org.apache.camel.impl.engine.SharedCamelInternalProcessor Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.impl.engine;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.RejectedExecutionException;
import org.apache.camel.AsyncCallback;
import org.apache.camel.AsyncProcessor;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.spi.AsyncProcessorAwaitManager;
import org.apache.camel.spi.CamelInternalProcessorAdvice;
import org.apache.camel.spi.ReactiveExecutor;
import org.apache.camel.spi.RoutePolicy;
import org.apache.camel.spi.SharedInternalProcessor;
import org.apache.camel.spi.ShutdownStrategy;
import org.apache.camel.spi.Transformer;
import org.apache.camel.spi.UnitOfWork;
import org.apache.camel.support.AsyncCallbackToCompletableFutureAdapter;
import org.apache.camel.support.PluginHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Shared (thread safe) internal {@link Processor} that Camel routing engine used during routing for cross cutting
* functionality such as:
*
* - Execute {@link UnitOfWork}
* - Keeping track which route currently is being routed
* - Execute {@link RoutePolicy}
* - Gather JMX performance statics
* - Tracing
* - Debugging
* - Message History
* - Stream Caching
* - {@link Transformer}
*
* ... and more.
*
* This implementation executes this cross cutting functionality as a {@link CamelInternalProcessorAdvice} advice
* (before and after advice) by executing the {@link CamelInternalProcessorAdvice#before(Exchange)} and
* {@link CamelInternalProcessorAdvice#after(Exchange, Object)} callbacks in correct order during routing. This reduces
* number of stack frames needed during routing, and reduce the number of lines in stacktraces, as well makes debugging
* the routing engine easier for end users.
*
* Debugging tips: Camel end users whom want to debug their Camel applications with the Camel source code, then
* make sure to read the source code of this class about the debugging tips, which you can find in the
* {@link #process(Exchange, AsyncCallback, AsyncProcessor, Processor)} method.
*/
public class SharedCamelInternalProcessor implements SharedInternalProcessor {
private static final Logger LOG = LoggerFactory.getLogger(SharedCamelInternalProcessor.class);
private final ReactiveExecutor reactiveExecutor;
private final AsyncProcessorAwaitManager awaitManager;
private final ShutdownStrategy shutdownStrategy;
private final CamelInternalProcessorAdvice> advice;
public SharedCamelInternalProcessor(CamelContext camelContext, CamelInternalProcessorAdvice> advice) {
this.reactiveExecutor = camelContext.getCamelContextExtension().getReactiveExecutor();
this.awaitManager = PluginHelper.getAsyncProcessorAwaitManager(camelContext);
this.shutdownStrategy = camelContext.getShutdownStrategy();
this.advice = Objects.requireNonNull(advice, "advice");
}
/**
* Synchronous API
*/
public void process(Exchange exchange, AsyncProcessor processor, Processor resultProcessor) {
awaitManager.process(new AsyncProcessor() {
@Override
public boolean process(Exchange exchange, AsyncCallback callback) {
return SharedCamelInternalProcessor.this.process(exchange, callback, processor, resultProcessor);
}
@Override
public CompletableFuture processAsync(Exchange exchange) {
AsyncCallbackToCompletableFutureAdapter callback
= new AsyncCallbackToCompletableFutureAdapter<>(exchange);
process(exchange, callback);
return callback.getFuture();
}
@Override
public void process(Exchange exchange) throws Exception {
throw new IllegalStateException();
}
}, exchange);
}
/**
* Asynchronous API
*/
public boolean process(
Exchange exchange, AsyncCallback originalCallback, AsyncProcessor processor, Processor resultProcessor) {
if (processor == null || !continueProcessing(exchange)) {
// no processor or we should not continue then we are done
originalCallback.done(true);
return true;
}
// optimise to use object array for states, and only for the number of advices that keep state
final Object state;
// optimise for loop using index access to avoid creating iterator object
try {
state = advice.before(exchange);
} catch (Exception e) {
return handleException(exchange, originalCallback, e);
}
// create internal callback which will execute the advices in reverse order when done
AsyncCallback callback = new InternalCallback(state, exchange, originalCallback, resultProcessor);
if (exchange.isTransacted()) {
return processTransacted(exchange, processor, callback);
} else {
return processNonTransacted(exchange, processor, callback);
}
}
private static boolean handleException(Exchange exchange, AsyncCallback originalCallback, Exception e) {
exchange.setException(e);
originalCallback.done(true);
return true;
}
private static boolean processNonTransacted(Exchange exchange, AsyncProcessor processor, AsyncCallback callback) {
final UnitOfWork uow = exchange.getUnitOfWork();
// do uow before processing and if a value is returned then the uow wants to be processed after in the same thread
AsyncCallback async = callback;
boolean beforeAndAfter = uow.isBeforeAfterProcess();
if (beforeAndAfter) {
async = uow.beforeProcess(processor, exchange, async);
}
if (LOG.isTraceEnabled()) {
LOG.trace("Processing exchange for exchangeId: {} -> {}", exchange.getExchangeId(), exchange);
}
boolean sync = processor.process(exchange, async);
// optimize to only do after uow processing if really needed
if (beforeAndAfter) {
// execute any after processor work (in current thread, not in the callback)
uow.afterProcess(processor, exchange, callback, sync);
}
if (LOG.isTraceEnabled()) {
LOG.trace("Exchange processed and is continued routed {} for exchangeId: {} -> {}",
sync ? "synchronously" : "asynchronously",
exchange.getExchangeId(), exchange);
}
return sync;
}
private static boolean processTransacted(Exchange exchange, AsyncProcessor processor, AsyncCallback callback) {
// must be synchronized for transacted exchanges
if (LOG.isTraceEnabled()) {
if (exchange.isTransacted()) {
LOG.trace("Transacted Exchange must be routed synchronously for exchangeId: {} -> {}",
exchange.getExchangeId(), exchange);
} else {
LOG.trace("Synchronous UnitOfWork Exchange must be routed synchronously for exchangeId: {} -> {}",
exchange.getExchangeId(), exchange);
}
}
try {
processor.process(exchange);
} catch (Exception e) {
exchange.setException(e);
}
callback.done(true);
return true;
}
/**
* Internal callback that executes the after advices.
*/
private final class InternalCallback implements AsyncCallback {
private final Object state;
private final Exchange exchange;
private final AsyncCallback callback;
private final Processor resultProcessor;
private InternalCallback(Object state, Exchange exchange, AsyncCallback callback, Processor resultProcessor) {
this.state = state;
this.exchange = exchange;
this.callback = callback;
this.resultProcessor = resultProcessor;
}
@Override
public void done(boolean doneSync) {
// NOTE: if you are debugging Camel routes, then all the code in the for loop below is internal only
// so you can step straight to the finally-block and invoke the callback
if (resultProcessor != null) {
try {
resultProcessor.process(exchange);
} catch (Exception e) {
exchange.setException(e);
}
}
// we should call after in reverse order
try {
AdviceIterator.runAfterTask(advice, state, exchange);
} finally {
// callback must be called
if (callback != null) {
reactiveExecutor.schedule(callback);
}
}
}
}
/**
* Strategy to determine if we should continue processing the {@link Exchange}.
*/
protected boolean continueProcessing(Exchange exchange) {
if (exchange.isRouteStop()) {
LOG.debug("Exchange is marked to stop routing: {}", exchange);
return false;
}
if (shutdownStrategy.isForceShutdown()) {
if (LOG.isDebugEnabled() || exchange.getException() == null) {
String msg = "Run not allowed as ShutdownStrategy is forcing shutting down, will reject executing exchange: "
+ exchange;
LOG.debug(msg);
if (exchange.getException() == null) {
exchange.setException(new RejectedExecutionException(msg));
}
}
return false;
}
// yes we can continue
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy