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.core.xml;
018    
019    import java.util.ArrayList;
020    import java.util.HashSet;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Set;
024    import javax.xml.bind.annotation.XmlAccessType;
025    import javax.xml.bind.annotation.XmlAccessorType;
026    import javax.xml.bind.annotation.XmlTransient;
027    
028    import org.apache.camel.CamelContext;
029    import org.apache.camel.CamelException;
030    import org.apache.camel.RoutesBuilder;
031    import org.apache.camel.ShutdownRoute;
032    import org.apache.camel.ShutdownRunningTask;
033    import org.apache.camel.builder.ErrorHandlerBuilderRef;
034    import org.apache.camel.builder.RouteBuilder;
035    import org.apache.camel.component.properties.PropertiesComponent;
036    import org.apache.camel.component.properties.PropertiesResolver;
037    import org.apache.camel.core.xml.scan.PatternBasedPackageScanFilter;
038    import org.apache.camel.management.DefaultManagementAgent;
039    import org.apache.camel.management.DefaultManagementLifecycleStrategy;
040    import org.apache.camel.management.DefaultManagementStrategy;
041    import org.apache.camel.management.ManagedManagementStrategy;
042    import org.apache.camel.model.ContextScanDefinition;
043    import org.apache.camel.model.FromDefinition;
044    import org.apache.camel.model.IdentifiedType;
045    import org.apache.camel.model.InterceptDefinition;
046    import org.apache.camel.model.InterceptFromDefinition;
047    import org.apache.camel.model.InterceptSendToEndpointDefinition;
048    import org.apache.camel.model.OnCompletionDefinition;
049    import org.apache.camel.model.OnExceptionDefinition;
050    import org.apache.camel.model.PackageScanDefinition;
051    import org.apache.camel.model.ProcessorDefinition;
052    import org.apache.camel.model.RouteBuilderDefinition;
053    import org.apache.camel.model.RouteContainer;
054    import org.apache.camel.model.RouteContextRefDefinition;
055    import org.apache.camel.model.RouteDefinition;
056    import org.apache.camel.model.ThreadPoolProfileDefinition;
057    import org.apache.camel.model.TransactedDefinition;
058    import org.apache.camel.model.config.PropertiesDefinition;
059    import org.apache.camel.model.dataformat.DataFormatsDefinition;
060    import org.apache.camel.processor.interceptor.Delayer;
061    import org.apache.camel.processor.interceptor.HandleFault;
062    import org.apache.camel.processor.interceptor.TraceFormatter;
063    import org.apache.camel.processor.interceptor.Tracer;
064    import org.apache.camel.spi.ClassResolver;
065    import org.apache.camel.spi.Debugger;
066    import org.apache.camel.spi.EventFactory;
067    import org.apache.camel.spi.EventNotifier;
068    import org.apache.camel.spi.ExecutorServiceStrategy;
069    import org.apache.camel.spi.FactoryFinderResolver;
070    import org.apache.camel.spi.InflightRepository;
071    import org.apache.camel.spi.InterceptStrategy;
072    import org.apache.camel.spi.LifecycleStrategy;
073    import org.apache.camel.spi.ManagementStrategy;
074    import org.apache.camel.spi.PackageScanClassResolver;
075    import org.apache.camel.spi.PackageScanFilter;
076    import org.apache.camel.spi.ProcessorFactory;
077    import org.apache.camel.spi.ShutdownStrategy;
078    import org.apache.camel.spi.ThreadPoolProfile;
079    import org.apache.camel.util.CamelContextHelper;
080    import org.apache.camel.util.EndpointHelper;
081    import org.apache.camel.util.ObjectHelper;
082    import org.apache.commons.logging.Log;
083    import org.apache.commons.logging.LogFactory;
084    
085    /**
086     * A factory to create and initialize a
087     * {@link CamelContext} and install routes either explicitly configured
088     * or found by searching the classpath for Java classes which extend
089     * {@link org.apache.camel.builder.RouteBuilder}.
090     *
091     * @version $Revision: 938746 $
092     */
093    @XmlAccessorType(XmlAccessType.FIELD)
094    public abstract class AbstractCamelContextFactoryBean<T extends CamelContext> extends IdentifiedType implements RouteContainer {
095        private static final Log LOG = LogFactory.getLog(AbstractCamelContextFactoryBean.class);
096    
097        @XmlTransient
098        private List<RoutesBuilder> builders = new ArrayList<RoutesBuilder>();
099        @XmlTransient
100        private ClassLoader contextClassLoaderOnStart;
101    
102        public AbstractCamelContextFactoryBean() {
103            // Lets keep track of the class loader for when we actually do start things up
104            contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader();
105        }
106    
107        public Object getObject() throws Exception {
108            return getContext();
109        }
110    
111        public Class getObjectType() {
112            return CamelContext.class;
113        }
114    
115        public boolean isSingleton() {
116            return true;
117        }
118    
119        public ClassLoader getContextClassLoaderOnStart() {
120            return contextClassLoaderOnStart;
121        }
122    
123        public void afterPropertiesSet() throws Exception {
124            if (ObjectHelper.isEmpty(getId())) {
125                throw new IllegalArgumentException("Id must be set");
126            }
127    
128            if (getProperties() != null) {
129                getContext().setProperties(getProperties().asMap());
130            }
131    
132            // set the resolvers first
133            PackageScanClassResolver packageResolver = getBeanForType(PackageScanClassResolver.class);
134            if (packageResolver != null) {
135                LOG.info("Using custom PackageScanClassResolver: " + packageResolver);
136                getContext().setPackageScanClassResolver(packageResolver);
137            }
138            ClassResolver classResolver = getBeanForType(ClassResolver.class);
139            if (classResolver != null) {
140                LOG.info("Using custom ClassResolver: " + classResolver);
141                getContext().setClassResolver(classResolver);
142            }
143            FactoryFinderResolver factoryFinderResolver = getBeanForType(FactoryFinderResolver.class);
144            if (factoryFinderResolver != null) {
145                LOG.info("Using custom FactoryFinderResolver: " + factoryFinderResolver);
146                getContext().setFactoryFinderResolver(factoryFinderResolver);
147            }
148            ExecutorServiceStrategy executorServiceStrategy = getBeanForType(ExecutorServiceStrategy.class);
149            if (executorServiceStrategy != null) {
150                LOG.info("Using custom ExecutorServiceStrategy: " + executorServiceStrategy);
151                getContext().setExecutorServiceStrategy(executorServiceStrategy);
152            }
153            ProcessorFactory processorFactory = getBeanForType(ProcessorFactory.class);
154            if (processorFactory != null) {
155                LOG.info("Using custom ProcessorFactory: " + processorFactory);
156                getContext().setProcessorFactory(processorFactory);
157            }
158    
159            Debugger debugger = getBeanForType(Debugger.class);
160            if (debugger != null) {
161                LOG.info("Using custom Debugger: " + debugger);
162                getContext().setDebugger(debugger);
163            }
164    
165            // set the custom registry if defined
166            initCustomRegistry(getContext());
167    
168            // setup property placeholder so we got it as early as possible
169            initPropertyPlaceholder();
170    
171            // setup JMX agent at first
172            initJMXAgent();
173    
174            Tracer tracer = getBeanForType(Tracer.class);
175            if (tracer != null) {
176                // use formatter if there is a TraceFormatter bean defined
177                TraceFormatter formatter = getBeanForType(TraceFormatter.class);
178                if (formatter != null) {
179                    tracer.setFormatter(formatter);
180                }
181                LOG.info("Using custom Tracer: " + tracer);
182                getContext().addInterceptStrategy(tracer);
183            }
184    
185            HandleFault handleFault = getBeanForType(HandleFault.class);
186            if (handleFault != null) {
187                LOG.info("Using custom HandleFault: " + handleFault);
188                getContext().addInterceptStrategy(handleFault);
189            }
190    
191            Delayer delayer = getBeanForType(Delayer.class);
192            if (delayer != null) {
193                LOG.info("Using custom Delayer: " + delayer);
194                getContext().addInterceptStrategy(delayer);
195            }
196    
197            InflightRepository inflightRepository = getBeanForType(InflightRepository.class);
198            if (delayer != null) {
199                LOG.info("Using custom InflightRepository: " + inflightRepository);
200                getContext().setInflightRepository(inflightRepository);
201            }
202    
203            ManagementStrategy managementStrategy = getBeanForType(ManagementStrategy.class);
204            if (managementStrategy != null) {
205                LOG.info("Using custom ManagementStrategy: " + managementStrategy);
206                getContext().setManagementStrategy(managementStrategy);
207            }
208    
209            EventFactory eventFactory = getBeanForType(EventFactory.class);
210            if (eventFactory != null) {
211                LOG.info("Using custom EventFactory: " + eventFactory);
212                getContext().getManagementStrategy().setEventFactory(eventFactory);
213            }
214    
215            // set the event notifier strategies if defined
216            Map<String, EventNotifier> eventNotifiers = getContext().getRegistry().lookupByType(EventNotifier.class);
217            if (eventNotifiers != null && !eventNotifiers.isEmpty()) {
218                for (String id : eventNotifiers.keySet()) {
219                    EventNotifier notifier = eventNotifiers.get(id);
220                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
221                    if (!getContext().getManagementStrategy().getEventNotifiers().contains(notifier)) {
222                        LOG.info("Using custom EventNotifier with id: " + id + " and implementation: " + notifier);
223                        getContext().getManagementStrategy().addEventNotifier(notifier);
224                    }
225                }
226            }
227    
228            ShutdownStrategy shutdownStrategy = getBeanForType(ShutdownStrategy.class);
229            if (shutdownStrategy != null) {
230                LOG.info("Using custom ShutdownStrategy: " + shutdownStrategy);
231                getContext().setShutdownStrategy(shutdownStrategy);
232            }
233    
234            // add global interceptors
235            Map<String, InterceptStrategy> interceptStrategies = getContext().getRegistry().lookupByType(InterceptStrategy.class);
236            if (interceptStrategies != null && !interceptStrategies.isEmpty()) {
237                for (String id : interceptStrategies.keySet()) {
238                    InterceptStrategy strategy = interceptStrategies.get(id);
239                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
240                    if (!getContext().getInterceptStrategies().contains(strategy)) {
241                        LOG.info("Using custom InterceptStrategy with id: " + id + " and implementation: " + strategy);
242                        getContext().addInterceptStrategy(strategy);
243                    }
244                }
245            }
246    
247            // set the lifecycle strategy if defined
248            Map<String, LifecycleStrategy> lifecycleStrategies = getContext().getRegistry().lookupByType(LifecycleStrategy.class);
249            if (lifecycleStrategies != null && !lifecycleStrategies.isEmpty()) {
250                for (String id : lifecycleStrategies.keySet()) {
251                    LifecycleStrategy strategy = lifecycleStrategies.get(id);
252                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
253                    if (!getContext().getLifecycleStrategies().contains(strategy)) {
254                        LOG.info("Using custom LifecycleStrategy with id: " + id + " and implementation: " + strategy);
255                        getContext().addLifecycleStrategy(strategy);
256                    }
257                }
258            }
259    
260            // set the default thread pool profile if defined
261            initThreadPoolProfiles(getContext());
262    
263            // Set the application context and camelContext for the beanPostProcessor
264            initBeanPostProcessor(getContext());
265    
266            initCamelContext(getContext());
267    
268            // must init route refs before we prepare the routes below
269            initRouteRefs();
270    
271            // do special preparation for some concepts such as interceptors and policies
272            // this is needed as JAXB does not build exactly the same model definition as Spring DSL would do
273            // using route builders. So we have here a little custom code to fix the JAXB gaps
274            for (RouteDefinition route : getRoutes()) {
275    
276                // abstracts is the cross cutting concerns
277                List<ProcessorDefinition> abstracts = new ArrayList<ProcessorDefinition>();
278    
279                // upper is the cross cutting concerns such as interceptors, error handlers etc
280                List<ProcessorDefinition> upper = new ArrayList<ProcessorDefinition>();
281    
282                // lower is the regular route
283                List<ProcessorDefinition> lower = new ArrayList<ProcessorDefinition>();
284    
285                prepareRouteForInit(route, abstracts, lower);
286    
287                // interceptors should be first for the cross cutting concerns
288                initInterceptors(route, upper);
289                // then on completion
290                initOnCompletions(abstracts, upper);
291                // then transactions
292                initTransacted(abstracts, lower);
293                // then on exception
294                initOnExceptions(abstracts, upper);
295    
296                // rebuild route as upper + lower
297                route.clearOutput();
298                route.getOutputs().addAll(upper);
299                route.getOutputs().addAll(lower);
300    
301                // configure parents
302                initParent(route);
303            }
304    
305            if (getDataFormats() != null) {
306                getContext().setDataFormats(getDataFormats().asMap());
307            }
308    
309            // lets force any lazy creation
310            getContext().addRouteDefinitions(getRoutes());
311    
312            if (LOG.isDebugEnabled()) {
313                LOG.debug("Found JAXB created routes: " + getRoutes());
314            }
315            findRouteBuilders();
316            installRoutes();
317        }
318    
319        protected abstract void initCustomRegistry(T context);
320    
321        private void prepareRouteForInit(RouteDefinition route, List<ProcessorDefinition> abstracts,
322                                         List<ProcessorDefinition> lower) {
323            // filter the route into abstracts and lower
324            for (ProcessorDefinition output : route.getOutputs()) {
325                if (output.isAbstract()) {
326                    abstracts.add(output);
327                } else {
328                    lower.add(output);
329                }
330            }
331        }
332    
333        private void initParent(RouteDefinition route) {
334            for (ProcessorDefinition output : route.getOutputs()) {
335                output.setParent(route);
336                if (output.getOutputs() != null) {
337                    // recursive the outputs
338                    initParent(output);
339                }
340            }
341        }
342    
343        @SuppressWarnings("unchecked")
344        private void initParent(ProcessorDefinition parent) {
345            List<ProcessorDefinition> children = parent.getOutputs();
346            for (ProcessorDefinition child : children) {
347                child.setParent(parent);
348                if (child.getOutputs() != null) {
349                    // recursive the children
350                    initParent(child);
351                }
352            }
353        }
354    
355        private void initOnExceptions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) {
356            // add global on exceptions if any
357            List<OnExceptionDefinition> onExceptions = getOnExceptions();
358            if (onExceptions != null && !onExceptions.isEmpty()) {
359                abstracts.addAll(onExceptions);
360            }
361    
362            // now add onExceptions to the route
363            for (ProcessorDefinition output : abstracts) {
364                if (output instanceof OnExceptionDefinition) {
365                    // on exceptions must be added at top, so the route flow is correct as
366                    // on exceptions should be the first outputs
367                    upper.add(0, output);
368                }
369            }
370        }
371    
372        private void initInterceptors(RouteDefinition route, List<ProcessorDefinition> upper) {
373            // configure intercept
374            for (InterceptDefinition intercept : getIntercepts()) {
375                intercept.afterPropertiesSet();
376                // add as first output so intercept is handled before the actual route and that gives
377                // us the needed head start to init and be able to intercept all the remaining processing steps
378                upper.add(0, intercept);
379            }
380    
381            // configure intercept from
382            for (InterceptFromDefinition intercept : getInterceptFroms()) {
383    
384                // should we only apply interceptor for a given endpoint uri
385                boolean match = true;
386                if (intercept.getUri() != null) {
387                    match = false;
388                    for (FromDefinition input : route.getInputs()) {
389                        if (EndpointHelper.matchEndpoint(input.getUri(), intercept.getUri())) {
390                            match = true;
391                            break;
392                        }
393                    }
394                }
395    
396                if (match) {
397                    intercept.afterPropertiesSet();
398                    // add as first output so intercept is handled before the actual route and that gives
399                    // us the needed head start to init and be able to intercept all the remaining processing steps
400                    upper.add(0, intercept);
401                }
402            }
403    
404            // configure intercept send to endpoint
405            for (InterceptSendToEndpointDefinition intercept : getInterceptSendToEndpoints()) {
406                intercept.afterPropertiesSet();
407                // add as first output so intercept is handled before the actual route and that gives
408                // us the needed head start to init and be able to intercept all the remaining processing steps
409                upper.add(0, intercept);
410            }
411        }
412    
413        private void initOnCompletions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) {
414            List<OnCompletionDefinition> completions = new ArrayList<OnCompletionDefinition>();
415    
416            // find the route scoped onCompletions
417            for (ProcessorDefinition out : abstracts) {
418                if (out instanceof OnCompletionDefinition) {
419                    completions.add((OnCompletionDefinition) out);
420                }
421            }
422    
423            // only add global onCompletion if there are no route already
424            if (completions.isEmpty()) {
425                completions = getOnCompletions();
426            }
427    
428            // are there any completions to init at all?
429            if (completions.isEmpty()) {
430                return;
431            }
432    
433            upper.addAll(completions);
434        }
435    
436        private void initTransacted(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> lower) {
437            TransactedDefinition transacted = null;
438    
439            // add to correct type
440            for (ProcessorDefinition type : abstracts) {
441                if (type instanceof TransactedDefinition) {
442                    if (transacted == null) {
443                        transacted = (TransactedDefinition) type;
444                    } else {
445                        throw new IllegalArgumentException("The route can only have one transacted defined");
446                    }
447                }
448            }
449    
450            if (transacted != null) {
451                // the outputs should be moved to the transacted policy
452                transacted.getOutputs().addAll(lower);
453                // and add it as the single output
454                lower.clear();
455                lower.add(transacted);
456            }
457        }
458    
459        private void initJMXAgent() throws Exception {
460            CamelJMXAgentDefinition camelJMXAgent = getCamelJMXAgent();
461            if (camelJMXAgent != null && camelJMXAgent.isAgentDisabled()) {
462                LOG.info("JMXAgent disabled");
463                // clear the existing lifecycle strategies define by the DefaultCamelContext constructor
464                getContext().getLifecycleStrategies().clear();
465                // no need to add a lifecycle strategy as we do not need one as JMX is disabled
466                getContext().setManagementStrategy(new DefaultManagementStrategy());
467            } else if (camelJMXAgent != null) {
468                LOG.info("JMXAgent enabled: " + camelJMXAgent);
469                DefaultManagementAgent agent = new DefaultManagementAgent(getContext());
470                agent.setConnectorPort(parseInteger(camelJMXAgent.getConnectorPort()));
471                agent.setCreateConnector(parseBoolean(camelJMXAgent.getCreateConnector()));
472                agent.setMBeanObjectDomainName(parseText(camelJMXAgent.getMbeanObjectDomainName()));
473                agent.setMBeanServerDefaultDomain(parseText(camelJMXAgent.getMbeanServerDefaultDomain()));
474                agent.setRegistryPort(parseInteger(camelJMXAgent.getRegistryPort()));
475                agent.setServiceUrlPath(parseText(camelJMXAgent.getServiceUrlPath()));
476                agent.setUsePlatformMBeanServer(parseBoolean(camelJMXAgent.getUsePlatformMBeanServer()));
477                agent.setOnlyRegisterProcessorWithCustomId(parseBoolean(camelJMXAgent.getOnlyRegisterProcessorWithCustomId()));
478    
479                ManagementStrategy managementStrategy = new ManagedManagementStrategy(agent);
480                getContext().setManagementStrategy(managementStrategy);
481    
482                // clear the existing lifecycle strategies define by the DefaultCamelContext constructor
483                getContext().getLifecycleStrategies().clear();
484                getContext().addLifecycleStrategy(new DefaultManagementLifecycleStrategy(getContext()));
485                // set additional configuration from camelJMXAgent
486                boolean onlyId = agent.getOnlyRegisterProcessorWithCustomId() != null && agent.getOnlyRegisterProcessorWithCustomId();
487                getContext().getManagementStrategy().onlyManageProcessorWithCustomId(onlyId);
488                getContext().getManagementStrategy().setStatisticsLevel(camelJMXAgent.getStatisticsLevel());
489            }
490        }
491    
492        private void initPropertyPlaceholder() throws Exception {
493            if (getCamelPropertyPlaceholder() != null) {
494                CamelPropertyPlaceholderDefinition def = getCamelPropertyPlaceholder();
495    
496                PropertiesComponent pc = new PropertiesComponent();
497                pc.setLocation(def.getLocation());
498    
499                // if using a custom resolver
500                if (ObjectHelper.isNotEmpty(def.getPropertiesResolverRef())) {
501                    PropertiesResolver resolver = CamelContextHelper.mandatoryLookup(getContext(), def.getPropertiesResolverRef(),
502                                                                                     PropertiesResolver.class);
503                    pc.setPropertiesResolver(resolver);
504                }
505    
506                // register the properties component
507                getContext().addComponent("properties", pc);
508            }
509        }
510    
511        private void initRouteRefs() throws Exception {
512            // add route refs to existing routes
513            if (getRouteRefs() != null) {
514                for (RouteContextRefDefinition ref : getRouteRefs()) {
515                    List<RouteDefinition> defs = ref.lookupRoutes(getContext());
516                    for (RouteDefinition def : defs) {
517                        if (LOG.isDebugEnabled()) {
518                            LOG.debug("Adding route from " + ref + " -> " + def);
519                        }
520                        // add in top as they are most likely to be common/shared
521                        // which you may want to start first
522                        getRoutes().add(0, def);
523                    }
524                }
525            }
526        }
527    
528        protected abstract <S> S getBeanForType(Class<S> clazz);
529    
530        public void destroy() throws Exception {
531            getContext().stop();
532        }
533    
534        private String parseText(String text) throws Exception {
535            // ensure we support property placeholders
536            return getContext().resolvePropertyPlaceholders(text);
537        }
538    
539        private Integer parseInteger(String text) throws Exception {
540            // ensure we support property placeholders
541            String s = getContext().resolvePropertyPlaceholders(text);
542            if (s != null) {
543                try {
544                    return new Integer(s);
545                } catch (NumberFormatException e) {
546                    if (s.equals(text)) {
547                        throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e);
548                    } else {
549                        throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e);
550                    }
551                }
552            }
553            return null;
554        }
555    
556        private Long parseLong(String text) throws Exception {
557            // ensure we support property placeholders
558            String s = getContext().resolvePropertyPlaceholders(text);
559            if (s != null) {
560                try {
561                    return new Long(s);
562                } catch (NumberFormatException e) {
563                    if (s.equals(text)) {
564                        throw new IllegalArgumentException("Error parsing [" + s + "] as a Long.", e);
565                    } else {
566                        throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Long.", e);
567                    }
568                }
569            }
570            return null;
571        }
572    
573        private Boolean parseBoolean(String text) throws Exception {
574            // ensure we support property placeholders
575            String s = getContext().resolvePropertyPlaceholders(text);
576            if (s != null) {
577                s = s.trim().toLowerCase();
578                if (s.equals("true") || s.equals("false")) {
579                    return new Boolean(s);
580                } else {
581                    if (s.equals(text)) {
582                        throw new IllegalArgumentException("Error parsing [" + s + "] as a Boolean.");
583                    } else {
584                        throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Boolean.");
585                    }
586                }
587            }
588            return null;
589        }
590    
591        // Properties
592        // -------------------------------------------------------------------------
593        public T getContext() {
594            return getContext(true);
595        }
596    
597        public abstract T getContext(boolean create);
598    
599        public abstract List<RouteDefinition> getRoutes();
600    
601        public abstract List<InterceptDefinition> getIntercepts();
602    
603        public abstract List<InterceptFromDefinition> getInterceptFroms();
604    
605        public abstract List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints();
606    
607        public abstract PropertiesDefinition getProperties();
608    
609        public abstract String[] getPackages();
610    
611        public abstract PackageScanDefinition getPackageScan();
612    
613        public abstract void setPackageScan(PackageScanDefinition packageScan);
614    
615        public abstract ContextScanDefinition getContextScan();
616    
617        public abstract void setContextScan(ContextScanDefinition contextScan);
618    
619        public abstract CamelPropertyPlaceholderDefinition getCamelPropertyPlaceholder();
620    
621        public abstract String getTrace();
622    
623        public abstract String getStreamCache();
624    
625        public abstract String getDelayer();
626    
627        public abstract String getHandleFault();
628    
629        public abstract String getAutoStartup();
630    
631        public abstract CamelJMXAgentDefinition getCamelJMXAgent();
632    
633        public abstract List<RouteBuilderDefinition> getBuilderRefs();
634    
635        public abstract List<RouteContextRefDefinition> getRouteRefs();
636    
637        public abstract String getErrorHandlerRef();
638    
639        public abstract DataFormatsDefinition getDataFormats();
640    
641        public abstract List<OnExceptionDefinition> getOnExceptions();
642    
643        public abstract List<OnCompletionDefinition> getOnCompletions();
644    
645        public abstract ShutdownRoute getShutdownRoute();
646    
647        public abstract ShutdownRunningTask getShutdownRunningTask();
648    
649        public abstract List<ThreadPoolProfileDefinition> getThreadPoolProfiles();
650    
651        public abstract String getDependsOn();
652    
653        // Implementation methods
654        // -------------------------------------------------------------------------
655    
656        /**
657         * Initializes the context
658         *
659         * @param ctx the context
660         * @throws Exception is thrown if error occurred
661         */
662        protected void initCamelContext(T ctx) throws Exception {
663            if (getStreamCache() != null) {
664                ctx.setStreamCaching(parseBoolean(getStreamCache()));
665            }
666            if (getTrace() != null) {
667                ctx.setTracing(parseBoolean(getTrace()));
668            }
669            if (getDelayer() != null) {
670                ctx.setDelayer(parseLong(getDelayer()));
671            }
672            if (getHandleFault() != null) {
673                ctx.setHandleFault(parseBoolean(getHandleFault()));
674            }
675            if (getErrorHandlerRef() != null) {
676                ctx.setErrorHandlerBuilder(new ErrorHandlerBuilderRef(getErrorHandlerRef()));
677            }
678            if (getAutoStartup() != null) {
679                ctx.setAutoStartup(parseBoolean(getAutoStartup()));
680            }
681            if (getShutdownRoute() != null) {
682                ctx.setShutdownRoute(getShutdownRoute());
683            }
684            if (getShutdownRunningTask() != null) {
685                ctx.setShutdownRunningTask(getShutdownRunningTask());
686            }
687        }
688    
689        private void initThreadPoolProfiles(T context) {
690            Set<String> defaultIds = new HashSet<String>();
691    
692            // lookup and use custom profiles from the registry
693            Map<String, ThreadPoolProfile> profiles = context.getRegistry().lookupByType(ThreadPoolProfile.class);
694            if (profiles != null && !profiles.isEmpty()) {
695                for (String id : profiles.keySet()) {
696                    ThreadPoolProfile profile = profiles.get(id);
697                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
698                    if (profile.isDefaultProfile()) {
699                        LOG.info("Using custom default ThreadPoolProfile with id: " + id + " and implementation: " + profile);
700                        context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile);
701                        defaultIds.add(id);
702                    } else {
703                        context.getExecutorServiceStrategy().registerThreadPoolProfile(profile);
704                    }
705                }
706            }
707    
708            // use custom profiles defined in the CamelContext
709            if (getThreadPoolProfiles() != null && !getThreadPoolProfiles().isEmpty()) {
710                for (ThreadPoolProfileDefinition profile : getThreadPoolProfiles()) {
711                    if (profile.isDefaultProfile()) {
712                        LOG.info("Using custom default ThreadPoolProfile with id: " + profile.getId() + " and implementation: " + profile);
713                        context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile);
714                        defaultIds.add(profile.getId());
715                    } else {
716                        context.getExecutorServiceStrategy().registerThreadPoolProfile(profile);
717                    }
718                }
719            }
720    
721            // validate at most one is defined
722            if (defaultIds.size() > 1) {
723                throw new IllegalArgumentException("Only exactly one default ThreadPoolProfile is allowed, was " + defaultIds.size() + " ids: " + defaultIds);
724            }
725        }
726    
727        protected abstract void initBeanPostProcessor(T context);
728    
729        /**
730         * Strategy to install all available routes into the context
731         */
732        protected void installRoutes() throws Exception {
733            List<RouteBuilder> builders = new ArrayList<RouteBuilder>();
734    
735            // lets add route builders added from references
736            if (getBuilderRefs() != null) {
737                for (RouteBuilderDefinition builderRef : getBuilderRefs()) {
738                    RouteBuilder builder = builderRef.createRouteBuilder(getContext());
739                    if (builder != null) {
740                        builders.add(builder);
741                    } else {
742                        // support to get the route here
743                        RoutesBuilder routes = builderRef.createRoutes(getContext());
744                        if (routes != null) {
745                            this.builders.add(routes);
746                        } else {
747                            // Throw the exception that we can't find any build here
748                            throw new CamelException("Cannot find any routes with this RouteBuilder reference: " + builderRef);
749                        }
750                    }
751                }
752            }
753    
754            // install already configured routes
755            for (RoutesBuilder routeBuilder : this.builders) {
756                getContext().addRoutes(routeBuilder);
757            }
758    
759            // install builders
760            for (RouteBuilder builder : builders) {
761                // Inject the annotated resource
762                postProcessBeforeInit(builder);
763                getContext().addRoutes(builder);
764            }
765        }
766    
767        protected abstract void postProcessBeforeInit(RouteBuilder builder);
768    
769        /**
770         * Strategy method to try find {@link org.apache.camel.builder.RouteBuilder} instances on the classpath
771         */
772        protected void findRouteBuilders() throws Exception {
773            // package scan
774            addPackageElementContentsToScanDefinition();
775            PackageScanDefinition packageScanDef = getPackageScan();
776            if (packageScanDef != null && packageScanDef.getPackages().size() > 0) {
777                // use package scan filter
778                PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter();
779                // support property placeholders in include and exclude
780                for (String include : packageScanDef.getIncludes()) {
781                    include = getContext().resolvePropertyPlaceholders(include);
782                    filter.addIncludePattern(include);
783                }
784                for (String exclude : packageScanDef.getExcludes()) {
785                    exclude = getContext().resolvePropertyPlaceholders(exclude);
786                    filter.addExcludePattern(exclude);
787                }
788    
789                String[] normalized = normalizePackages(getContext(), packageScanDef.getPackages());
790                findRouteBuildersByPackageScan(normalized, filter, builders);
791            }
792    
793            // context scan
794            ContextScanDefinition contextScanDef = getContextScan();
795            if (contextScanDef != null) {
796                // use package scan filter
797                PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter();
798                // support property placeholders in include and exclude
799                for (String include : contextScanDef.getIncludes()) {
800                    include = getContext().resolvePropertyPlaceholders(include);
801                    filter.addIncludePattern(include);
802                }
803                for (String exclude : contextScanDef.getExcludes()) {
804                    exclude = getContext().resolvePropertyPlaceholders(exclude);
805                    filter.addExcludePattern(exclude);
806                }
807                findRouteBuildersByContextScan(filter, builders);
808            }
809        }
810    
811        protected abstract void findRouteBuildersByPackageScan(String[] packages, PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception;
812    
813        protected abstract void findRouteBuildersByContextScan(PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception;
814    
815        private void addPackageElementContentsToScanDefinition() {
816            PackageScanDefinition packageScanDef = getPackageScan();
817    
818            if (getPackages() != null && getPackages().length > 0) {
819                if (packageScanDef == null) {
820                    packageScanDef = new PackageScanDefinition();
821                    setPackageScan(packageScanDef);
822                }
823    
824                for (String pkg : getPackages()) {
825                    packageScanDef.getPackages().add(pkg);
826                }
827            }
828        }
829    
830        private String[] normalizePackages(T context, List<String> unnormalized) throws Exception {
831            List<String> packages = new ArrayList<String>();
832            for (String name : unnormalized) {
833                // it may use property placeholders
834                name = context.resolvePropertyPlaceholders(name);
835                name = ObjectHelper.normalizeClassName(name);
836                if (ObjectHelper.isNotEmpty(name)) {
837                    if (LOG.isTraceEnabled()) {
838                        LOG.trace("Using package: " + name + " to scan for RouteBuilder classes");
839                    }
840                    packages.add(name);
841                }
842            }
843            return packages.toArray(new String[packages.size()]);
844        }
845    
846    }