org.apache.camel.processor.channel.DefaultChannel 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.processor.channel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.camel.AsyncProcessor;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Channel;
import org.apache.camel.Exchange;
import org.apache.camel.NamedNode;
import org.apache.camel.NamedRoute;
import org.apache.camel.Processor;
import org.apache.camel.processor.CamelInternalProcessor;
import org.apache.camel.processor.WrapProcessor;
import org.apache.camel.processor.errorhandler.RedeliveryErrorHandler;
import org.apache.camel.processor.interceptor.BacklogDebugger;
import org.apache.camel.processor.interceptor.BacklogTracer;
import org.apache.camel.spi.Debugger;
import org.apache.camel.spi.InterceptStrategy;
import org.apache.camel.spi.ManagementInterceptStrategy;
import org.apache.camel.spi.MessageHistoryFactory;
import org.apache.camel.spi.RouteContext;
import org.apache.camel.spi.Tracer;
import org.apache.camel.support.OrderedComparator;
import org.apache.camel.support.service.ServiceHelper;
/**
* DefaultChannel is the default {@link Channel}.
*
* The current implementation is just a composite containing the interceptors and error handler
* that beforehand was added to the route graph directly.
*
* With this {@link Channel} we can in the future implement better strategies for routing the
* {@link Exchange} in the route graph, as we have a {@link Channel} between each and every node
* in the graph.
*/
public class DefaultChannel extends CamelInternalProcessor implements Channel {
private Processor errorHandler;
// the next processor (non wrapped)
private Processor nextProcessor;
// the real output to invoke that has been wrapped
private Processor output;
private NamedNode definition;
private ManagementInterceptStrategy.InstrumentationProcessor> instrumentationProcessor;
private CamelContext camelContext;
private RouteContext routeContext;
private boolean routeScoped = true;
@Override
public Processor getOutput() {
// the errorHandler is already decorated with interceptors
// so it contain the entire chain of processors, so we can safely use it directly as output
// if no error handler provided we use the output
// the error handlers, interceptors, etc. woven in at design time
return errorHandler != null ? errorHandler : output;
}
@Override
public boolean hasNext() {
return nextProcessor != null;
}
@Override
public List next() {
if (!hasNext()) {
return null;
}
List answer = new ArrayList<>(1);
answer.add(nextProcessor);
return answer;
}
public void setOutput(Processor output) {
this.output = output;
}
@Override
public Processor getNextProcessor() {
return nextProcessor;
}
/**
* Sets the {@link org.apache.camel.processor.ErrorHandler} this Channel uses.
*
* @param errorHandler the error handler
*/
public void setErrorHandler(Processor errorHandler) {
this.errorHandler = errorHandler;
}
@Override
public Processor getErrorHandler() {
return errorHandler;
}
@Override
public NamedNode getProcessorDefinition() {
return definition;
}
public void setDefinition(NamedNode definition) {
this.definition = definition;
}
@Override
public RouteContext getRouteContext() {
return routeContext;
}
@Override
protected void doStart() throws Exception {
// the output has now been created, so assign the output as the processor
setProcessor(getOutput());
ServiceHelper.startService(errorHandler, output);
}
@Override
protected void doStop() throws Exception {
if (isRouteScoped()) {
// only stop services if not context scoped (as context scoped is reused by others)
ServiceHelper.stopService(output, errorHandler);
}
}
@Override
protected void doShutdown() throws Exception {
ServiceHelper.stopAndShutdownServices(output, errorHandler);
}
public boolean isRouteScoped() {
return routeScoped;
}
/**
* Initializes the channel.
* If the initialized output definition contained outputs (children) then
* the childDefinition will be set so we can leverage fine grained tracing
*
* @param routeContext the route context
* @param definition the route definition the {@link Channel} represents
* @param childDefinition the child definition
* @throws Exception is thrown if some error occurred
*/
public void initChannel(RouteContext routeContext,
NamedNode definition,
NamedNode childDefinition,
List interceptors,
Processor nextProcessor,
NamedRoute route,
boolean first,
boolean routeScoped) throws Exception {
this.routeContext = routeContext;
this.definition = definition;
this.camelContext = routeContext.getCamelContext();
this.nextProcessor = nextProcessor;
this.routeScoped = routeScoped;
// init CamelContextAware as early as possible on nextProcessor
if (nextProcessor instanceof CamelContextAware) {
((CamelContextAware) nextProcessor).setCamelContext(camelContext);
}
// the definition to wrap should be the fine grained,
// so if a child is set then use it, if not then its the original output used
NamedNode targetOutputDef = childDefinition != null ? childDefinition : definition;
log.debug("Initialize channel for target: '{}'", targetOutputDef);
// setup instrumentation processor for management (jmx)
// this is later used in postInitChannel as we need to setup the error handler later as well
ManagementInterceptStrategy managed = routeContext.getManagementInterceptStrategy();
if (managed != null) {
instrumentationProcessor = managed.createProcessor(targetOutputDef, nextProcessor);
}
if (routeContext.isMessageHistory()) {
// add message history advice
MessageHistoryFactory factory = camelContext.getMessageHistoryFactory();
addAdvice(new MessageHistoryAdvice(factory, targetOutputDef));
}
// then wrap the output with the tracer and debugger (debugger first,
// as we do not want regular tracer to trace the debugger)
if (routeContext.isDebugging()) {
if (camelContext.getDebugger() != null) {
// use custom debugger
Debugger debugger = camelContext.getDebugger();
addAdvice(new DebuggerAdvice(debugger, nextProcessor, targetOutputDef));
} else {
// use backlog debugger
BacklogDebugger debugger = getOrCreateBacklogDebugger();
camelContext.addService(debugger);
addAdvice(new BacklogDebuggerAdvice(debugger, nextProcessor, targetOutputDef));
}
}
if (routeContext.isBacklogTracing()) {
// add jmx backlog tracer
BacklogTracer backlogTracer = getOrCreateBacklogTracer();
addAdvice(new BacklogTracerAdvice(backlogTracer, targetOutputDef, route, first));
}
if (routeContext.isTracing()) {
// add logger tracer
Tracer tracer = camelContext.getTracer();
addAdvice(new TracingAdvice(tracer, targetOutputDef, route, first));
}
// sort interceptors according to ordered
interceptors.sort(OrderedComparator.get());
// reverse list so the first will be wrapped last, as it would then be first being invoked
Collections.reverse(interceptors);
// wrap the output with the configured interceptors
Processor target = nextProcessor;
for (InterceptStrategy strategy : interceptors) {
Processor next = target == nextProcessor ? null : nextProcessor;
// use the fine grained definition (eg the child if available). Its always possible to get back to the parent
Processor wrapped = strategy.wrapProcessorInInterceptors(routeContext.getCamelContext(), targetOutputDef, target, next);
if (!(wrapped instanceof AsyncProcessor)) {
log.warn("Interceptor: " + strategy + " at: " + definition + " does not return an AsyncProcessor instance."
+ " This causes the asynchronous routing engine to not work as optimal as possible."
+ " See more details at the InterceptStrategy javadoc."
+ " Camel will use a bridge to adapt the interceptor to the asynchronous routing engine,"
+ " but its not the most optimal solution. Please consider changing your interceptor to comply.");
}
if (!(wrapped instanceof WrapProcessor)) {
// wrap the target so it becomes a service and we can manage its lifecycle
wrapped = new WrapProcessor(wrapped, target);
}
target = wrapped;
}
if (routeContext.isStreamCaching()) {
addAdvice(new StreamCachingAdvice(camelContext.getStreamCachingStrategy()));
}
if (routeContext.getDelayer() != null && routeContext.getDelayer() > 0) {
addAdvice(new DelayerAdvice(routeContext.getDelayer()));
}
// sets the delegate to our wrapped output
output = target;
}
/**
* Post initializes the channel.
*
* @throws Exception is thrown if some error occurred
*/
public void postInitChannel() throws Exception {
// if jmx was enabled for the processor then either add as advice or wrap and change the processor
// on the error handler. See more details in the class javadoc of InstrumentationProcessor
if (instrumentationProcessor != null) {
boolean redeliveryPossible = false;
if (errorHandler instanceof RedeliveryErrorHandler) {
redeliveryPossible = ((RedeliveryErrorHandler) errorHandler).determineIfRedeliveryIsEnabled();
if (redeliveryPossible) {
// okay we can redeliver then we need to change the output in the error handler
// to use us which we then wrap the call so we can capture before/after for redeliveries as well
Processor currentOutput = ((RedeliveryErrorHandler) errorHandler).getOutput();
instrumentationProcessor.setProcessor(currentOutput);
((RedeliveryErrorHandler) errorHandler).changeOutput(instrumentationProcessor);
}
}
if (!redeliveryPossible) {
// optimise to use advice as we cannot redeliver
addAdvice(CamelInternalProcessor.wrap(instrumentationProcessor));
}
}
}
private BacklogTracer getOrCreateBacklogTracer() {
BacklogTracer tracer = null;
if (camelContext.getRegistry() != null) {
// lookup in registry
Map map = camelContext.getRegistry().findByTypeWithName(BacklogTracer.class);
if (map.size() == 1) {
tracer = map.values().iterator().next();
}
}
if (tracer == null) {
tracer = camelContext.getExtension(BacklogTracer.class);
}
if (tracer == null) {
tracer = BacklogTracer.createTracer(camelContext);
camelContext.setExtension(BacklogTracer.class, tracer);
}
return tracer;
}
private BacklogDebugger getOrCreateBacklogDebugger() {
BacklogDebugger debugger = null;
if (camelContext.getRegistry() != null) {
// lookup in registry
Map map = camelContext.getRegistry().findByTypeWithName(BacklogDebugger.class);
if (map.size() == 1) {
debugger = map.values().iterator().next();
}
}
if (debugger == null) {
debugger = camelContext.hasService(BacklogDebugger.class);
}
if (debugger == null) {
// fallback to use the default debugger
debugger = BacklogDebugger.createDebugger(camelContext);
}
return debugger;
}
@Override
public String toString() {
// just output the next processor as all the interceptors and error handler is just too verbose
return "Channel[" + nextProcessor + "]";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy