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