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.spring;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    import java.util.Map;
022    import javax.xml.bind.annotation.XmlAccessType;
023    import javax.xml.bind.annotation.XmlAccessorType;
024    import javax.xml.bind.annotation.XmlAttribute;
025    import javax.xml.bind.annotation.XmlElement;
026    import javax.xml.bind.annotation.XmlElements;
027    import javax.xml.bind.annotation.XmlRootElement;
028    import javax.xml.bind.annotation.XmlTransient;
029    
030    import org.apache.camel.CamelException;
031    import org.apache.camel.RoutesBuilder;
032    import org.apache.camel.builder.ErrorHandlerBuilder;
033    import org.apache.camel.builder.RouteBuilder;
034    import org.apache.camel.impl.DefaultLifecycleStrategy;
035    import org.apache.camel.impl.scan.PatternBasedPackageScanFilter;
036    import org.apache.camel.management.DefaultInstrumentationAgent;
037    import org.apache.camel.management.InstrumentationLifecycleStrategy;
038    import org.apache.camel.model.FromDefinition;
039    import org.apache.camel.model.IdentifiedType;
040    import org.apache.camel.model.InterceptDefinition;
041    import org.apache.camel.model.InterceptFromDefinition;
042    import org.apache.camel.model.InterceptSendToEndpointDefinition;
043    import org.apache.camel.model.OnCompletionDefinition;
044    import org.apache.camel.model.OnExceptionDefinition;
045    import org.apache.camel.model.PackageScanDefinition;
046    import org.apache.camel.model.PolicyDefinition;
047    import org.apache.camel.model.ProcessorDefinition;
048    import org.apache.camel.model.RouteBuilderDefinition;
049    import org.apache.camel.model.RouteContainer;
050    import org.apache.camel.model.RouteDefinition;
051    import org.apache.camel.model.TransactedDefinition;
052    import org.apache.camel.model.config.PropertiesDefinition;
053    import org.apache.camel.model.dataformat.DataFormatsDefinition;
054    import org.apache.camel.processor.interceptor.Delayer;
055    import org.apache.camel.processor.interceptor.HandleFault;
056    import org.apache.camel.processor.interceptor.TraceFormatter;
057    import org.apache.camel.processor.interceptor.Tracer;
058    import org.apache.camel.spi.ClassResolver;
059    import org.apache.camel.spi.FactoryFinderResolver;
060    import org.apache.camel.spi.InterceptStrategy;
061    import org.apache.camel.spi.LifecycleStrategy;
062    import org.apache.camel.spi.PackageScanClassResolver;
063    import org.apache.camel.spi.Registry;
064    import org.apache.camel.util.EndpointHelper;
065    import org.apache.camel.util.ObjectHelper;
066    import org.apache.commons.logging.Log;
067    import org.apache.commons.logging.LogFactory;
068    import org.springframework.beans.factory.DisposableBean;
069    import org.springframework.beans.factory.FactoryBean;
070    import org.springframework.beans.factory.InitializingBean;
071    import org.springframework.beans.factory.config.BeanPostProcessor;
072    import org.springframework.context.ApplicationContext;
073    import org.springframework.context.ApplicationContextAware;
074    import org.springframework.context.ApplicationEvent;
075    import org.springframework.context.ApplicationListener;
076    import org.springframework.context.event.ContextRefreshedEvent;
077    import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
078    
079    /**
080     * A Spring {@link FactoryBean} to create and initialize a
081     * {@link SpringCamelContext} and install routes either explicitly configured in
082     * Spring XML or found by searching the classpath for Java classes which extend
083     * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
084     *
085     * @version $Revision: 792468 $
086     */
087    @XmlRootElement(name = "camelContext")
088    @XmlAccessorType(XmlAccessType.FIELD)
089    public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener {
090        private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class);
091    
092        @XmlAttribute(required = false)
093        private Boolean trace;
094        @XmlAttribute(required = false)
095        private Boolean streamCache = Boolean.TRUE;
096        @XmlAttribute(required = false)
097        private Long delay;
098        @XmlAttribute(required = false)
099        private Boolean handleFault;
100        @XmlAttribute(required = false)
101        private String errorHandlerRef;
102        @XmlAttribute(required = false)
103        private Boolean shouldStartContext = Boolean.TRUE;
104        @XmlElement(name = "properties", required = false)
105        private PropertiesDefinition properties;
106        @XmlElement(name = "package", required = false)
107        private String[] packages = {};
108        @XmlElement(name = "packageScan", type = PackageScanDefinition.class, required = false)
109        private PackageScanDefinition packageScan;
110        @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class, required = false)
111        private CamelJMXAgentDefinition camelJMXAgent;    
112        @XmlElements({
113            @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false),
114            @XmlElement(name = "template", type = CamelProducerTemplateFactoryBean.class, required = false),
115            @XmlElement(name = "consumerTemplate", type = CamelConsumerTemplateFactoryBean.class, required = false),
116            @XmlElement(name = "proxy", type = CamelProxyFactoryDefinition.class, required = false),
117            @XmlElement(name = "export", type = CamelServiceExporterDefinition.class, required = false)})
118        private List beans;    
119        @XmlElement(name = "routeBuilder", required = false)
120        private List<RouteBuilderDefinition> builderRefs = new ArrayList<RouteBuilderDefinition>();
121        @XmlElement(name = "endpoint", required = false)
122        private List<CamelEndpointFactoryBean> endpoints;
123        @XmlElement(name = "dataFormats", required = false)
124        private DataFormatsDefinition dataFormats;
125        @XmlElement(name = "onException", required = false)
126        private List<OnExceptionDefinition> onExceptions = new ArrayList<OnExceptionDefinition>();
127        @XmlElement(name = "onCompletion", required = false)
128        private List<OnCompletionDefinition> onCompletions = new ArrayList<OnCompletionDefinition>();
129        @XmlElement(name = "intercept", required = false)
130        private List<InterceptDefinition> intercepts = new ArrayList<InterceptDefinition>();
131        @XmlElement(name = "interceptFrom", required = false)
132        private List<InterceptFromDefinition> interceptFroms = new ArrayList<InterceptFromDefinition>();
133        @XmlElement(name = "interceptSendToEndpoint", required = false)
134        private List<InterceptSendToEndpointDefinition> interceptSendToEndpoints = new ArrayList<InterceptSendToEndpointDefinition>();
135        @XmlElement(name = "route", required = false)
136        private List<RouteDefinition> routes = new ArrayList<RouteDefinition>();    
137        @XmlTransient
138        private SpringCamelContext context;
139        @XmlTransient
140        private RouteBuilder routeBuilder;
141        @XmlTransient
142        private List<RoutesBuilder> additionalBuilders = new ArrayList<RoutesBuilder>();
143        @XmlTransient
144        private ApplicationContext applicationContext;
145        @XmlTransient
146        private ClassLoader contextClassLoaderOnStart;
147        @XmlTransient
148        private BeanPostProcessor beanPostProcessor;
149    
150        public CamelContextFactoryBean() {
151            // Lets keep track of the class loader for when we actually do start things up
152            contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader();
153        }
154    
155        public Object getObject() throws Exception {
156            return getContext();
157        }
158    
159        public Class getObjectType() {
160            return SpringCamelContext.class;
161        }
162    
163        public boolean isSingleton() {
164            return true;
165        }
166        
167        public ClassLoader getContextClassLoaderOnStart() {
168            return contextClassLoaderOnStart;
169        }
170        
171        public List<RoutesBuilder> getAdditionalBuilders() {
172            return additionalBuilders;
173        }
174    
175        public void afterPropertiesSet() throws Exception {
176            if (properties != null) {
177                getContext().setProperties(properties.asMap());
178            }
179    
180            // set the resolvers first
181            PackageScanClassResolver packageResolver = getBeanForType(PackageScanClassResolver.class);
182            if (packageResolver != null) {
183                LOG.info("Using custom PackageScanClassResolver: " + packageResolver);
184                getContext().setPackageScanClassResolver(packageResolver);
185            }
186            ClassResolver classResolver = getBeanForType(ClassResolver.class);
187            if (classResolver != null) {
188                LOG.info("Using custom ClassResolver: " + classResolver);
189                getContext().setClassResolver(classResolver);
190            }
191            FactoryFinderResolver factoryFinderResolver = getBeanForType(FactoryFinderResolver.class);
192            if (factoryFinderResolver != null) {
193                LOG.info("Using custom FactoryFinderResolver: " + factoryFinderResolver);
194                getContext().setFactoryFinderResolver(factoryFinderResolver);
195            }
196    
197            // set the lifecycle strategy if defined
198            LifecycleStrategy lifecycleStrategy = getBeanForType(LifecycleStrategy.class);
199            if (lifecycleStrategy != null) {
200                LOG.info("Using custom LifecycleStrategy: " + lifecycleStrategy);
201                getContext().setLifecycleStrategy(lifecycleStrategy);
202            }
203    
204            // set the strategy if defined
205            Registry registry = getBeanForType(Registry.class);
206            if (registry != null) {
207                LOG.info("Using custom Registry: " + registry);
208                getContext().setRegistry(registry);
209            }
210    
211            Tracer tracer = getBeanForType(Tracer.class);
212            if (tracer != null) {
213                // use formatter if there is a TraceFormatter bean defined
214                TraceFormatter formatter = getBeanForType(TraceFormatter.class);
215                if (formatter != null) {
216                    tracer.setFormatter(formatter);
217                }
218                LOG.info("Using custom Tracer: " + tracer);
219                getContext().addInterceptStrategy(tracer);
220            }
221    
222            HandleFault handleFault = getBeanForType(HandleFault.class);
223            if (handleFault != null) {
224                LOG.info("Using custom HandleFault: " + handleFault);
225                getContext().addInterceptStrategy(handleFault);
226            }
227    
228            Delayer delayer = getBeanForType(Delayer.class);
229            if (delayer != null) {
230                LOG.info("Using custom Delayer: " + delayer);
231                getContext().addInterceptStrategy(delayer);
232            }
233    
234            // add global interceptors
235            Map<String, InterceptStrategy> strategies = getContext().getRegistry().lookupByType(InterceptStrategy.class);
236            if (strategies != null && !strategies.isEmpty()) {
237                for (String id : strategies.keySet()) {
238                    InterceptStrategy strategy = strategies.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 intercept strategy with id: " + id + " and implementation: " + strategy);
242                        getContext().addInterceptStrategy(strategy);
243                    }
244                }
245            }
246    
247            // Set the application context and camelContext for the beanPostProcessor
248            if (beanPostProcessor != null) {
249                if (beanPostProcessor instanceof ApplicationContextAware) {
250                    ((ApplicationContextAware)beanPostProcessor).setApplicationContext(applicationContext);
251                }
252                if (beanPostProcessor instanceof CamelBeanPostProcessor) {
253                    ((CamelBeanPostProcessor)beanPostProcessor).setCamelContext(getContext());
254                }
255            }
256    
257            // do special preparation for some concepts such as interceptors and policies
258            // this is needed as JAXB does not build excaclty the same model definition as Spring DSL would do
259            // using route builders. So we have here a little custom code to fix the JAXB gaps
260            for (RouteDefinition route : routes) {
261                // interceptors should be first
262                initInterceptors(route);
263                // then on completion
264                initOnCompletions(route);
265                // then polices
266                initPolicies(route);
267                // and last on exception
268                initOnExceptions(route);
269            }
270    
271            if (dataFormats != null) {
272                getContext().setDataFormats(dataFormats.asMap());
273            } 
274            
275            // lets force any lazy creation
276            getContext().addRouteDefinitions(routes);
277    
278            // setup JMX agent
279            initJMXAgent();
280    
281            if (LOG.isDebugEnabled()) {
282                LOG.debug("Found JAXB created routes: " + getRoutes());
283            }
284            findRouteBuilders();
285            installRoutes();
286        }
287    
288        private void initOnExceptions(RouteDefinition route) {
289            List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>();
290            List<ProcessorDefinition<?>> exceptionHandlers = new ArrayList<ProcessorDefinition<?>>();
291    
292            // add global on exceptions if any
293            if (onExceptions != null && !onExceptions.isEmpty()) {
294                // on exceptions must be added at top, so the route flow is correct as
295                // on exceptions should be the first outputs
296                route.getOutputs().addAll(0, onExceptions);
297            }
298    
299            for (ProcessorDefinition output : route.getOutputs()) {
300                // split into on exception and regular outputs
301                if (output instanceof OnExceptionDefinition) {
302                    exceptionHandlers.add(output);
303                } else {
304                    outputs.add(output);
305                }
306            }
307    
308            // clearing the outputs
309            route.clearOutput();
310    
311            // add exception handlers as top children
312            route.getOutputs().addAll(exceptionHandlers);
313    
314            // and the remaining outputs
315            route.getOutputs().addAll(outputs);
316        }
317    
318        private void initInterceptors(RouteDefinition route) {
319    
320            // configure intercept
321            for (InterceptDefinition intercept : getIntercepts()) {
322                intercept.afterPropertiesSet();
323                // add as first output so intercept is handled before the acutal route and that gives
324                // us the needed head start to init and be able to intercept all the remaining processing steps
325                route.getOutputs().add(0, intercept);
326            }
327    
328            // configure intercept from
329            for (InterceptFromDefinition intercept : getInterceptFroms()) {
330    
331                // should we only apply interceptor for a given endpoint uri
332                boolean match = true;
333                if (intercept.getUri() != null) {
334                    match = false;
335                    for (FromDefinition input : route.getInputs()) {
336                        if (EndpointHelper.matchEndpoint(input.getUri(), intercept.getUri())) {
337                            match = true;
338                            break;
339                        }
340                    }
341                }
342    
343                if (match) {
344                    intercept.afterPropertiesSet();
345                    // add as first output so intercept is handled before the acutal route and that gives
346                    // us the needed head start to init and be able to intercept all the remaining processing steps
347                    route.getOutputs().add(0, intercept);
348                }
349            }
350    
351            // configure intercept send to endpoint
352            for (InterceptSendToEndpointDefinition intercept : getInterceptSendToEndpoints()) {
353                intercept.afterPropertiesSet();
354                // add as first output so intercept is handled before the acutal route and that gives
355                // us the needed head start to init and be able to intercept all the remaining processing steps
356                route.getOutputs().add(0, intercept);
357            }
358    
359        }
360    
361        private void initOnCompletions(RouteDefinition route) {
362            // only add global onCompletion if there are no route alredy
363            boolean hasRouteScope = false;
364            for (ProcessorDefinition out : route.getOutputs()) {
365                if (out instanceof OnCompletionDefinition) {
366                    hasRouteScope = true;
367                    break;
368                }
369            }
370            // only add global onCompletion if we do *not* have any route onCompletion defined in the route
371            // add onCompletion *after* intercept, as its important intercept is first 
372            if (!hasRouteScope) {
373                int index = 0;
374                for (int i = 0; i < route.getOutputs().size(); i++) {
375                    index = i;
376                    ProcessorDefinition out = route.getOutputs().get(i);
377                    if (!(out instanceof InterceptDefinition)) {
378                        break;
379                    }
380                }
381                route.getOutputs().addAll(index, getOnCompletions());
382            }
383        }
384    
385        private void initPolicies(RouteDefinition route) {
386            // setup the policies as JAXB yet again have not created a correct model for us
387            List<ProcessorDefinition> types = route.getOutputs();
388            // we need to types as transacted cannot extend policy due JAXB limitations
389            PolicyDefinition policy = null;
390            TransactedDefinition transacted = null;
391            for (ProcessorDefinition type : types) {
392                if (type instanceof PolicyDefinition) {
393                    policy = (PolicyDefinition) type;
394                } else if (type instanceof TransactedDefinition) {
395                    transacted = (TransactedDefinition) type;
396                } else if (policy != null) {
397                    // the outputs should be moved to the policy
398                    policy.addOutput(type);
399                } else if (transacted != null) {
400                    // the outputs should be moved to the transacted policy
401                    transacted.addOutput(type);
402                }
403            }
404            // did we find a policy if so replace it as the only output on the route
405            if (policy != null) {
406                route.clearOutput();
407                route.addOutput(policy);
408            } else if (transacted != null) {
409                route.clearOutput();
410                route.addOutput(transacted);
411            }
412        }
413    
414        private void initJMXAgent() throws Exception {
415            if (camelJMXAgent != null && camelJMXAgent.isDisabled()) {
416                LOG.info("JMXAgent disabled");
417                getContext().setLifecycleStrategy(new DefaultLifecycleStrategy());
418            } else if (camelJMXAgent != null) {
419                DefaultInstrumentationAgent agent = new DefaultInstrumentationAgent();
420                agent.setConnectorPort(camelJMXAgent.getConnectorPort());
421                agent.setCreateConnector(camelJMXAgent.isCreateConnector());
422                agent.setMBeanObjectDomainName(camelJMXAgent.getMbeanObjectDomainName());
423                agent.setMBeanServerDefaultDomain(camelJMXAgent.getMbeanServerDefaultDomain());
424                agent.setRegistryPort(camelJMXAgent.getRegistryPort());
425                agent.setServiceUrlPath(camelJMXAgent.getServiceUrlPath());
426                agent.setUsePlatformMBeanServer(camelJMXAgent.isUsePlatformMBeanServer());
427                agent.setOnlyRegisterProcessorWithCustomId(camelJMXAgent.getOnlyRegisterProcessorWithCustomId());
428    
429                LOG.info("JMXAgent enabled: " + camelJMXAgent);
430                getContext().setLifecycleStrategy(new InstrumentationLifecycleStrategy(agent));
431            }
432        }
433    
434        @SuppressWarnings("unchecked")
435        private <T> T getBeanForType(Class<T> clazz) {
436            T bean = null;
437            String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true);
438            if (names.length == 1) {
439                bean = (T) getApplicationContext().getBean(names[0], clazz);
440            }
441            if (bean == null) {
442                ApplicationContext parentContext = getApplicationContext().getParent();
443                if (parentContext != null) {
444                    names = parentContext.getBeanNamesForType(clazz, true, true);
445                    if (names.length == 1) {
446                        bean = (T) parentContext.getBean(names[0], clazz);
447                    }
448                }
449            }
450            return bean;
451    
452        }
453    
454        public void destroy() throws Exception {
455            getContext().stop();
456        }
457    
458        public void onApplicationEvent(ApplicationEvent event) {
459            if (context != null) {
460                // let the spring camel context handle the events
461                context.onApplicationEvent(event);
462            } else {
463                if (LOG.isDebugEnabled()) {
464                    LOG.debug("Publishing spring-event: " + event);
465                }
466    
467                if (event instanceof ContextRefreshedEvent) {
468                    // now lets start the CamelContext so that all its possible
469                    // dependencies are initialized
470                    try {
471                        LOG.debug("Starting the context now!");
472                        getContext().start();
473                    } catch (Exception e) {
474                        throw wrapRuntimeCamelException(e);
475                    }
476                }
477            }
478        }
479    
480        // Properties
481        // -------------------------------------------------------------------------
482        public SpringCamelContext getContext() throws Exception {
483            if (context == null) {
484                context = createContext();
485            }
486            return context;
487        }
488    
489        public void setContext(SpringCamelContext context) {
490            this.context = context;
491        }
492    
493        public List<RouteDefinition> getRoutes() {
494            return routes;
495        }
496    
497        public void setRoutes(List<RouteDefinition> routes) {
498            this.routes = routes;
499        }
500    
501        public List<InterceptDefinition> getIntercepts() {
502            return intercepts;
503        }
504    
505        public void setIntercepts(List<InterceptDefinition> intercepts) {
506            this.intercepts = intercepts;
507        }
508    
509        public List<InterceptFromDefinition> getInterceptFroms() {
510            return interceptFroms;
511        }
512    
513        public void setInterceptFroms(List<InterceptFromDefinition> interceptFroms) {
514            this.interceptFroms = interceptFroms;
515        }
516    
517        public List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints() {
518            return interceptSendToEndpoints;
519        }
520    
521        public void setInterceptSendToEndpoints(List<InterceptSendToEndpointDefinition> interceptSendToEndpoints) {
522            this.interceptSendToEndpoints = interceptSendToEndpoints;
523        }
524    
525        public RouteBuilder getRouteBuilder() {
526            return routeBuilder;
527        }
528    
529        /**
530         * Set a single {@link RouteBuilder} to be used to create the default routes
531         * on startup
532         */
533        public void setRouteBuilder(RouteBuilder routeBuilder) {
534            this.routeBuilder = routeBuilder;
535        }
536    
537        /**
538         * Set a collection of {@link RouteBuilder} instances to be used to create
539         * the default routes on startup
540         */
541        public void setRouteBuilders(RouteBuilder[] builders) {
542            for (RouteBuilder builder : builders) {
543                additionalBuilders.add(builder);
544            }
545        }
546    
547        public ApplicationContext getApplicationContext() {
548            if (applicationContext == null) {
549                throw new IllegalArgumentException("No applicationContext has been injected!");
550            }
551            return applicationContext;
552        }
553    
554        public void setApplicationContext(ApplicationContext applicationContext) {
555            this.applicationContext = applicationContext;
556        }
557        
558        public PropertiesDefinition getProperties() {
559            return properties;
560        }
561        
562        public void setProperties(PropertiesDefinition properties) {        
563            this.properties = properties;
564        }
565    
566        /**
567         * @deprecated replaced by {@link #getPackageScan()}
568         */
569        @Deprecated
570        public String[] getPackages() {
571            return packages;
572        }
573    
574        /**
575         * Sets the package names to be recursively searched for Java classes which
576         * extend {@link RouteBuilder} to be auto-wired up to the
577         * {@link SpringCamelContext} as a route. Note that classes are excluded if
578         * they are specifically configured in the spring.xml
579         * 
580         * @deprecated replaced by {@link #setPackageScan(org.apache.camel.model.PackageScanDefinition)}
581         * @param packages the package names which are recursively searched
582         */
583        @Deprecated
584        public void setPackages(String[] packages) {
585            this.packages = packages;
586        }
587    
588        public PackageScanDefinition getPackageScan() {
589            return packageScan;
590        }
591    
592        /**
593         * Sets the package scanning information. Package scanning allows for the
594         * automatic discovery of certain camel classes at runtime for inclusion
595         * e.g. {@link RouteBuilder} implementations
596         * 
597         * @param packageScan the package scan
598         */
599        public void setPackageScan(PackageScanDefinition packageScan) {
600            this.packageScan = packageScan;
601        }
602    
603        public void setBeanPostProcessor(BeanPostProcessor postProcessor) {
604            this.beanPostProcessor = postProcessor;
605        }
606    
607        public BeanPostProcessor getBeanPostProcessor() {
608            return beanPostProcessor;
609        }
610    
611        public void setCamelJMXAgent(CamelJMXAgentDefinition agent) {
612            camelJMXAgent = agent;
613        }
614    
615        public Boolean getTrace() {
616            return trace;
617        }
618    
619        public void setTrace(Boolean trace) {
620            this.trace = trace;
621        }
622    
623        public Boolean getStreamCache() {
624            return streamCache;
625        }
626    
627        public void setStreamCache(Boolean streamCache) {
628            this.streamCache = streamCache;
629        }
630    
631        public Long getDelay() {
632            return delay;
633        }
634    
635        public void setDelay(Long delay) {
636            this.delay = delay;
637        }
638    
639        public Boolean getHandleFault() {
640            return handleFault;
641        }
642    
643        public void setHandleFault(Boolean handleFault) {
644            this.handleFault = handleFault;
645        }
646    
647        public CamelJMXAgentDefinition getCamelJMXAgent() {
648            return camelJMXAgent;
649        }
650    
651        public List<RouteBuilderDefinition> getBuilderRefs() {
652            return builderRefs;
653        }
654    
655        public void setBuilderRefs(List<RouteBuilderDefinition> builderRefs) {
656            this.builderRefs = builderRefs;
657        }
658    
659        public String getErrorHandlerRef() {
660            return errorHandlerRef;
661        }
662    
663        /**
664         * Sets the name of the error handler object used to default the error handling strategy
665         *
666         * @param errorHandlerRef the Spring bean ref of the error handler
667         */
668        public void setErrorHandlerRef(String errorHandlerRef) {
669            this.errorHandlerRef = errorHandlerRef;
670        }
671    
672        public Boolean getShouldStartContext() {
673            return shouldStartContext;
674        }
675    
676        public void setShouldStartContext(Boolean shouldStartContext) {
677            this.shouldStartContext = shouldStartContext;
678        }
679    
680        public void setDataFormats(DataFormatsDefinition dataFormats) {
681            this.dataFormats = dataFormats;
682        }
683    
684        public DataFormatsDefinition getDataFormats() {
685            return dataFormats;
686        }
687    
688        public void setOnExceptions(List<OnExceptionDefinition> onExceptions) {
689            this.onExceptions = onExceptions;
690        }
691    
692        public List<OnExceptionDefinition> getOnExceptions() {
693            return onExceptions;
694        }
695    
696        public List<OnCompletionDefinition> getOnCompletions() {
697            return onCompletions;
698        }
699    
700        public void setOnCompletions(List<OnCompletionDefinition> onCompletions) {
701            this.onCompletions = onCompletions;
702        }
703    
704        // Implementation methods
705        // -------------------------------------------------------------------------
706    
707        /**
708         * Create the context
709         */
710        protected SpringCamelContext createContext() {
711            SpringCamelContext ctx = new SpringCamelContext(getApplicationContext());
712            ctx.setName(getId());
713            if (streamCache != null) {
714                ctx.setStreamCaching(streamCache);
715            }
716            if (trace != null) {
717                ctx.setTrace(trace);
718            }
719            if (delay != null) {
720                ctx.setDelay(delay);
721            }
722            if (handleFault != null) {
723                ctx.setHandleFault(handleFault);
724            }
725            if (errorHandlerRef != null) {
726                ErrorHandlerBuilder errorHandlerBuilder = (ErrorHandlerBuilder) getApplicationContext().getBean(errorHandlerRef, ErrorHandlerBuilder.class);
727                if (errorHandlerBuilder == null) {
728                    throw new IllegalArgumentException("Cannot find ErrorHandlerBuilder bean with id: " + errorHandlerRef);
729                }
730                ctx.setErrorHandlerBuilder(errorHandlerBuilder);
731            }
732    
733            if (shouldStartContext != null) {
734                ctx.setShouldStartContext(shouldStartContext);
735            }
736    
737            return ctx;
738        }
739    
740        /**
741         * Strategy to install all available routes into the context
742         */
743        @SuppressWarnings("unchecked")
744        protected void installRoutes() throws Exception {
745            List<RouteBuilder> builders = new ArrayList<RouteBuilder>();
746    
747            if (routeBuilder != null) {
748                builders.add(routeBuilder);
749            }
750    
751            // lets add route builders added from references
752            if (builderRefs != null) {
753                for (RouteBuilderDefinition builderRef : builderRefs) {
754                    RouteBuilder builder = builderRef.createRouteBuilder(getContext());
755                    if (builder != null) {
756                        builders.add(builder);
757                    } else {
758                        // support to get the route here
759                        RoutesBuilder routes = builderRef.createRoutes(getContext());
760                        if (routes != null) {
761                            additionalBuilders.add(routes);
762                        } else {
763                            // Throw the exception that we can't find any build here
764                            throw new CamelException("Cannot find any routes with this RouteBuilder reference: " + builderRef);
765                        }
766                    }
767                    
768                }
769            }
770    
771            // install already configured routes
772            for (RoutesBuilder routeBuilder : additionalBuilders) {
773                getContext().addRoutes(routeBuilder);
774            }
775    
776            // install builders
777            for (RouteBuilder builder : builders) {
778                if (beanPostProcessor != null) {
779                    // Inject the annotated resource
780                    beanPostProcessor.postProcessBeforeInitialization(builder, builder.toString());
781                }
782                getContext().addRoutes(builder);
783            }
784        }
785    
786        /**
787         * Strategy method to try find {@link RouteBuilder} instances on the classpath
788         */
789        protected void findRouteBuilders() throws Exception {
790    
791            PackageScanClassResolver resolver = getContext().getPackageScanClassResolver();
792            addPackageElementContentsToScanDefinition();
793    
794            PackageScanDefinition packageScanDef = getPackageScan();
795    
796            if (packageScanDef != null && packageScanDef.getPackages().size() > 0) {
797    
798                PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter();
799                filter.addIncludePatterns(packageScanDef.getIncludes());
800                filter.addExcludePatterns(packageScanDef.getExcludes());
801                resolver.addFilter(filter);
802    
803                String[] normalized = normalizePackages(packageScanDef.getPackages());
804                RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), normalized, getContextClassLoaderOnStart(), getBeanPostProcessor(), getContext()
805                    .getPackageScanClassResolver());
806                finder.appendBuilders(getAdditionalBuilders());
807            }
808    
809        }
810    
811        private void addPackageElementContentsToScanDefinition() {
812            PackageScanDefinition packageScanDef = getPackageScan();
813    
814            if (getPackages() != null && getPackages().length > 0) {
815                LOG.warn("Using a packages element to specify packages to search has been deprecated. Please use a packageScan element instead.");
816                if (packageScanDef == null) {
817                    packageScanDef = new PackageScanDefinition();
818                    setPackageScan(packageScanDef);
819                }
820    
821                for (String pkg : getPackages()) {
822                    packageScanDef.getPackages().add(pkg);
823                }
824            }
825        }
826    
827        private String[] normalizePackages(List<String> unnormalized) {
828            List<String> packages = new ArrayList<String>();
829            for (String name : unnormalized) {
830                name = ObjectHelper.normalizeClassName(name);
831                if (ObjectHelper.isNotEmpty(name)) {
832                    if (LOG.isTraceEnabled()) {
833                        LOG.trace("Using package: " + name + " to scan for RouteBuilder classes");
834                    }
835                    packages.add(name);
836                }
837            }
838            return packages.toArray(new String[packages.size()]);
839        }
840    
841    }