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 */
017package org.apache.camel.reifier;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.ExecutorService;
025import java.util.concurrent.ScheduledExecutorService;
026import java.util.function.BiFunction;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.Channel;
030import org.apache.camel.ErrorHandlerFactory;
031import org.apache.camel.ExtendedCamelContext;
032import org.apache.camel.Processor;
033import org.apache.camel.Route;
034import org.apache.camel.model.AggregateDefinition;
035import org.apache.camel.model.BeanDefinition;
036import org.apache.camel.model.CatchDefinition;
037import org.apache.camel.model.ChoiceDefinition;
038import org.apache.camel.model.CircuitBreakerDefinition;
039import org.apache.camel.model.ClaimCheckDefinition;
040import org.apache.camel.model.ConvertBodyDefinition;
041import org.apache.camel.model.DelayDefinition;
042import org.apache.camel.model.DynamicRouterDefinition;
043import org.apache.camel.model.EnrichDefinition;
044import org.apache.camel.model.ExecutorServiceAwareDefinition;
045import org.apache.camel.model.FilterDefinition;
046import org.apache.camel.model.FinallyDefinition;
047import org.apache.camel.model.IdempotentConsumerDefinition;
048import org.apache.camel.model.InOnlyDefinition;
049import org.apache.camel.model.InOutDefinition;
050import org.apache.camel.model.InterceptDefinition;
051import org.apache.camel.model.InterceptFromDefinition;
052import org.apache.camel.model.InterceptSendToEndpointDefinition;
053import org.apache.camel.model.LoadBalanceDefinition;
054import org.apache.camel.model.LogDefinition;
055import org.apache.camel.model.LoopDefinition;
056import org.apache.camel.model.MarshalDefinition;
057import org.apache.camel.model.MulticastDefinition;
058import org.apache.camel.model.OnCompletionDefinition;
059import org.apache.camel.model.OnExceptionDefinition;
060import org.apache.camel.model.OnFallbackDefinition;
061import org.apache.camel.model.OptionalIdentifiedDefinition;
062import org.apache.camel.model.OtherwiseDefinition;
063import org.apache.camel.model.PipelineDefinition;
064import org.apache.camel.model.PolicyDefinition;
065import org.apache.camel.model.PollEnrichDefinition;
066import org.apache.camel.model.ProcessDefinition;
067import org.apache.camel.model.ProcessorDefinition;
068import org.apache.camel.model.ProcessorDefinitionHelper;
069import org.apache.camel.model.RecipientListDefinition;
070import org.apache.camel.model.RemoveHeaderDefinition;
071import org.apache.camel.model.RemoveHeadersDefinition;
072import org.apache.camel.model.RemovePropertiesDefinition;
073import org.apache.camel.model.RemovePropertyDefinition;
074import org.apache.camel.model.ResequenceDefinition;
075import org.apache.camel.model.RollbackDefinition;
076import org.apache.camel.model.RouteDefinition;
077import org.apache.camel.model.RouteDefinitionHelper;
078import org.apache.camel.model.RoutingSlipDefinition;
079import org.apache.camel.model.SagaDefinition;
080import org.apache.camel.model.SamplingDefinition;
081import org.apache.camel.model.ScriptDefinition;
082import org.apache.camel.model.SetBodyDefinition;
083import org.apache.camel.model.SetExchangePatternDefinition;
084import org.apache.camel.model.SetHeaderDefinition;
085import org.apache.camel.model.SetPropertyDefinition;
086import org.apache.camel.model.SortDefinition;
087import org.apache.camel.model.SplitDefinition;
088import org.apache.camel.model.StepDefinition;
089import org.apache.camel.model.StopDefinition;
090import org.apache.camel.model.ThreadsDefinition;
091import org.apache.camel.model.ThrottleDefinition;
092import org.apache.camel.model.ThrowExceptionDefinition;
093import org.apache.camel.model.ToDefinition;
094import org.apache.camel.model.ToDynamicDefinition;
095import org.apache.camel.model.TransactedDefinition;
096import org.apache.camel.model.TransformDefinition;
097import org.apache.camel.model.TryDefinition;
098import org.apache.camel.model.UnmarshalDefinition;
099import org.apache.camel.model.ValidateDefinition;
100import org.apache.camel.model.WhenDefinition;
101import org.apache.camel.model.WhenSkipSendToEndpointDefinition;
102import org.apache.camel.model.WireTapDefinition;
103import org.apache.camel.model.cloud.ServiceCallDefinition;
104import org.apache.camel.processor.InterceptEndpointProcessor;
105import org.apache.camel.processor.Pipeline;
106import org.apache.camel.processor.channel.DefaultChannel;
107import org.apache.camel.reifier.errorhandler.ErrorHandlerReifier;
108import org.apache.camel.spi.ExecutorServiceManager;
109import org.apache.camel.spi.IdAware;
110import org.apache.camel.spi.InterceptStrategy;
111import org.apache.camel.spi.LifecycleStrategy;
112import org.apache.camel.spi.ReifierStrategy;
113import org.apache.camel.spi.RouteIdAware;
114import org.apache.camel.util.ObjectHelper;
115import org.slf4j.Logger;
116import org.slf4j.LoggerFactory;
117
118public abstract class ProcessorReifier<T extends ProcessorDefinition<?>> extends AbstractReifier {
119
120    private static final Map<Class<?>, BiFunction<Route, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>>> PROCESSORS;
121    static {
122        // NOTE: if adding a new class then update the initial capacity of the
123        // HashMap
124        Map<Class<?>, BiFunction<Route, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>>> map = new HashMap<>(65);
125        map.put(AggregateDefinition.class, AggregateReifier::new);
126        map.put(BeanDefinition.class, BeanReifier::new);
127        map.put(CatchDefinition.class, CatchReifier::new);
128        map.put(ChoiceDefinition.class, ChoiceReifier::new);
129        map.put(CircuitBreakerDefinition.class, CircuitBreakerReifier::new);
130        map.put(ClaimCheckDefinition.class, ClaimCheckReifier::new);
131        map.put(ConvertBodyDefinition.class, ConvertBodyReifier::new);
132        map.put(DelayDefinition.class, DelayReifier::new);
133        map.put(DynamicRouterDefinition.class, DynamicRouterReifier::new);
134        map.put(EnrichDefinition.class, EnrichReifier::new);
135        map.put(FilterDefinition.class, FilterReifier::new);
136        map.put(FinallyDefinition.class, FinallyReifier::new);
137        map.put(IdempotentConsumerDefinition.class, IdempotentConsumerReifier::new);
138        map.put(InOnlyDefinition.class, SendReifier::new);
139        map.put(InOutDefinition.class, SendReifier::new);
140        map.put(InterceptDefinition.class, InterceptReifier::new);
141        map.put(InterceptFromDefinition.class, InterceptFromReifier::new);
142        map.put(InterceptSendToEndpointDefinition.class, InterceptSendToEndpointReifier::new);
143        map.put(LoadBalanceDefinition.class, LoadBalanceReifier::new);
144        map.put(LogDefinition.class, LogReifier::new);
145        map.put(LoopDefinition.class, LoopReifier::new);
146        map.put(MarshalDefinition.class, MarshalReifier::new);
147        map.put(MulticastDefinition.class, MulticastReifier::new);
148        map.put(OnCompletionDefinition.class, OnCompletionReifier::new);
149        map.put(OnExceptionDefinition.class, OnExceptionReifier::new);
150        map.put(OnFallbackDefinition.class, OnFallbackReifier::new);
151        map.put(OtherwiseDefinition.class, OtherwiseReifier::new);
152        map.put(PipelineDefinition.class, PipelineReifier::new);
153        map.put(PolicyDefinition.class, PolicyReifier::new);
154        map.put(PollEnrichDefinition.class, PollEnrichReifier::new);
155        map.put(ProcessDefinition.class, ProcessReifier::new);
156        map.put(RecipientListDefinition.class, RecipientListReifier::new);
157        map.put(RemoveHeaderDefinition.class, RemoveHeaderReifier::new);
158        map.put(RemoveHeadersDefinition.class, RemoveHeadersReifier::new);
159        map.put(RemovePropertiesDefinition.class, RemovePropertiesReifier::new);
160        map.put(RemovePropertyDefinition.class, RemovePropertyReifier::new);
161        map.put(ResequenceDefinition.class, ResequenceReifier::new);
162        map.put(RollbackDefinition.class, RollbackReifier::new);
163        map.put(RoutingSlipDefinition.class, RoutingSlipReifier::new);
164        map.put(SagaDefinition.class, SagaReifier::new);
165        map.put(SamplingDefinition.class, SamplingReifier::new);
166        map.put(ScriptDefinition.class, ScriptReifier::new);
167        map.put(ServiceCallDefinition.class, ServiceCallReifier::new);
168        map.put(SetBodyDefinition.class, SetBodyReifier::new);
169        map.put(SetExchangePatternDefinition.class, SetExchangePatternReifier::new);
170        map.put(SetHeaderDefinition.class, SetHeaderReifier::new);
171        map.put(SetPropertyDefinition.class, SetPropertyReifier::new);
172        map.put(SortDefinition.class, SortReifier::new);
173        map.put(SplitDefinition.class, SplitReifier::new);
174        map.put(StepDefinition.class, StepReifier::new);
175        map.put(StopDefinition.class, StopReifier::new);
176        map.put(ThreadsDefinition.class, ThreadsReifier::new);
177        map.put(ThrottleDefinition.class, ThrottleReifier::new);
178        map.put(ThrowExceptionDefinition.class, ThrowExceptionReifier::new);
179        map.put(ToDefinition.class, SendReifier::new);
180        map.put(ToDynamicDefinition.class, ToDynamicReifier::new);
181        map.put(TransactedDefinition.class, TransactedReifier::new);
182        map.put(TransformDefinition.class, TransformReifier::new);
183        map.put(TryDefinition.class, TryReifier::new);
184        map.put(UnmarshalDefinition.class, UnmarshalReifier::new);
185        map.put(ValidateDefinition.class, ValidateReifier::new);
186        map.put(WireTapDefinition.class, WireTapReifier::new);
187        map.put(WhenSkipSendToEndpointDefinition.class, WhenSkipSendToEndpointReifier::new);
188        map.put(WhenDefinition.class, WhenReifier::new);
189        PROCESSORS = map;
190        ReifierStrategy.addReifierClearer(ProcessorReifier::clearReifiers);
191    }
192    protected final Logger log = LoggerFactory.getLogger(getClass());
193
194    protected final T definition;
195
196    public ProcessorReifier(Route route, T definition) {
197        super(route);
198        this.definition = definition;
199    }
200
201    public ProcessorReifier(CamelContext camelContext, T definition) {
202        super(camelContext);
203        this.definition = definition;
204    }
205
206    public static void registerReifier(Class<?> processorClass, BiFunction<Route, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>> creator) {
207        PROCESSORS.put(processorClass, creator);
208    }
209
210    public static void clearReifiers() {
211        PROCESSORS.clear();
212    }
213
214    public static ProcessorReifier<? extends ProcessorDefinition<?>> reifier(Route route, ProcessorDefinition<?> definition) {
215        BiFunction<Route, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>> reifier = PROCESSORS.get(definition.getClass());
216        if (reifier != null) {
217            return reifier.apply(route, definition);
218        }
219        throw new IllegalStateException("Unsupported definition: " + definition);
220    }
221
222    /**
223     * Determines whether a new thread pool will be created or not.
224     * <p/>
225     * This is used to know if a new thread pool will be created, and therefore
226     * is not shared by others, and therefore exclusive to the definition.
227     *
228     * @param definition the node definition which may leverage executor
229     *            service.
230     * @param useDefault whether to fallback and use a default thread pool, if
231     *            no explicit configured
232     * @return <tt>true</tt> if a new thread pool will be created,
233     *         <tt>false</tt> if not
234     * @see #getConfiguredExecutorService(String, ExecutorServiceAwareDefinition, boolean)
235     */
236    public boolean willCreateNewThreadPool(ExecutorServiceAwareDefinition<?> definition, boolean useDefault) {
237        ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
238        ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext);
239
240        if (definition.getExecutorService() != null) {
241            // no there is a custom thread pool configured
242            return false;
243        } else if (definition.getExecutorServiceRef() != null) {
244            ExecutorService answer = lookup(definition.getExecutorServiceRef(), ExecutorService.class);
245            // if no existing thread pool, then we will have to create a new
246            // thread pool
247            return answer == null;
248        } else if (useDefault) {
249            return true;
250        }
251
252        return false;
253    }
254
255    /**
256     * Will lookup and get the configured
257     * {@link ExecutorService} from the given definition.
258     * <p/>
259     * This method will lookup for configured thread pool in the following order
260     * <ul>
261     * <li>from the definition if any explicit configured executor service.</li>
262     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
263     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile
264     * ThreadPoolProfile(s)}.</li>
265     * <li>if none found, then <tt>null</tt> is returned.</li>
266     * </ul>
267     * The various {@link ExecutorServiceAwareDefinition} should use this helper
268     * method to ensure they support configured executor services in the same
269     * coherent way.
270     *
271     * @param name name which is appended to the thread name, when the
272     *            {@link ExecutorService} is created based
273     *            on a {@link org.apache.camel.spi.ThreadPoolProfile}.
274     * @param definition the node definition which may leverage executor
275     *            service.
276     * @param useDefault whether to fallback and use a default thread pool, if
277     *            no explicit configured
278     * @return the configured executor service, or <tt>null</tt> if none was
279     *         configured.
280     * @throws IllegalArgumentException is thrown if lookup of executor service
281     *             in {@link org.apache.camel.spi.Registry} was not found
282     */
283    public ExecutorService getConfiguredExecutorService(String name, ExecutorServiceAwareDefinition<?> definition, boolean useDefault)
284        throws IllegalArgumentException {
285        ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
286        ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext);
287
288        // prefer to use explicit configured executor on the definition
289        if (definition.getExecutorService() != null) {
290            return definition.getExecutorService();
291        } else if (definition.getExecutorServiceRef() != null) {
292            // lookup in registry first and use existing thread pool if exists
293            ExecutorService answer = lookupExecutorServiceRef(name, definition, parseString(definition.getExecutorServiceRef()));
294            if (answer == null) {
295                throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef()
296                                                   + " not found in registry (as an ExecutorService instance) or as a thread pool profile.");
297            }
298            return answer;
299        } else if (useDefault) {
300            return manager.newDefaultThreadPool(definition, name);
301        }
302
303        return null;
304    }
305
306    /**
307     * Will lookup and get the configured
308     * {@link java.util.concurrent.ScheduledExecutorService} from the given
309     * definition.
310     * <p/>
311     * This method will lookup for configured thread pool in the following order
312     * <ul>
313     * <li>from the definition if any explicit configured executor service.</li>
314     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
315     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile
316     * ThreadPoolProfile(s)}.</li>
317     * <li>if none found, then <tt>null</tt> is returned.</li>
318     * </ul>
319     * The various {@link ExecutorServiceAwareDefinition} should use this helper
320     * method to ensure they support configured executor services in the same
321     * coherent way.
322     *
323     * @param name name which is appended to the thread name, when the
324     *            {@link ExecutorService} is created based
325     *            on a {@link org.apache.camel.spi.ThreadPoolProfile}.
326     * @param definition the node definition which may leverage executor
327     *            service.
328     * @param useDefault whether to fallback and use a default thread pool, if
329     *            no explicit configured
330     * @return the configured executor service, or <tt>null</tt> if none was
331     *         configured.
332     * @throws IllegalArgumentException is thrown if the found instance is not a
333     *             ScheduledExecutorService type, or lookup of executor service
334     *             in {@link org.apache.camel.spi.Registry} was not found
335     */
336    public ScheduledExecutorService getConfiguredScheduledExecutorService(String name, ExecutorServiceAwareDefinition<?> definition,
337                                                                                 boolean useDefault)
338        throws IllegalArgumentException {
339        ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
340        ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext);
341
342        // prefer to use explicit configured executor on the definition
343        if (definition.getExecutorService() != null) {
344            ExecutorService executorService = definition.getExecutorService();
345            if (executorService instanceof ScheduledExecutorService) {
346                return (ScheduledExecutorService)executorService;
347            }
348            throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() + " is not an ScheduledExecutorService instance");
349        } else if (definition.getExecutorServiceRef() != null) {
350            ScheduledExecutorService answer = lookupScheduledExecutorServiceRef(name, definition, definition.getExecutorServiceRef());
351            if (answer == null) {
352                throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef()
353                                                   + " not found in registry (as an ScheduledExecutorService instance) or as a thread pool profile.");
354            }
355            return answer;
356        } else if (useDefault) {
357            return manager.newDefaultScheduledThreadPool(definition, name);
358        }
359
360        return null;
361    }
362
363    /**
364     * Will lookup in {@link org.apache.camel.spi.Registry} for a
365     * {@link ScheduledExecutorService} registered with the given
366     * <tt>executorServiceRef</tt> name.
367     * <p/>
368     * This method will lookup for configured thread pool in the following order
369     * <ul>
370     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
371     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile
372     * ThreadPoolProfile(s)}.</li>
373     * <li>if none found, then <tt>null</tt> is returned.</li>
374     * </ul>
375     *
376     * @param name name which is appended to the thread name, when the
377     *            {@link ExecutorService} is created based
378     *            on a {@link org.apache.camel.spi.ThreadPoolProfile}.
379     * @param source the source to use the thread pool
380     * @param executorServiceRef reference name of the thread pool
381     * @return the executor service, or <tt>null</tt> if none was found.
382     */
383    public ScheduledExecutorService lookupScheduledExecutorServiceRef(String name, Object source, String executorServiceRef) {
384
385        ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
386        ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext);
387        ObjectHelper.notNull(executorServiceRef, "executorServiceRef");
388
389        // lookup in registry first and use existing thread pool if exists
390        ScheduledExecutorService answer = lookup(executorServiceRef, ScheduledExecutorService.class);
391        if (answer == null) {
392            // then create a thread pool assuming the ref is a thread pool
393            // profile id
394            answer = manager.newScheduledThreadPool(source, name, executorServiceRef);
395        }
396        return answer;
397    }
398
399    /**
400     * Will lookup in {@link org.apache.camel.spi.Registry} for a
401     * {@link ExecutorService} registered with the given
402     * <tt>executorServiceRef</tt> name.
403     * <p/>
404     * This method will lookup for configured thread pool in the following order
405     * <ul>
406     * <li>from the {@link org.apache.camel.spi.Registry} if found</li>
407     * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile
408     * ThreadPoolProfile(s)}.</li>
409     * <li>if none found, then <tt>null</tt> is returned.</li>
410     * </ul>
411     *
412     * @param name name which is appended to the thread name, when the
413     *            {@link ExecutorService} is created based
414     *            on a {@link org.apache.camel.spi.ThreadPoolProfile}.
415     * @param source the source to use the thread pool
416     * @param executorServiceRef reference name of the thread pool
417     * @return the executor service, or <tt>null</tt> if none was found.
418     */
419    public ExecutorService lookupExecutorServiceRef(String name, Object source, String executorServiceRef) {
420
421        ExecutorServiceManager manager = camelContext.getExecutorServiceManager();
422        ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext);
423        ObjectHelper.notNull(executorServiceRef, "executorServiceRef");
424
425        // lookup in registry first and use existing thread pool if exists
426        ExecutorService answer = lookup(executorServiceRef, ExecutorService.class);
427        if (answer == null) {
428            // then create a thread pool assuming the ref is a thread pool
429            // profile id
430            answer = manager.newThreadPool(source, name, executorServiceRef);
431        }
432        return answer;
433    }
434
435    /**
436     * Is there any outputs in the given list.
437     * <p/>
438     * Is used for check if the route output has any real outputs (non
439     * abstracts)
440     *
441     * @param outputs the outputs
442     * @param excludeAbstract whether or not to exclude abstract outputs (e.g.
443     *            skip onException etc.)
444     * @return <tt>true</tt> if has outputs, otherwise <tt>false</tt> is
445     *         returned
446     */
447    @SuppressWarnings({"unchecked", "rawtypes"})
448    public boolean hasOutputs(List<ProcessorDefinition<?>> outputs, boolean excludeAbstract) {
449        if (outputs == null || outputs.isEmpty()) {
450            return false;
451        }
452        if (!excludeAbstract) {
453            return true;
454        }
455        for (ProcessorDefinition output : outputs) {
456            if (output.isWrappingEntireOutput()) {
457                // special for those as they wrap entire output, so we should
458                // just check its output
459                return hasOutputs(output.getOutputs(), excludeAbstract);
460            }
461            if (!output.isAbstract()) {
462                return true;
463            }
464        }
465        return false;
466    }
467
468    /**
469     * Override this in definition class and implement logic to create the
470     * processor based on the definition model.
471     */
472    public abstract Processor createProcessor() throws Exception;
473
474    /**
475     * Prefer to use {#link #createChildProcessor}.
476     */
477    protected Processor createOutputsProcessor() throws Exception {
478        Collection<ProcessorDefinition<?>> outputs = definition.getOutputs();
479        return createOutputsProcessor(outputs);
480    }
481
482    /**
483     * Creates the child processor (outputs) from the current definition
484     *
485     * @param mandatory whether or not children is mandatory (ie the definition
486     *            should have outputs)
487     * @return the created children, or <tt>null</tt> if definition had no
488     *         output
489     * @throws Exception is thrown if error creating the child or if it was
490     *             mandatory and there was no output defined on definition
491     */
492    protected Processor createChildProcessor(boolean mandatory) throws Exception {
493        Processor children = null;
494        // at first use custom factory
495        if (camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory() != null) {
496            children = camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory().createChildProcessor(route, definition, mandatory);
497        }
498        // fallback to default implementation if factory did not create the
499        // child
500        if (children == null) {
501            children = createOutputsProcessor();
502        }
503
504        if (children == null && mandatory) {
505            throw new IllegalArgumentException("Definition has no children on " + definition);
506        }
507        return children;
508    }
509
510    public void addRoutes() throws Exception {
511        Channel processor = makeProcessor();
512        if (processor == null) {
513            // no processor to add
514            return;
515        }
516
517        // are we routing to an endpoint interceptor, if so we should not
518        // add it as an event driven
519        // processor as we use the producer to trigger the interceptor
520        boolean endpointInterceptor = processor.getNextProcessor() instanceof InterceptEndpointProcessor;
521
522        // only add regular processors as event driven
523        if (endpointInterceptor) {
524            log.debug("Endpoint interceptor should not be added as an event driven consumer route: {}", processor);
525        } else {
526            log.trace("Adding event driven processor: {}", processor);
527            route.getEventDrivenProcessors().add(processor);
528        }
529    }
530
531    /**
532     * Wraps the child processor in whatever necessary interceptors and error
533     * handlers
534     */
535    public Channel wrapProcessor(Processor processor) throws Exception {
536        // don't double wrap
537        if (processor instanceof Channel) {
538            return (Channel)processor;
539        }
540        return wrapChannel(processor, null);
541    }
542
543    protected Channel wrapChannel(Processor processor, ProcessorDefinition<?> child) throws Exception {
544        return wrapChannel(processor, child, definition.isInheritErrorHandler());
545    }
546
547    protected Channel wrapChannel(Processor processor, ProcessorDefinition<?> child, Boolean inheritErrorHandler) throws Exception {
548        // put a channel in between this and each output to control the route
549        // flow logic
550        DefaultChannel channel = new DefaultChannel(camelContext);
551
552        // add interceptor strategies to the channel must be in this order:
553        // camel context, route context, local
554        List<InterceptStrategy> interceptors = new ArrayList<>();
555        interceptors.addAll(camelContext.adapt(ExtendedCamelContext.class).getInterceptStrategies());
556        interceptors.addAll(route.getInterceptStrategies());
557        interceptors.addAll(definition.getInterceptStrategies());
558
559        // force the creation of an id
560        RouteDefinitionHelper.forceAssignIds(camelContext, definition);
561
562        // fix parent/child relationship. This will be the case of the routes
563        // has been
564        // defined using XML DSL or end user may have manually assembled a route
565        // from the model.
566        // Background note: parent/child relationship is assembled on-the-fly
567        // when using Java DSL (fluent builders)
568        // where as when using XML DSL (JAXB) then it fixed after, but if people
569        // are using custom interceptors
570        // then we need to fix the parent/child relationship beforehand, and
571        // thus we can do it here
572        // ideally we need the design time route -> runtime route to be a
573        // 2-phase pass (scheduled work for Camel 3.0)
574        if (child != null && definition != child) {
575            child.setParent(definition);
576        }
577
578        // set the child before init the channel
579        RouteDefinition route = ProcessorDefinitionHelper.getRoute(definition);
580        boolean first = false;
581        if (route != null && !route.getOutputs().isEmpty()) {
582            first = route.getOutputs().get(0) == definition;
583        }
584        // initialize the channel
585        channel.initChannel(this.route, definition, child, interceptors, processor, route, first);
586
587        boolean wrap = false;
588        // set the error handler, must be done after init as we can set the
589        // error handler as first in the chain
590        if (definition instanceof TryDefinition || definition instanceof CatchDefinition || definition instanceof FinallyDefinition) {
591            // do not use error handler for try .. catch .. finally blocks as it
592            // will handle errors itself
593            log.trace("{} is part of doTry .. doCatch .. doFinally so no error handler is applied", definition);
594        } else if (ProcessorDefinitionHelper.isParentOfType(TryDefinition.class, definition, true)
595                   || ProcessorDefinitionHelper.isParentOfType(CatchDefinition.class, definition, true)
596                   || ProcessorDefinitionHelper.isParentOfType(FinallyDefinition.class, definition, true)) {
597            // do not use error handler for try .. catch .. finally blocks as it
598            // will handle errors itself
599            // by checking that any of our parent(s) is not a try .. catch or
600            // finally type
601            log.trace("{} is part of doTry .. doCatch .. doFinally so no error handler is applied", definition);
602        } else if (definition instanceof OnExceptionDefinition || ProcessorDefinitionHelper.isParentOfType(OnExceptionDefinition.class, definition, true)) {
603            log.trace("{} is part of OnException so no error handler is applied", definition);
604            // do not use error handler for onExceptions blocks as it will
605            // handle errors itself
606        } else if (definition instanceof CircuitBreakerDefinition || ProcessorDefinitionHelper.isParentOfType(CircuitBreakerDefinition.class, definition, true)) {
607            // do not use error handler for circuit breaker
608            // however if inherit error handler is enabled, we need to wrap an error handler on the parent
609            if (inheritErrorHandler != null && inheritErrorHandler && child == null) {
610                // only wrap the parent (not the children of the circuit breaker)
611                wrap = true;
612            } else {
613                log.trace("{} is part of CircuitBreaker so no error handler is applied", definition);
614            }
615        } else if (definition instanceof MulticastDefinition) {
616            // do not use error handler for multicast as it offers fine grained
617            // error handlers for its outputs
618            // however if share unit of work is enabled, we need to wrap an
619            // error handler on the multicast parent
620            MulticastDefinition def = (MulticastDefinition)definition;
621            boolean isShareUnitOfWork = parseBoolean(def.getShareUnitOfWork(), false);
622            if (isShareUnitOfWork && child == null) {
623                // only wrap the parent (not the children of the multicast)
624                wrap = true;
625            } else {
626                log.trace("{} is part of multicast which have special error handling so no error handler is applied", definition);
627            }
628        } else {
629            // use error handler by default or if configured to do so
630            wrap = true;
631        }
632        if (wrap) {
633            wrapChannelInErrorHandler(channel, inheritErrorHandler);
634        }
635
636        // do post init at the end
637        channel.postInitChannel();
638        log.trace("{} wrapped in Channel: {}", definition, channel);
639
640        return channel;
641    }
642
643    /**
644     * Wraps the given channel in error handler (if error handler is inherited)
645     *
646     * @param channel the channel
647     * @param inheritErrorHandler whether to inherit error handler
648     * @throws Exception can be thrown if failed to create error handler builder
649     */
650    private void wrapChannelInErrorHandler(DefaultChannel channel, Boolean inheritErrorHandler) throws Exception {
651        if (inheritErrorHandler == null || inheritErrorHandler) {
652            log.trace("{} is configured to inheritErrorHandler", definition);
653            Processor output = channel.getOutput();
654            Processor errorHandler = wrapInErrorHandler(output, true);
655            // set error handler on channel
656            channel.setErrorHandler(errorHandler);
657        } else {
658            log.debug("{} is configured to not inheritErrorHandler.", definition);
659        }
660    }
661
662    /**
663     * Wraps the given output in an error handler
664     *
665     * @param output the output
666     * @param longLived if the processor is longLived or not
667     * @return the output wrapped with the error handler
668     * @throws Exception can be thrown if failed to create error handler builder
669     */
670    protected Processor wrapInErrorHandler(Processor output, boolean longLived) throws Exception {
671        ErrorHandlerFactory builder = route.getErrorHandlerFactory();
672
673        // create error handler
674        Processor errorHandler = ErrorHandlerReifier.reifier(route, builder).createErrorHandler(output);
675
676        if (longLived) {
677            // invoke lifecycles so we can manage this error handler builder
678            for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
679                strategy.onErrorHandlerAdd(route, errorHandler, builder);
680            }
681        }
682
683        return errorHandler;
684    }
685
686    /**
687     * Creates a new instance of some kind of composite processor which defaults
688     * to using a {@link Pipeline} but derived classes could change the
689     * behaviour
690     */
691    protected Processor createCompositeProcessor(List<Processor> list) throws Exception {
692        return Pipeline.newInstance(camelContext, list);
693    }
694
695    protected Processor createOutputsProcessor(Collection<ProcessorDefinition<?>> outputs) throws Exception {
696        List<Processor> list = new ArrayList<>();
697        for (ProcessorDefinition<?> output : outputs) {
698
699            // allow any custom logic before we create the processor
700            reifier(route, output).preCreateProcessor();
701
702            Processor processor = createProcessor(output);
703
704            // inject id
705            if (processor instanceof IdAware) {
706                String id = getId(output);
707                ((IdAware)processor).setId(id);
708            }
709            if (processor instanceof RouteIdAware) {
710                ((RouteIdAware)processor).setRouteId(route.getRouteId());
711            }
712
713            if (output instanceof Channel && processor == null) {
714                continue;
715            }
716
717            Processor channel = wrapChannel(processor, output);
718            list.add(channel);
719        }
720
721        // if more than one output wrap than in a composite processor else just
722        // keep it as is
723        Processor processor = null;
724        if (!list.isEmpty()) {
725            if (list.size() == 1) {
726                processor = list.get(0);
727            } else {
728                processor = createCompositeProcessor(list);
729            }
730        }
731
732        return processor;
733    }
734
735    protected Processor createProcessor(ProcessorDefinition<?> output) throws Exception {
736        Processor processor = null;
737        // at first use custom factory
738        if (camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory() != null) {
739            processor = camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory().createProcessor(route, output);
740        }
741        // fallback to default implementation if factory did not create the
742        // processor
743        if (processor == null) {
744            processor = reifier(route, output).createProcessor();
745        }
746        return processor;
747    }
748
749    /**
750     * Creates the processor and wraps it in any necessary interceptors and
751     * error handlers
752     */
753    protected Channel makeProcessor() throws Exception {
754        Processor processor = null;
755
756        // allow any custom logic before we create the processor
757        preCreateProcessor();
758
759        // at first use custom factory
760        if (camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory() != null) {
761            processor = camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory().createProcessor(route, definition);
762        }
763        // fallback to default implementation if factory did not create the
764        // processor
765        if (processor == null) {
766            processor = createProcessor();
767        }
768
769        // inject id
770        if (processor instanceof IdAware) {
771            String id = getId(definition);
772            ((IdAware)processor).setId(id);
773        }
774        if (processor instanceof RouteIdAware) {
775            ((RouteIdAware)processor).setRouteId(route.getRouteId());
776        }
777
778        if (processor == null) {
779            // no processor to make
780            return null;
781        }
782        return wrapProcessor(processor);
783    }
784
785    /**
786     * Strategy to execute any custom logic before the {@link Processor} is
787     * created.
788     */
789    protected void preCreateProcessor() {
790        definition.preCreateProcessor();
791    }
792
793    /**
794     * Strategy for children to do any custom configuration
795     *
796     * @param output the child to be added as output to this
797     */
798    public void configureChild(ProcessorDefinition<?> output) {
799        // noop
800    }
801
802    protected String getId(OptionalIdentifiedDefinition<?> def) {
803        return def.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory());
804    }
805
806}