001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.processor;
018
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.Map;
022
023 import org.apache.camel.CamelContext;
024 import org.apache.camel.Channel;
025 import org.apache.camel.Exchange;
026 import org.apache.camel.Processor;
027 import org.apache.camel.Service;
028 import org.apache.camel.impl.ServiceSupport;
029 import org.apache.camel.model.ProcessorDefinition;
030 import org.apache.camel.processor.interceptor.TraceFormatter;
031 import org.apache.camel.processor.interceptor.TraceInterceptor;
032 import org.apache.camel.processor.interceptor.Tracer;
033 import org.apache.camel.spi.InterceptStrategy;
034 import org.apache.camel.spi.LifecycleStrategy;
035 import org.apache.camel.spi.RouteContext;
036 import org.apache.camel.util.ServiceHelper;
037 import org.apache.commons.logging.Log;
038 import org.apache.commons.logging.LogFactory;
039
040 /**
041 * DefaultChannel is the default {@link Channel}.
042 * <p/>
043 * The current implementation is just a composite containing the interceptors and error handler
044 * that beforehand was added to the route graph directly.
045 * <br/>
046 * With this {@link Channel} we can in the future implement better strategies for routing the
047 * {@link Exchange} in the route graph, as we have a {@link Channel} between each and every node
048 * in the graph.
049 *
050 * @version $Revision: 899628 $
051 */
052 public class DefaultChannel extends ServiceSupport implements Processor, Channel {
053
054 private static final transient Log LOG = LogFactory.getLog(DefaultChannel.class);
055
056 private final List<InterceptStrategy> interceptors = new ArrayList<InterceptStrategy>();
057 private Processor errorHandler;
058 // the next processor (non wrapped)
059 private Processor nextProcessor;
060 // the real output to invoke that has been wrapped
061 private Processor output;
062 private ProcessorDefinition<?> definition;
063 private ProcessorDefinition<?> childDefinition;
064 private CamelContext camelContext;
065 private RouteContext routeContext;
066
067 public List<Processor> next() {
068 List<Processor> answer = new ArrayList<Processor>(1);
069 answer.add(nextProcessor);
070 return answer;
071 }
072
073 public boolean hasNext() {
074 return nextProcessor != null;
075 }
076
077 public void setNextProcessor(Processor next) {
078 this.nextProcessor = next;
079 }
080
081 public Processor getOutput() {
082 // the errorHandler is already decorated with interceptors
083 // so it contain the entire chain of processors, so we can safely use it directly as output
084 // if no error handler provided we use the output
085 return errorHandler != null ? errorHandler : output;
086 }
087
088 public void setOutput(Processor output) {
089 this.output = output;
090 }
091
092 public Processor getNextProcessor() {
093 return nextProcessor;
094 }
095
096 public boolean hasInterceptorStrategy(Class<?> type) {
097 for (InterceptStrategy strategy : interceptors) {
098 if (type.isInstance(strategy)) {
099 return true;
100 }
101 }
102 return false;
103 }
104
105 public void setErrorHandler(Processor errorHandler) {
106 this.errorHandler = errorHandler;
107 }
108
109 public Processor getErrorHandler() {
110 return errorHandler;
111 }
112
113 public void addInterceptStrategy(InterceptStrategy strategy) {
114 interceptors.add(strategy);
115 }
116
117 public void addInterceptStrategies(List<InterceptStrategy> strategies) {
118 interceptors.addAll(strategies);
119 }
120
121 public List<InterceptStrategy> getInterceptStrategies() {
122 return interceptors;
123 }
124
125 public ProcessorDefinition<?> getProcessorDefinition() {
126 return definition;
127 }
128
129 public void setChildDefinition(ProcessorDefinition<?> childDefinition) {
130 this.childDefinition = childDefinition;
131 }
132
133 public RouteContext getRouteContext() {
134 return routeContext;
135 }
136
137 @Override
138 protected void doStart() throws Exception {
139 ServiceHelper.startServices(errorHandler, output);
140 }
141
142 @Override
143 protected void doStop() throws Exception {
144 ServiceHelper.stopServices(output, errorHandler);
145 }
146
147 public void initChannel(ProcessorDefinition<?> outputDefinition, RouteContext routeContext) throws Exception {
148 this.routeContext = routeContext;
149 this.definition = outputDefinition;
150 this.camelContext = routeContext.getCamelContext();
151
152 Processor target = nextProcessor;
153 Processor next;
154
155 // first wrap the output with the managed strategy if any
156 InterceptStrategy managed = routeContext.getManagedInterceptStrategy();
157 if (managed != null) {
158 next = target == nextProcessor ? null : nextProcessor;
159 target = managed.wrapProcessorInInterceptors(routeContext.getCamelContext(), outputDefinition, target, next);
160 }
161
162 // then wrap the output with the tracer
163 // the tracer should have the fine grained definition so if a child is set then use it, if not then its the original output used
164 ProcessorDefinition<?> traceDef = childDefinition != null ? childDefinition : outputDefinition;
165 TraceInterceptor trace = (TraceInterceptor) getOrCreateTracer().wrapProcessorInInterceptors(routeContext.getCamelContext(), traceDef, target, null);
166 // trace interceptor need to have a reference to route context so we at runtime can enable/disable tracing on-the-fly
167 trace.setRouteContext(routeContext);
168 target = trace;
169
170 // wrap the output with the configured interceptors
171 for (InterceptStrategy strategy : interceptors) {
172 next = target == nextProcessor ? null : nextProcessor;
173 // skip tracer as we did the specially beforehand and it could potentially be added as an interceptor strategy
174 if (strategy instanceof Tracer) {
175 continue;
176 }
177 target = strategy.wrapProcessorInInterceptors(routeContext.getCamelContext(), outputDefinition, target, next);
178 }
179
180 // sets the delegate to our wrapped output
181 output = target;
182 }
183
184 private InterceptStrategy getOrCreateTracer() {
185 InterceptStrategy tracer = Tracer.getTracer(camelContext);
186 if (tracer == null) {
187 if (camelContext.getRegistry() != null) {
188 // lookup in registry
189 Map<String, Tracer> map = camelContext.getRegistry().lookupByType(Tracer.class);
190 if (map.size() == 1) {
191 tracer = map.values().iterator().next();
192 }
193 }
194 if (tracer == null) {
195 // fallback to use the default tracer
196 tracer = camelContext.getDefaultTracer();
197
198 // configure and use any trace formatter if any exists
199 Map<String, TraceFormatter> formatters = camelContext.getRegistry().lookupByType(TraceFormatter.class);
200 if (formatters.size() == 1) {
201 TraceFormatter formatter = formatters.values().iterator().next();
202 if (tracer instanceof Tracer) {
203 ((Tracer) tracer).setFormatter(formatter);
204 }
205 }
206 }
207 }
208
209 // which we must manage as well
210 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
211 if (tracer instanceof Service) {
212 strategy.onServiceAdd(camelContext, (Service) tracer, null);
213 }
214 }
215
216 return tracer;
217 }
218
219 public void process(Exchange exchange) throws Exception {
220 // push the current route context
221 if (exchange.getUnitOfWork() != null) {
222 exchange.getUnitOfWork().pushRouteContext(routeContext);
223 }
224
225 Processor processor = getOutput();
226 try {
227 if (processor != null && continueProcessing(exchange)) {
228 processor.process(exchange);
229 }
230 } finally {
231 // pop the route context we just used
232 if (exchange.getUnitOfWork() != null) {
233 exchange.getUnitOfWork().popRouteContext();
234 }
235 }
236 }
237
238 /**
239 * Strategy to determine if we should continue processing the {@link Exchange}.
240 */
241 protected boolean continueProcessing(Exchange exchange) {
242 Object stop = exchange.getProperty(Exchange.ROUTE_STOP);
243 if (stop != null) {
244 boolean doStop = exchange.getContext().getTypeConverter().convertTo(Boolean.class, stop);
245 if (doStop) {
246 if (LOG.isDebugEnabled()) {
247 LOG.debug("Exchange is marked to stop routing: " + exchange);
248 }
249 return false;
250 }
251 }
252 return true;
253 }
254
255 @Override
256 public String toString() {
257 // just output the next processor as all the interceptors and error handler is just too verbose
258 return "Channel[" + nextProcessor + "]";
259 }
260
261 }