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.HashSet;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Set;
024    
025    import javax.xml.bind.annotation.XmlAccessType;
026    import javax.xml.bind.annotation.XmlAccessorType;
027    import javax.xml.bind.annotation.XmlAttribute;
028    import javax.xml.bind.annotation.XmlElement;
029    import javax.xml.bind.annotation.XmlElements;
030    import javax.xml.bind.annotation.XmlRootElement;
031    import javax.xml.bind.annotation.XmlTransient;
032    
033    import org.apache.camel.CamelContext;
034    import org.apache.camel.CamelException;
035    import org.apache.camel.RoutesBuilder;
036    import org.apache.camel.ShutdownRoute;
037    import org.apache.camel.ShutdownRunningTask;
038    import org.apache.camel.builder.ErrorHandlerBuilderRef;
039    import org.apache.camel.builder.RouteBuilder;
040    import org.apache.camel.component.properties.PropertiesComponent;
041    import org.apache.camel.component.properties.PropertiesResolver;
042    import org.apache.camel.management.DefaultManagementAgent;
043    import org.apache.camel.management.DefaultManagementLifecycleStrategy;
044    import org.apache.camel.management.DefaultManagementStrategy;
045    import org.apache.camel.management.ManagedManagementStrategy;
046    import org.apache.camel.model.FromDefinition;
047    import org.apache.camel.model.IdentifiedType;
048    import org.apache.camel.model.InterceptDefinition;
049    import org.apache.camel.model.InterceptFromDefinition;
050    import org.apache.camel.model.InterceptSendToEndpointDefinition;
051    import org.apache.camel.model.OnCompletionDefinition;
052    import org.apache.camel.model.OnExceptionDefinition;
053    import org.apache.camel.model.PackageScanDefinition;
054    import org.apache.camel.model.ProcessorDefinition;
055    import org.apache.camel.model.RouteBuilderDefinition;
056    import org.apache.camel.model.RouteContainer;
057    import org.apache.camel.model.RouteContextRefDefinition;
058    import org.apache.camel.model.RouteDefinition;
059    import org.apache.camel.model.ThreadPoolProfileDefinition;
060    import org.apache.camel.model.ToDefinition;
061    import org.apache.camel.model.TransactedDefinition;
062    import org.apache.camel.model.config.PropertiesDefinition;
063    import org.apache.camel.model.dataformat.DataFormatsDefinition;
064    import org.apache.camel.processor.interceptor.Delayer;
065    import org.apache.camel.processor.interceptor.HandleFault;
066    import org.apache.camel.processor.interceptor.TraceFormatter;
067    import org.apache.camel.processor.interceptor.Tracer;
068    import org.apache.camel.spi.ClassResolver;
069    import org.apache.camel.spi.EventFactory;
070    import org.apache.camel.spi.EventNotifier;
071    import org.apache.camel.spi.ExecutorServiceStrategy;
072    import org.apache.camel.spi.FactoryFinderResolver;
073    import org.apache.camel.spi.InflightRepository;
074    import org.apache.camel.spi.InterceptStrategy;
075    import org.apache.camel.spi.LifecycleStrategy;
076    import org.apache.camel.spi.ManagementStrategy;
077    import org.apache.camel.spi.PackageScanClassResolver;
078    import org.apache.camel.spi.Registry;
079    import org.apache.camel.spi.RouteContext;
080    import org.apache.camel.spi.ShutdownStrategy;
081    import org.apache.camel.spi.ThreadPoolProfile;
082    import org.apache.camel.util.CamelContextHelper;
083    import org.apache.camel.util.EndpointHelper;
084    import org.apache.camel.util.ObjectHelper;
085    import org.apache.commons.logging.Log;
086    import org.apache.commons.logging.LogFactory;
087    import org.springframework.beans.factory.DisposableBean;
088    import org.springframework.beans.factory.FactoryBean;
089    import org.springframework.beans.factory.InitializingBean;
090    import org.springframework.beans.factory.config.BeanPostProcessor;
091    import org.springframework.context.ApplicationContext;
092    import org.springframework.context.ApplicationContextAware;
093    import org.springframework.context.ApplicationEvent;
094    import org.springframework.context.ApplicationListener;
095    import org.springframework.context.event.ContextRefreshedEvent;
096    
097    import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
098    
099    /**
100     * A Spring {@link FactoryBean} to create and initialize a
101     * {@link SpringCamelContext} and install routes either explicitly configured in
102     * Spring XML or found by searching the classpath for Java classes which extend
103     * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
104     *
105     * @version $Revision: 945532 $
106     */
107    @XmlRootElement(name = "camelContext")
108    @XmlAccessorType(XmlAccessType.FIELD)
109    @SuppressWarnings("unused")
110    public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener {
111        private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class);
112    
113        @XmlAttribute(name = "depends-on", required = false)
114        private String dependsOn;
115        @XmlAttribute(required = false)
116        private String trace;
117        @XmlAttribute(required = false)
118        private String streamCache = "false";
119        @XmlAttribute(required = false)
120        private String delayer;
121        @XmlAttribute(required = false)
122        private String handleFault;
123        @XmlAttribute(required = false)
124        private String errorHandlerRef;
125        @XmlAttribute(required = false)
126        private String autoStartup = "true";
127        @XmlAttribute(required = false)
128        private ShutdownRoute shutdownRoute;
129        @XmlAttribute(required = false)
130        private ShutdownRunningTask shutdownRunningTask;
131        @XmlElement(name = "properties", required = false)
132        private PropertiesDefinition properties;
133        @XmlElement(name = "propertyPlaceholder", type = CamelPropertyPlaceholderDefinition.class, required = false)
134        private CamelPropertyPlaceholderDefinition camelPropertyPlaceholder;
135        @XmlElement(name = "package", required = false)
136        private String[] packages = {};
137        @XmlElement(name = "packageScan", type = PackageScanDefinition.class, required = false)
138        private PackageScanDefinition packageScan;
139        @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class, required = false)
140        private CamelJMXAgentDefinition camelJMXAgent;    
141        @XmlElements({
142            @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false),
143            @XmlElement(name = "template", type = CamelProducerTemplateFactoryBean.class, required = false),
144            @XmlElement(name = "consumerTemplate", type = CamelConsumerTemplateFactoryBean.class, required = false),
145            @XmlElement(name = "proxy", type = CamelProxyFactoryDefinition.class, required = false),
146            @XmlElement(name = "export", type = CamelServiceExporterDefinition.class, required = false),
147            @XmlElement(name = "errorHandler", type = ErrorHandlerDefinition.class, required = false)})
148        private List beans;    
149        @XmlElement(name = "routeBuilder", required = false)
150        private List<RouteBuilderDefinition> builderRefs = new ArrayList<RouteBuilderDefinition>();
151        @XmlElement(name = "routeContextRef", required = false)
152        private List<RouteContextRefDefinition> routeRefs = new ArrayList<RouteContextRefDefinition>();
153        @XmlElement(name = "threadPoolProfile", required = false)
154        private List<ThreadPoolProfileDefinition> threadPoolProfiles;
155        @XmlElement(name = "threadPool", required = false)
156        private List<CamelThreadPoolFactoryBean> threadPools;
157        @XmlElement(name = "endpoint", required = false)
158        private List<CamelEndpointFactoryBean> endpoints;
159        @XmlElement(name = "dataFormats", required = false)
160        private DataFormatsDefinition dataFormats;
161        @XmlElement(name = "onException", required = false)
162        private List<OnExceptionDefinition> onExceptions = new ArrayList<OnExceptionDefinition>();
163        @XmlElement(name = "onCompletion", required = false)
164        private List<OnCompletionDefinition> onCompletions = new ArrayList<OnCompletionDefinition>();
165        @XmlElement(name = "intercept", required = false)
166        private List<InterceptDefinition> intercepts = new ArrayList<InterceptDefinition>();
167        @XmlElement(name = "interceptFrom", required = false)
168        private List<InterceptFromDefinition> interceptFroms = new ArrayList<InterceptFromDefinition>();
169        @XmlElement(name = "interceptSendToEndpoint", required = false)
170        private List<InterceptSendToEndpointDefinition> interceptSendToEndpoints = new ArrayList<InterceptSendToEndpointDefinition>();
171        @XmlElement(name = "route", required = false)
172        private List<RouteDefinition> routes = new ArrayList<RouteDefinition>();    
173        @XmlTransient
174        private SpringCamelContext context;
175        @XmlTransient
176        private List<RoutesBuilder> builders = new ArrayList<RoutesBuilder>();
177        @XmlTransient
178        private ApplicationContext applicationContext;
179        @XmlTransient
180        private ClassLoader contextClassLoaderOnStart;
181        @XmlTransient
182        private BeanPostProcessor beanPostProcessor;
183    
184        public CamelContextFactoryBean() {
185            // Lets keep track of the class loader for when we actually do start things up
186            contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader();
187        }
188    
189        public Object getObject() throws Exception {
190            return getContext();
191        }
192    
193        public Class getObjectType() {
194            return SpringCamelContext.class;
195        }
196    
197        public boolean isSingleton() {
198            return true;
199        }
200        
201        public ClassLoader getContextClassLoaderOnStart() {
202            return contextClassLoaderOnStart;
203        }
204    
205        public void afterPropertiesSet() throws Exception {
206            if (ObjectHelper.isEmpty(getId())) {
207                throw new IllegalArgumentException("Id must be set");
208            }
209    
210            if (properties != null) {
211                getContext().setProperties(properties.asMap());
212            }
213    
214            // set the resolvers first
215            PackageScanClassResolver packageResolver = getBeanForType(PackageScanClassResolver.class);
216            if (packageResolver != null) {
217                LOG.info("Using custom PackageScanClassResolver: " + packageResolver);
218                getContext().setPackageScanClassResolver(packageResolver);
219            }
220            ClassResolver classResolver = getBeanForType(ClassResolver.class);
221            if (classResolver != null) {
222                LOG.info("Using custom ClassResolver: " + classResolver);
223                getContext().setClassResolver(classResolver);
224            }
225            FactoryFinderResolver factoryFinderResolver = getBeanForType(FactoryFinderResolver.class);
226            if (factoryFinderResolver != null) {
227                LOG.info("Using custom FactoryFinderResolver: " + factoryFinderResolver);
228                getContext().setFactoryFinderResolver(factoryFinderResolver);
229            }
230            ExecutorServiceStrategy executorServiceStrategy = getBeanForType(ExecutorServiceStrategy.class);
231            if (executorServiceStrategy != null) {
232                LOG.info("Using custom ExecutorServiceStrategy: " + executorServiceStrategy);
233                getContext().setExecutorServiceStrategy(executorServiceStrategy);
234            }
235    
236            // set the custom registry if defined
237            Registry registry = getBeanForType(Registry.class);
238            if (registry != null) {
239                LOG.info("Using custom Registry: " + registry);
240                getContext().setRegistry(registry);
241            }
242    
243            // setup property placeholder so we got it as early as possible
244            initPropertyPlaceholder();
245    
246            // setup JMX agent at first
247            initJMXAgent();
248    
249            Tracer tracer = getBeanForType(Tracer.class);
250            if (tracer != null) {
251                // use formatter if there is a TraceFormatter bean defined
252                TraceFormatter formatter = getBeanForType(TraceFormatter.class);
253                if (formatter != null) {
254                    tracer.setFormatter(formatter);
255                }
256                LOG.info("Using custom Tracer: " + tracer);
257                getContext().addInterceptStrategy(tracer);
258            }
259    
260            HandleFault handleFault = getBeanForType(HandleFault.class);
261            if (handleFault != null) {
262                LOG.info("Using custom HandleFault: " + handleFault);
263                getContext().addInterceptStrategy(handleFault);
264            }
265    
266            Delayer delayer = getBeanForType(Delayer.class);
267            if (delayer != null) {
268                LOG.info("Using custom Delayer: " + delayer);
269                getContext().addInterceptStrategy(delayer);
270            }
271    
272            InflightRepository inflightRepository = getBeanForType(InflightRepository.class);
273            if (delayer != null) {
274                LOG.info("Using custom InflightRepository: " + inflightRepository);
275                getContext().setInflightRepository(inflightRepository);
276            }
277    
278            ManagementStrategy managementStrategy = getBeanForType(ManagementStrategy.class);
279            if (managementStrategy != null) {
280                LOG.info("Using custom ManagementStrategy: " + managementStrategy);
281                getContext().setManagementStrategy(managementStrategy);
282            }
283    
284            EventFactory eventFactory = getBeanForType(EventFactory.class);
285            if (eventFactory != null) {
286                LOG.info("Using custom EventFactory: " + eventFactory);
287                getContext().getManagementStrategy().setEventFactory(eventFactory);
288            }
289    
290            // set the event notifier strategies if defined
291            Map<String, EventNotifier> eventNotifiers = getContext().getRegistry().lookupByType(EventNotifier.class);
292            if (eventNotifiers != null && !eventNotifiers.isEmpty()) {
293                for (String id : eventNotifiers.keySet()) {
294                    EventNotifier notifier = eventNotifiers.get(id);
295                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
296                    if (!getContext().getManagementStrategy().getEventNotifiers().contains(notifier)) {
297                        LOG.info("Using custom EventNotifier with id: " + id + " and implementation: " + notifier);
298                        getContext().getManagementStrategy().addEventNotifier(notifier);
299                    }
300                }
301            }
302    
303            ShutdownStrategy shutdownStrategy = getBeanForType(ShutdownStrategy.class);
304            if (shutdownStrategy != null) {
305                LOG.info("Using custom ShutdownStrategy: " + shutdownStrategy);
306                getContext().setShutdownStrategy(shutdownStrategy);
307            }
308    
309            // add global interceptors
310            Map<String, InterceptStrategy> interceptStrategies = getContext().getRegistry().lookupByType(InterceptStrategy.class);
311            if (interceptStrategies != null && !interceptStrategies.isEmpty()) {
312                for (String id : interceptStrategies.keySet()) {
313                    InterceptStrategy strategy = interceptStrategies.get(id);
314                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
315                    if (!getContext().getInterceptStrategies().contains(strategy)) {
316                        LOG.info("Using custom InterceptStrategy with id: " + id + " and implementation: " + strategy);
317                        getContext().addInterceptStrategy(strategy);
318                    }
319                }
320            }
321    
322            // set the lifecycle strategy if defined
323            Map<String, LifecycleStrategy> lifecycleStrategies = getContext().getRegistry().lookupByType(LifecycleStrategy.class);
324            if (lifecycleStrategies != null && !lifecycleStrategies.isEmpty()) {
325                for (String id : lifecycleStrategies.keySet()) {
326                    LifecycleStrategy strategy = lifecycleStrategies.get(id);
327                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
328                    if (!getContext().getLifecycleStrategies().contains(strategy)) {
329                        LOG.info("Using custom LifecycleStrategy with id: " + id + " and implementation: " + strategy);
330                        getContext().addLifecycleStrategy(strategy);
331                    }
332                }
333            }
334    
335            // set the default thread pool profile if defined
336            initThreadPoolProfiles(getContext());
337    
338            // Set the application context and camelContext for the beanPostProcessor
339            if (beanPostProcessor != null) {
340                if (beanPostProcessor instanceof ApplicationContextAware) {
341                    ((ApplicationContextAware)beanPostProcessor).setApplicationContext(applicationContext);
342                }
343                if (beanPostProcessor instanceof CamelBeanPostProcessor) {
344                    ((CamelBeanPostProcessor)beanPostProcessor).setCamelContext(getContext());
345                }
346            }
347    
348            initSpringCamelContext(getContext());
349    
350            // must init route refs before we prepare the routes below
351            initRouteRefs();
352    
353            // do special preparation for some concepts such as interceptors and policies
354            // this is needed as JAXB does not build exactly the same model definition as Spring DSL would do
355            // using route builders. So we have here a little custom code to fix the JAXB gaps
356            for (RouteDefinition route : routes) {
357    
358                // abstracts is the cross cutting concerns
359                List<ProcessorDefinition> abstracts = new ArrayList<ProcessorDefinition>();
360    
361                // upper is the cross cutting concerns such as interceptors, error handlers etc
362                List<ProcessorDefinition> upper = new ArrayList<ProcessorDefinition>();
363    
364                // lower is the regular route
365                List<ProcessorDefinition> lower = new ArrayList<ProcessorDefinition>();
366    
367                prepareRouteForInit(route, abstracts, lower);
368    
369                // toAsync should fix up itself at first
370                initToAsync(lower);
371    
372                // interceptors should be first for the cross cutting concerns
373                initInterceptors(route, upper);
374                // then on completion
375                initOnCompletions(abstracts, upper);
376                // then transactions
377                initTransacted(abstracts, lower);
378                // then on exception
379                initOnExceptions(abstracts, upper);
380    
381                // rebuild route as upper + lower
382                route.clearOutput();
383                route.getOutputs().addAll(upper);
384                route.getOutputs().addAll(lower);
385    
386                // configure parents
387                initParent(route);
388            }
389    
390            if (dataFormats != null) {
391                getContext().setDataFormats(dataFormats.asMap());
392            }
393    
394            // lets force any lazy creation
395            getContext().addRouteDefinitions(routes);
396    
397            if (LOG.isDebugEnabled()) {
398                LOG.debug("Found JAXB created routes: " + getRoutes());
399            }
400            findRouteBuilders();
401            installRoutes();
402        }
403    
404        private void prepareRouteForInit(RouteDefinition route, List<ProcessorDefinition> abstracts,
405                                         List<ProcessorDefinition> lower) {
406            // filter the route into abstracts and lower
407            for (ProcessorDefinition output : route.getOutputs()) {
408                if (output.isAbstract()) {
409                    abstracts.add(output);
410                } else {
411                    lower.add(output);
412                }
413            }
414        }
415    
416        private void initParent(RouteDefinition route) {
417            for (ProcessorDefinition output : route.getOutputs()) {
418                output.setParent(route);
419                if (output.getOutputs() != null) {
420                    // recursive the outputs
421                    initParent(output);
422                }
423            }
424        }
425    
426        @SuppressWarnings("unchecked")
427        private void initParent(ProcessorDefinition parent) {
428            List<ProcessorDefinition> children = parent.getOutputs();
429            for (ProcessorDefinition child : children) {
430                child.setParent(parent);
431                if (child.getOutputs() != null) {
432                    // recursive the children
433                    initParent(child);
434                }
435            }
436        }
437    
438        private void initToAsync(List<ProcessorDefinition> lower) {
439            List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
440            ToDefinition toAsync = null;
441    
442            for (ProcessorDefinition output : lower) {
443                if (toAsync != null) {
444                    // add this output on toAsync
445                    toAsync.getOutputs().add(output);
446                } else {
447                    // regular outputs
448                    outputs.add(output);
449                }
450    
451                if (output instanceof ToDefinition) {
452                    ToDefinition to = (ToDefinition) output;
453                    if (to.isAsync() != null && to.isAsync()) {
454                        // new current to async
455                        toAsync = to;
456                    }
457                }
458            }
459    
460            // rebuild outputs
461            lower.clear();
462            lower.addAll(outputs);
463        }
464    
465        private void initOnExceptions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) {
466            // add global on exceptions if any
467            if (onExceptions != null && !onExceptions.isEmpty()) {
468                abstracts.addAll(onExceptions);
469            }
470    
471            // now add onExceptions to the route
472            for (ProcessorDefinition output : abstracts) {
473                if (output instanceof OnExceptionDefinition) {
474                    // on exceptions must be added at top, so the route flow is correct as
475                    // on exceptions should be the first outputs
476                    upper.add(0, output);
477                }
478            }
479        }
480    
481        private void initInterceptors(RouteDefinition route, List<ProcessorDefinition> upper) {
482            // configure intercept
483            for (InterceptDefinition intercept : getIntercepts()) {
484                intercept.afterPropertiesSet();
485                // add as first output so intercept is handled before the actual route and that gives
486                // us the needed head start to init and be able to intercept all the remaining processing steps
487                upper.add(0, intercept);
488            }
489    
490            // configure intercept from
491            for (InterceptFromDefinition intercept : getInterceptFroms()) {
492    
493                // should we only apply interceptor for a given endpoint uri
494                boolean match = true;
495                if (intercept.getUri() != null) {
496                    match = false;
497                    for (FromDefinition input : route.getInputs()) {
498                        if (EndpointHelper.matchEndpoint(input.getUri(), intercept.getUri())) {
499                            match = true;
500                            break;
501                        }
502                    }
503                }
504    
505                if (match) {
506                    intercept.afterPropertiesSet();
507                    // add as first output so intercept is handled before the actual route and that gives
508                    // us the needed head start to init and be able to intercept all the remaining processing steps
509                    upper.add(0, intercept);
510                }
511            }
512    
513            // configure intercept send to endpoint
514            for (InterceptSendToEndpointDefinition intercept : getInterceptSendToEndpoints()) {
515                intercept.afterPropertiesSet();
516                // add as first output so intercept is handled before the actual route and that gives
517                // us the needed head start to init and be able to intercept all the remaining processing steps
518                upper.add(0, intercept);
519            }
520        }
521    
522        private void initOnCompletions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) {
523            List<OnCompletionDefinition> completions = new ArrayList<OnCompletionDefinition>();
524    
525            // find the route scoped onCompletions
526            for (ProcessorDefinition out : abstracts) {
527                if (out instanceof OnCompletionDefinition) {
528                    completions.add((OnCompletionDefinition) out);
529                }
530            }
531    
532            // only add global onCompletion if there are no route already
533            if (completions.isEmpty()) {
534                completions = getOnCompletions();
535            }
536    
537            // are there any completions to init at all?
538            if (completions.isEmpty()) {
539                return;
540            }
541    
542            upper.addAll(completions);
543        }
544    
545        private void initTransacted(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> lower) {
546            TransactedDefinition transacted = null;
547    
548            // add to correct type
549            for (ProcessorDefinition type : abstracts) {
550                if (type instanceof TransactedDefinition) {
551                    if (transacted == null) {
552                        transacted = (TransactedDefinition) type;
553                    } else {
554                        throw new IllegalArgumentException("The route can only have one transacted defined");
555                    }
556                }
557            }
558    
559            if (transacted != null) {
560                // the outputs should be moved to the transacted policy
561                transacted.getOutputs().addAll(lower);
562                // and add it as the single output
563                lower.clear();
564                lower.add(transacted);
565            } 
566        }
567    
568        private void initJMXAgent() throws Exception {
569            if (camelJMXAgent != null && camelJMXAgent.isAgentDisabled()) {
570                LOG.info("JMXAgent disabled");
571                // clear the existing lifecycle strategies define by the DefaultCamelContext constructor
572                getContext().getLifecycleStrategies().clear();
573                // no need to add a lifecycle strategy as we do not need one as JMX is disabled
574                getContext().setManagementStrategy(new DefaultManagementStrategy());
575            } else if (camelJMXAgent != null) {
576                LOG.info("JMXAgent enabled: " + camelJMXAgent);
577                DefaultManagementAgent agent = new DefaultManagementAgent(getContext());
578                agent.setConnectorPort(parseInteger(camelJMXAgent.getConnectorPort()));
579                agent.setCreateConnector(parseBoolean(camelJMXAgent.getCreateConnector()));
580                agent.setMBeanObjectDomainName(parseText(camelJMXAgent.getMbeanObjectDomainName()));
581                agent.setMBeanServerDefaultDomain(parseText(camelJMXAgent.getMbeanServerDefaultDomain()));
582                agent.setRegistryPort(parseInteger(camelJMXAgent.getRegistryPort()));
583                agent.setServiceUrlPath(parseText(camelJMXAgent.getServiceUrlPath()));
584                agent.setUsePlatformMBeanServer(parseBoolean(camelJMXAgent.getUsePlatformMBeanServer()));
585                agent.setOnlyRegisterProcessorWithCustomId(parseBoolean(camelJMXAgent.getOnlyRegisterProcessorWithCustomId()));
586    
587                ManagementStrategy managementStrategy = new ManagedManagementStrategy(agent);
588                getContext().setManagementStrategy(managementStrategy);
589    
590                // clear the existing lifecycle strategies define by the DefaultCamelContext constructor
591                getContext().getLifecycleStrategies().clear();
592                getContext().addLifecycleStrategy(new DefaultManagementLifecycleStrategy(getContext()));
593                // set additional configuration from camelJMXAgent
594                boolean onlyId = agent.getOnlyRegisterProcessorWithCustomId() != null && agent.getOnlyRegisterProcessorWithCustomId();
595                getContext().getManagementStrategy().onlyManageProcessorWithCustomId(onlyId);
596                getContext().getManagementStrategy().setStatisticsLevel(camelJMXAgent.getStatisticsLevel());
597            }
598        }
599    
600        private void initPropertyPlaceholder() throws Exception {
601            if (getCamelPropertyPlaceholder() != null) {
602                CamelPropertyPlaceholderDefinition def = getCamelPropertyPlaceholder();
603    
604                PropertiesComponent pc = new PropertiesComponent();
605                pc.setLocation(def.getLocation());
606    
607                // if using a custom resolver
608                if (ObjectHelper.isNotEmpty(def.getPropertiesResolverRef())) {
609                    PropertiesResolver resolver = CamelContextHelper.mandatoryLookup(getContext(), def.getPropertiesResolverRef(),
610                                                                                     PropertiesResolver.class);
611                    pc.setPropertiesResolver(resolver);
612                }
613    
614                // register the properties component
615                getContext().addComponent("properties", pc);
616            }
617        }
618    
619        private void initRouteRefs() throws Exception {
620            // add route refs to existing routes
621            if (routeRefs != null) {
622                for (RouteContextRefDefinition ref : routeRefs) {
623                    List<RouteDefinition> defs = ref.lookupRoutes(getContext());
624                    for (RouteDefinition def : defs) {
625                        if (LOG.isDebugEnabled()) {
626                            LOG.debug("Adding route from " + ref + " -> " + def);
627                        }
628                        // add in top as they are most likely to be common/shared
629                        // which you may want to start first
630                        routes.add(0, def);
631                    }
632                }
633            }
634        }
635    
636        private <T> T getBeanForType(Class<T> clazz) {
637            T bean = null;
638            String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true);
639            if (names.length == 1) {
640                bean = (T) getApplicationContext().getBean(names[0], clazz);
641            }
642            if (bean == null) {
643                ApplicationContext parentContext = getApplicationContext().getParent();
644                if (parentContext != null) {
645                    names = parentContext.getBeanNamesForType(clazz, true, true);
646                    if (names.length == 1) {
647                        bean = (T) parentContext.getBean(names[0], clazz);
648                    }
649                }
650            }
651            return bean;
652        }
653    
654        public void destroy() throws Exception {
655            getContext().stop();
656        }
657    
658        public void onApplicationEvent(ApplicationEvent event) {
659            // From Spring 3.0.1, The BeanFactory applicationEventListener 
660            // and Bean's applicationEventListener will be called,
661            // So we just delegate the onApplicationEvent call here.
662            
663            if (context != null) {
664                // let the spring camel context handle the events
665                context.onApplicationEvent(event);
666            } else {
667                if (LOG.isDebugEnabled()) {
668                    LOG.debug("Publishing spring-event: " + event);
669                }
670    
671                if (event instanceof ContextRefreshedEvent) {
672                    // now lets start the CamelContext so that all its possible
673                    // dependencies are initialized
674                    try {
675                        LOG.debug("Starting the context now!");
676                        getContext().start();
677                    } catch (Exception e) {
678                        throw wrapRuntimeCamelException(e);
679                    }
680                }
681            }
682        }
683    
684        private String parseText(String text) throws Exception {
685            // ensure we support property placeholders
686            return getContext().resolvePropertyPlaceholders(text);
687        }
688    
689        private Integer parseInteger(String text) throws Exception {
690            // ensure we support property placeholders
691            String s = getContext().resolvePropertyPlaceholders(text);
692            if (s != null) {
693                try {
694                    return new Integer(s);
695                } catch (NumberFormatException e) {
696                    if (s.equals(text)) {
697                        throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e);
698                    } else {
699                        throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e);
700                    }
701                }
702            }
703            return null;
704        }
705    
706        private Long parseLong(String text) throws Exception {
707            // ensure we support property placeholders
708            String s = getContext().resolvePropertyPlaceholders(text);
709            if (s != null) {
710                try {
711                    return new Long(s);
712                } catch (NumberFormatException e) {
713                    if (s.equals(text)) {
714                        throw new IllegalArgumentException("Error parsing [" + s + "] as a Long.", e);
715                    } else {
716                        throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Long.", e);
717                    }
718                }
719            }
720            return null;
721        }
722    
723        private Boolean parseBoolean(String text) throws Exception {
724            // ensure we support property placeholders
725            String s = getContext().resolvePropertyPlaceholders(text);
726            if (s != null) {
727                s = s.trim().toLowerCase();
728                if (s.equals("true") || s.equals("false")) {
729                    return new Boolean(s);
730                } else {
731                    if (s.equals(text)) {
732                        throw new IllegalArgumentException("Error parsing [" + s + "] as a Boolean.");
733                    } else {
734                        throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Boolean.");
735                    }
736                }
737            }
738            return null;
739        }
740    
741        // Properties
742        // -------------------------------------------------------------------------
743        public SpringCamelContext getContext() throws Exception {
744            if (context == null) {
745                context = createContext();
746            }
747            return context;
748        }
749    
750        public void setContext(SpringCamelContext context) {
751            this.context = context;
752        }
753    
754        public List<RouteDefinition> getRoutes() {
755            return routes;
756        }
757    
758        public void setRoutes(List<RouteDefinition> routes) {
759            this.routes = routes;
760        }
761    
762        public List<InterceptDefinition> getIntercepts() {
763            return intercepts;
764        }
765    
766        public void setIntercepts(List<InterceptDefinition> intercepts) {
767            this.intercepts = intercepts;
768        }
769    
770        public List<InterceptFromDefinition> getInterceptFroms() {
771            return interceptFroms;
772        }
773    
774        public void setInterceptFroms(List<InterceptFromDefinition> interceptFroms) {
775            this.interceptFroms = interceptFroms;
776        }
777    
778        public List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints() {
779            return interceptSendToEndpoints;
780        }
781    
782        public void setInterceptSendToEndpoints(List<InterceptSendToEndpointDefinition> interceptSendToEndpoints) {
783            this.interceptSendToEndpoints = interceptSendToEndpoints;
784        }
785    
786        public ApplicationContext getApplicationContext() {
787            if (applicationContext == null) {
788                throw new IllegalArgumentException("No applicationContext has been injected!");
789            }
790            return applicationContext;
791        }
792    
793        public void setApplicationContext(ApplicationContext applicationContext) {
794            this.applicationContext = applicationContext;
795        }
796        
797        public PropertiesDefinition getProperties() {
798            return properties;
799        }
800        
801        public void setProperties(PropertiesDefinition properties) {        
802            this.properties = properties;
803        }
804    
805        public String[] getPackages() {
806            return packages;
807        }
808    
809        /**
810         * Sets the package names to be recursively searched for Java classes which
811         * extend {@link RouteBuilder} to be auto-wired up to the
812         * {@link SpringCamelContext} as a route. Note that classes are excluded if
813         * they are specifically configured in the spring.xml
814         * <p/>
815         * A more advanced configuration can be done using {@link #setPackageScan(org.apache.camel.model.PackageScanDefinition)}
816         * 
817         * @param packages the package names which are recursively searched
818         * @see #setPackageScan(org.apache.camel.model.PackageScanDefinition)
819         */
820        public void setPackages(String[] packages) {
821            this.packages = packages;
822        }
823    
824        public PackageScanDefinition getPackageScan() {
825            return packageScan;
826        }
827    
828        /**
829         * Sets the package scanning information. Package scanning allows for the
830         * automatic discovery of certain camel classes at runtime for inclusion
831         * e.g. {@link RouteBuilder} implementations
832         * 
833         * @param packageScan the package scan
834         */
835        public void setPackageScan(PackageScanDefinition packageScan) {
836            this.packageScan = packageScan;
837        }
838    
839        public CamelPropertyPlaceholderDefinition getCamelPropertyPlaceholder() {
840            return camelPropertyPlaceholder;
841        }
842    
843        public void setCamelPropertyPlaceholder(CamelPropertyPlaceholderDefinition camelPropertyPlaceholder) {
844            this.camelPropertyPlaceholder = camelPropertyPlaceholder;
845        }
846    
847        public void setBeanPostProcessor(BeanPostProcessor postProcessor) {
848            this.beanPostProcessor = postProcessor;
849        }
850    
851        public BeanPostProcessor getBeanPostProcessor() {
852            return beanPostProcessor;
853        }
854    
855        public void setCamelJMXAgent(CamelJMXAgentDefinition agent) {
856            camelJMXAgent = agent;
857        }
858    
859        public String getTrace() {
860            return trace;
861        }
862    
863        public void setTrace(String trace) {
864            this.trace = trace;
865        }
866    
867        public String getStreamCache() {
868            return streamCache;
869        }
870    
871        public void setStreamCache(String streamCache) {
872            this.streamCache = streamCache;
873        }
874    
875        public String getDelayer() {
876            return delayer;
877        }
878    
879        public void setDelayer(String delayer) {
880            this.delayer = delayer;
881        }
882    
883        public String getHandleFault() {
884            return handleFault;
885        }
886    
887        public void setHandleFault(String handleFault) {
888            this.handleFault = handleFault;
889        }
890    
891        public String getAutoStartup() {
892            return autoStartup;
893        }
894    
895        public void setAutoStartup(String autoStartup) {
896            this.autoStartup = autoStartup;
897        }
898    
899        public CamelJMXAgentDefinition getCamelJMXAgent() {
900            return camelJMXAgent;
901        }
902    
903        public List<RouteBuilderDefinition> getBuilderRefs() {
904            return builderRefs;
905        }
906    
907        public void setBuilderRefs(List<RouteBuilderDefinition> builderRefs) {
908            this.builderRefs = builderRefs;
909        }
910    
911        public List<RouteContextRefDefinition> getRouteRefs() {
912            return routeRefs;
913        }
914    
915        public void setRouteRefs(List<RouteContextRefDefinition> routeRefs) {
916            this.routeRefs = routeRefs;
917        }
918    
919        public String getErrorHandlerRef() {
920            return errorHandlerRef;
921        }
922    
923        /**
924         * Sets the name of the error handler object used to default the error handling strategy
925         *
926         * @param errorHandlerRef the Spring bean ref of the error handler
927         */
928        public void setErrorHandlerRef(String errorHandlerRef) {
929            this.errorHandlerRef = errorHandlerRef;
930        }
931    
932        public void setDataFormats(DataFormatsDefinition dataFormats) {
933            this.dataFormats = dataFormats;
934        }
935    
936        public DataFormatsDefinition getDataFormats() {
937            return dataFormats;
938        }
939    
940        public void setOnExceptions(List<OnExceptionDefinition> onExceptions) {
941            this.onExceptions = onExceptions;
942        }
943    
944        public List<OnExceptionDefinition> getOnExceptions() {
945            return onExceptions;
946        }
947    
948        public List<OnCompletionDefinition> getOnCompletions() {
949            return onCompletions;
950        }
951    
952        public void setOnCompletions(List<OnCompletionDefinition> onCompletions) {
953            this.onCompletions = onCompletions;
954        }
955    
956        public ShutdownRoute getShutdownRoute() {
957            return shutdownRoute;
958        }
959    
960        public void setShutdownRoute(ShutdownRoute shutdownRoute) {
961            this.shutdownRoute = shutdownRoute;
962        }
963    
964        public ShutdownRunningTask getShutdownRunningTask() {
965            return shutdownRunningTask;
966        }
967    
968        public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
969            this.shutdownRunningTask = shutdownRunningTask;
970        }
971    
972        public List<ThreadPoolProfileDefinition> getThreadPoolProfiles() {
973            return threadPoolProfiles;
974        }
975    
976        public void setThreadPoolProfiles(List<ThreadPoolProfileDefinition> threadPoolProfiles) {
977            this.threadPoolProfiles = threadPoolProfiles;
978        }
979    
980        public String getDependsOn() {
981            return dependsOn;
982        }
983    
984        public void setDependsOn(String dependsOn) {
985            this.dependsOn = dependsOn;
986        }
987    
988        // Implementation methods
989        // -------------------------------------------------------------------------
990    
991        /**
992         * Create the context
993         */
994        protected SpringCamelContext createContext() {
995            SpringCamelContext ctx = newCamelContext();
996            ctx.setName(getId());
997            return ctx;
998        }
999    
1000        /**
1001         * Initializes the context
1002         * 
1003         * @param ctx the context
1004         * @throws Exception is thrown if error occurred
1005         */
1006        protected void initSpringCamelContext(SpringCamelContext ctx) throws Exception {
1007            if (streamCache != null) {
1008                ctx.setStreamCaching(parseBoolean(getStreamCache()));
1009            }
1010            if (trace != null) {
1011                ctx.setTracing(parseBoolean(getTrace()));
1012            }
1013            if (delayer != null) {
1014                ctx.setDelayer(parseLong(getDelayer()));
1015            }
1016            if (handleFault != null) {
1017                ctx.setHandleFault(parseBoolean(getHandleFault()));
1018            }
1019            if (errorHandlerRef != null) {
1020                ctx.setErrorHandlerBuilder(new ErrorHandlerBuilderRef(getErrorHandlerRef()));
1021            }
1022            if (autoStartup != null) {
1023                ctx.setAutoStartup(parseBoolean(getAutoStartup()));
1024            }
1025            if (shutdownRoute != null) {
1026                ctx.setShutdownRoute(getShutdownRoute());
1027            }
1028            if (shutdownRunningTask != null) {
1029                ctx.setShutdownRunningTask(getShutdownRunningTask());
1030            }
1031        }
1032        
1033        protected SpringCamelContext newCamelContext() {
1034            return new SpringCamelContext(getApplicationContext());
1035        }
1036    
1037        private void initThreadPoolProfiles(CamelContext context) {
1038            Set<String> defaultIds = new HashSet<String>();
1039    
1040            // lookup and use custom profiles from the registry
1041            Map<String, ThreadPoolProfile> profiles = context.getRegistry().lookupByType(ThreadPoolProfile.class);
1042            if (profiles != null && !profiles.isEmpty()) {
1043                for (String id : profiles.keySet()) {
1044                    ThreadPoolProfile profile = profiles.get(id);
1045                    // do not add if already added, for instance a tracer that is also an InterceptStrategy class
1046                    if (profile.isDefaultProfile()) {
1047                        LOG.info("Using custom default ThreadPoolProfile with id: " + id + " and implementation: " + profile);
1048                        context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile);
1049                        defaultIds.add(id);
1050                    } else {
1051                        context.getExecutorServiceStrategy().registerThreadPoolProfile(profile);
1052                    }
1053                }
1054            }
1055    
1056            // use custom profiles defined in the CamelContext
1057            if (threadPoolProfiles != null && !threadPoolProfiles.isEmpty()) {
1058                for (ThreadPoolProfileDefinition profile : threadPoolProfiles) {
1059                    if (profile.isDefaultProfile()) {
1060                        LOG.info("Using custom default ThreadPoolProfile with id: " + profile.getId() + " and implementation: " + profile);
1061                        context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile);
1062                        defaultIds.add(profile.getId());
1063                    } else {
1064                        context.getExecutorServiceStrategy().registerThreadPoolProfile(profile);
1065                    }
1066                }
1067            }
1068    
1069            // validate at most one is defined
1070            if (defaultIds.size() > 1) {
1071                throw new IllegalArgumentException("Only exactly one default ThreadPoolProfile is allowed, was " + defaultIds.size() + " ids: " + defaultIds);
1072            }
1073        }
1074    
1075        /**
1076         * Strategy to install all available routes into the context
1077         */
1078        protected void installRoutes() throws Exception {
1079            List<RouteBuilder> builders = new ArrayList<RouteBuilder>();
1080    
1081            // lets add route builders added from references
1082            if (builderRefs != null) {
1083                for (RouteBuilderDefinition builderRef : builderRefs) {
1084                    RouteBuilder builder = builderRef.createRouteBuilder(getContext());
1085                    if (builder != null) {
1086                        builders.add(builder);
1087                    } else {
1088                        // support to get the route here
1089                        RoutesBuilder routes = builderRef.createRoutes(getContext());
1090                        if (routes != null) {
1091                            this.builders.add(routes);
1092                        } else {
1093                            // Throw the exception that we can't find any build here
1094                            throw new CamelException("Cannot find any routes with this RouteBuilder reference: " + builderRef);
1095                        }
1096                    }
1097                }
1098            }
1099    
1100            // install already configured routes
1101            for (RoutesBuilder routeBuilder : this.builders) {
1102                getContext().addRoutes(routeBuilder);
1103            }
1104    
1105            // install builders
1106            for (RouteBuilder builder : builders) {
1107                if (beanPostProcessor != null) {
1108                    // Inject the annotated resource
1109                    beanPostProcessor.postProcessBeforeInitialization(builder, builder.toString());
1110                }
1111                getContext().addRoutes(builder);
1112            }
1113        }
1114    
1115        /**
1116         * Strategy method to try find {@link RouteBuilder} instances on the classpath
1117         */
1118        protected void findRouteBuilders() throws Exception {
1119            PackageScanClassResolver resolver = getContext().getPackageScanClassResolver();
1120            addPackageElementContentsToScanDefinition();
1121    
1122            PackageScanDefinition packageScanDef = getPackageScan();
1123            if (packageScanDef != null && packageScanDef.getPackages().size() > 0) {
1124                // use package scan filter
1125                PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter();
1126                // support property placeholders in include and exclude
1127                for (String include : packageScanDef.getIncludes()) {
1128                    include = getContext().resolvePropertyPlaceholders(include);
1129                    filter.addIncludePattern(include);
1130                }
1131                for (String exclude : packageScanDef.getExcludes()) {
1132                    exclude = getContext().resolvePropertyPlaceholders(exclude);
1133                    filter.addExcludePattern(exclude);
1134                }
1135                resolver.addFilter(filter);
1136    
1137                String[] normalized = normalizePackages(getContext(), packageScanDef.getPackages());
1138                RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), normalized, getContextClassLoaderOnStart(),
1139                        getBeanPostProcessor(), getContext().getPackageScanClassResolver());
1140                finder.appendBuilders(builders);
1141            }
1142        }
1143    
1144        private void addPackageElementContentsToScanDefinition() {
1145            PackageScanDefinition packageScanDef = getPackageScan();
1146    
1147            if (getPackages() != null && getPackages().length > 0) {
1148                if (packageScanDef == null) {
1149                    packageScanDef = new PackageScanDefinition();
1150                    setPackageScan(packageScanDef);
1151                }
1152    
1153                for (String pkg : getPackages()) {
1154                    packageScanDef.getPackages().add(pkg);
1155                }
1156            }
1157        }
1158    
1159        private String[] normalizePackages(CamelContext context, List<String> unnormalized) throws Exception {
1160            List<String> packages = new ArrayList<String>();
1161            for (String name : unnormalized) {
1162                // it may use property placeholders
1163                name = context.resolvePropertyPlaceholders(name);
1164                name = ObjectHelper.normalizeClassName(name);
1165                if (ObjectHelper.isNotEmpty(name)) {
1166                    if (LOG.isTraceEnabled()) {
1167                        LOG.trace("Using package: " + name + " to scan for RouteBuilder classes");
1168                    }
1169                    packages.add(name);
1170                }
1171            }
1172            return packages.toArray(new String[packages.size()]);
1173        }
1174    
1175    }