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 javax.xml.bind.annotation.XmlAccessType;
022    import javax.xml.bind.annotation.XmlAccessorType;
023    import javax.xml.bind.annotation.XmlAttribute;
024    import javax.xml.bind.annotation.XmlElement;
025    import javax.xml.bind.annotation.XmlElements;
026    import javax.xml.bind.annotation.XmlRootElement;
027    import javax.xml.bind.annotation.XmlTransient;
028    
029    import org.apache.camel.CamelContext;
030    import org.apache.camel.RoutesBuilder;
031    import org.apache.camel.ShutdownRoute;
032    import org.apache.camel.ShutdownRunningTask;
033    import org.apache.camel.builder.RouteBuilder;
034    import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
035    import org.apache.camel.core.xml.CamelJMXAgentDefinition;
036    import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition;
037    import org.apache.camel.core.xml.CamelProxyFactoryDefinition;
038    import org.apache.camel.core.xml.CamelServiceExporterDefinition;
039    import org.apache.camel.impl.DefaultCamelContextNameStrategy;
040    import org.apache.camel.model.ContextScanDefinition;
041    import org.apache.camel.model.InterceptDefinition;
042    import org.apache.camel.model.InterceptFromDefinition;
043    import org.apache.camel.model.InterceptSendToEndpointDefinition;
044    import org.apache.camel.model.OnCompletionDefinition;
045    import org.apache.camel.model.OnExceptionDefinition;
046    import org.apache.camel.model.PackageScanDefinition;
047    import org.apache.camel.model.RouteBuilderDefinition;
048    import org.apache.camel.model.RouteContextRefDefinition;
049    import org.apache.camel.model.RouteDefinition;
050    import org.apache.camel.model.ThreadPoolProfileDefinition;
051    import org.apache.camel.model.config.PropertiesDefinition;
052    import org.apache.camel.model.dataformat.DataFormatsDefinition;
053    import org.apache.camel.spi.PackageScanFilter;
054    import org.apache.camel.spi.Registry;
055    import org.apache.camel.util.ObjectHelper;
056    import org.apache.commons.logging.Log;
057    import org.apache.commons.logging.LogFactory;
058    import org.springframework.beans.factory.DisposableBean;
059    import org.springframework.beans.factory.FactoryBean;
060    import org.springframework.beans.factory.InitializingBean;
061    import org.springframework.beans.factory.config.BeanPostProcessor;
062    import org.springframework.context.ApplicationContext;
063    import org.springframework.context.ApplicationContextAware;
064    import org.springframework.context.ApplicationEvent;
065    import org.springframework.context.ApplicationListener;
066    import org.springframework.context.event.ContextRefreshedEvent;
067    
068    import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
069    
070    /**
071     * A Spring {@link FactoryBean} to create and initialize a
072     * {@link SpringCamelContext} and install routes either explicitly configured in
073     * Spring XML or found by searching the classpath for Java classes which extend
074     * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
075     *
076     * @version $Revision: 1051484 $
077     */
078    @XmlRootElement(name = "camelContext")
079    @XmlAccessorType(XmlAccessType.FIELD)
080    @SuppressWarnings("unused")
081    public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<SpringCamelContext>
082            implements FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener {
083        private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class);
084    
085        @XmlAttribute(name = "depends-on", required = false)
086        private String dependsOn;
087        @XmlAttribute(required = false)
088        private String trace;
089        @XmlAttribute(required = false)
090        private String streamCache;
091        @XmlAttribute(required = false)
092        private String delayer;
093        @XmlAttribute(required = false)
094        private String handleFault;
095        @XmlAttribute(required = false)
096        private String errorHandlerRef;
097        @XmlAttribute(required = false)
098        private String autoStartup;
099        @XmlAttribute(required = false)
100        private ShutdownRoute shutdownRoute;
101        @XmlAttribute(required = false)
102        private ShutdownRunningTask shutdownRunningTask;
103        @XmlAttribute(required = false)
104        private Boolean lazyLoadTypeConverters;
105        @XmlElement(name = "properties", required = false)
106        private PropertiesDefinition properties;
107        @XmlElement(name = "propertyPlaceholder", type = CamelPropertyPlaceholderDefinition.class, required = false)
108        private CamelPropertyPlaceholderDefinition camelPropertyPlaceholder;
109        @XmlElement(name = "package", required = false)
110        private String[] packages = {};
111        @XmlElement(name = "packageScan", type = PackageScanDefinition.class, required = false)
112        private PackageScanDefinition packageScan;
113        @XmlElement(name = "contextScan", type = ContextScanDefinition.class, required = false)
114        private ContextScanDefinition contextScan;
115        @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class, required = false)
116        private CamelJMXAgentDefinition camelJMXAgent;
117        @XmlElements({
118                @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false),
119                @XmlElement(name = "template", type = CamelProducerTemplateFactoryBean.class, required = false),
120                @XmlElement(name = "consumerTemplate", type = CamelConsumerTemplateFactoryBean.class, required = false),
121                @XmlElement(name = "proxy", type = CamelProxyFactoryDefinition.class, required = false),
122                @XmlElement(name = "export", type = CamelServiceExporterDefinition.class, required = false),
123                @XmlElement(name = "errorHandler", type = ErrorHandlerDefinition.class, required = false)})
124        private List beans;
125        @XmlElement(name = "routeBuilder", required = false)
126        private List<RouteBuilderDefinition> builderRefs = new ArrayList<RouteBuilderDefinition>();
127        @XmlElement(name = "routeContextRef", required = false)
128        private List<RouteContextRefDefinition> routeRefs = new ArrayList<RouteContextRefDefinition>();
129        @XmlElement(name = "threadPoolProfile", required = false)
130        private List<ThreadPoolProfileDefinition> threadPoolProfiles;
131        @XmlElement(name = "threadPool", required = false)
132        private List<CamelThreadPoolFactoryBean> threadPools;
133        @XmlElement(name = "endpoint", required = false)
134        private List<CamelEndpointFactoryBean> endpoints;
135        @XmlElement(name = "dataFormats", required = false)
136        private DataFormatsDefinition dataFormats;
137        @XmlElement(name = "onException", required = false)
138        private List<OnExceptionDefinition> onExceptions = new ArrayList<OnExceptionDefinition>();
139        @XmlElement(name = "onCompletion", required = false)
140        private List<OnCompletionDefinition> onCompletions = new ArrayList<OnCompletionDefinition>();
141        @XmlElement(name = "intercept", required = false)
142        private List<InterceptDefinition> intercepts = new ArrayList<InterceptDefinition>();
143        @XmlElement(name = "interceptFrom", required = false)
144        private List<InterceptFromDefinition> interceptFroms = new ArrayList<InterceptFromDefinition>();
145        @XmlElement(name = "interceptSendToEndpoint", required = false)
146        private List<InterceptSendToEndpointDefinition> interceptSendToEndpoints = new ArrayList<InterceptSendToEndpointDefinition>();
147        @XmlElement(name = "route", required = false)
148        private List<RouteDefinition> routes = new ArrayList<RouteDefinition>();
149        @XmlTransient
150        private SpringCamelContext context;
151        @XmlTransient
152        private ClassLoader contextClassLoaderOnStart;
153        @XmlTransient
154        private ApplicationContext applicationContext;
155        @XmlTransient
156        private BeanPostProcessor beanPostProcessor;
157        @XmlTransient
158        private boolean implicitId;
159        
160    
161        @Override
162        public Class getObjectType() {
163            return SpringCamelContext.class;
164        }
165        
166        protected <S> S getBeanForType(Class<S> clazz) {
167            S bean = null;
168            String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true);
169            if (names.length == 1) {
170                bean = (S) getApplicationContext().getBean(names[0], clazz);
171            }
172            if (bean == null) {
173                ApplicationContext parentContext = getApplicationContext().getParent();
174                if (parentContext != null) {
175                    names = parentContext.getBeanNamesForType(clazz, true, true);
176                    if (names.length == 1) {
177                        bean = (S) parentContext.getBean(names[0], clazz);
178                    }
179                }
180            }
181            return bean;
182        }
183    
184        @Override
185        protected void findRouteBuildersByPackageScan(String[] packages, PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception {
186            // add filter to class resolver which then will filter
187            getContext().getPackageScanClassResolver().addFilter(filter);
188    
189            PackageScanRouteBuilderFinder finder = new PackageScanRouteBuilderFinder(getContext(), packages, getContextClassLoaderOnStart(),
190                                                                                     getBeanPostProcessor(), getContext().getPackageScanClassResolver());
191            finder.appendBuilders(builders);
192    
193            // and remove the filter
194            getContext().getPackageScanClassResolver().removeFilter(filter);
195        }
196    
197        @Override
198        protected void findRouteBuildersByContextScan(PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception {
199            ContextScanRouteBuilderFinder finder = new ContextScanRouteBuilderFinder(getContext(), filter);
200            finder.appendBuilders(builders);
201        }
202    
203        protected void initBeanPostProcessor(SpringCamelContext context) {
204            if (beanPostProcessor != null) {
205                if (beanPostProcessor instanceof ApplicationContextAware) {
206                    ((ApplicationContextAware) beanPostProcessor).setApplicationContext(applicationContext);
207                }
208                if (beanPostProcessor instanceof CamelBeanPostProcessor) {
209                    ((CamelBeanPostProcessor) beanPostProcessor).setCamelContext(getContext());
210                }
211            }
212        }
213    
214        protected void postProcessBeforeInit(RouteBuilder builder) {
215            if (beanPostProcessor != null) {
216                // Inject the annotated resource
217                beanPostProcessor.postProcessBeforeInitialization(builder, builder.toString());
218            }
219        }
220    
221        protected void initCustomRegistry(SpringCamelContext context) {
222            Registry registry = getBeanForType(Registry.class);
223            if (registry != null) {
224                LOG.info("Using custom Registry: " + registry);
225                context.setRegistry(registry);
226            }
227        }
228    
229        public void onApplicationEvent(ApplicationEvent event) {
230            // From Spring 3.0.1, The BeanFactory applicationEventListener 
231            // and Bean's applicationEventListener will be called,
232            // So we just delegate the onApplicationEvent call here.
233    
234            SpringCamelContext context = getContext(false);
235            if (context != null) {
236                // let the spring camel context handle the events
237                context.onApplicationEvent(event);
238            } else {
239                if (LOG.isDebugEnabled()) {
240                    LOG.debug("Publishing spring-event: " + event);
241                }
242    
243                if (event instanceof ContextRefreshedEvent) {
244                    // now lets start the CamelContext so that all its possible
245                    // dependencies are initialized
246                    try {
247                        LOG.debug("Starting the context now!");
248                        getContext().start();
249                    } catch (Exception e) {
250                        throw wrapRuntimeCamelException(e);
251                    }
252                }
253            }
254        }
255    
256        // Properties
257        // -------------------------------------------------------------------------
258    
259        public ApplicationContext getApplicationContext() {
260            if (applicationContext == null) {
261                throw new IllegalArgumentException("No applicationContext has been injected!");
262            }
263            return applicationContext;
264        }
265    
266        public void setApplicationContext(ApplicationContext applicationContext) {
267            this.applicationContext = applicationContext;
268        }
269    
270        public void setBeanPostProcessor(BeanPostProcessor postProcessor) {
271            this.beanPostProcessor = postProcessor;
272        }
273    
274        public BeanPostProcessor getBeanPostProcessor() {
275            return beanPostProcessor;
276        }
277    
278        // Implementation methods
279        // -------------------------------------------------------------------------
280    
281        /**
282         * Create the context
283         */
284        protected SpringCamelContext createContext() {
285            SpringCamelContext ctx = newCamelContext();        
286            ctx.setName(getId());        
287            return ctx;
288        }
289    
290        protected SpringCamelContext newCamelContext() {
291            return new SpringCamelContext(getApplicationContext());
292        }
293    
294        public SpringCamelContext getContext(boolean create) {
295            if (context == null && create) {
296                context = createContext();
297            }
298            return context;
299        }
300    
301        public void setContext(SpringCamelContext context) {
302            this.context = context;
303        }
304    
305        public List<RouteDefinition> getRoutes() {
306            return routes;
307        }
308    
309        public void setRoutes(List<RouteDefinition> routes) {
310            this.routes = routes;
311        }
312    
313        public List<InterceptDefinition> getIntercepts() {
314            return intercepts;
315        }
316    
317        public void setIntercepts(List<InterceptDefinition> intercepts) {
318            this.intercepts = intercepts;
319        }
320    
321        public List<InterceptFromDefinition> getInterceptFroms() {
322            return interceptFroms;
323        }
324    
325        public void setInterceptFroms(List<InterceptFromDefinition> interceptFroms) {
326            this.interceptFroms = interceptFroms;
327        }
328    
329        public List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints() {
330            return interceptSendToEndpoints;
331        }
332    
333        public void setInterceptSendToEndpoints(List<InterceptSendToEndpointDefinition> interceptSendToEndpoints) {
334            this.interceptSendToEndpoints = interceptSendToEndpoints;
335        }
336    
337        public PropertiesDefinition getProperties() {
338            return properties;
339        }
340    
341        public void setProperties(PropertiesDefinition properties) {
342            this.properties = properties;
343        }
344    
345        public String[] getPackages() {
346            return packages;
347        }
348    
349        /**
350         * Sets the package names to be recursively searched for Java classes which
351         * extend {@link org.apache.camel.builder.RouteBuilder} to be auto-wired up to the
352         * {@link CamelContext} as a route. Note that classes are excluded if
353         * they are specifically configured in the spring.xml
354         * <p/>
355         * A more advanced configuration can be done using {@link #setPackageScan(org.apache.camel.model.PackageScanDefinition)}
356         *
357         * @param packages the package names which are recursively searched
358         * @see #setPackageScan(org.apache.camel.model.PackageScanDefinition)
359         */
360        public void setPackages(String[] packages) {
361            this.packages = packages;
362        }
363    
364        public PackageScanDefinition getPackageScan() {
365            return packageScan;
366        }
367    
368        /**
369         * Sets the package scanning information. Package scanning allows for the
370         * automatic discovery of certain camel classes at runtime for inclusion
371         * e.g. {@link org.apache.camel.builder.RouteBuilder} implementations
372         *
373         * @param packageScan the package scan
374         */
375        public void setPackageScan(PackageScanDefinition packageScan) {
376            this.packageScan = packageScan;
377        }
378    
379        public ContextScanDefinition getContextScan() {
380            return contextScan;
381        }
382    
383        /**
384         * Sets the context scanning (eg Spring's ApplicationContext) information.
385         * Context scanning allows for the automatic discovery of Camel routes runtime for inclusion
386         * e.g. {@link org.apache.camel.builder.RouteBuilder} implementations
387         *
388         * @param contextScan the context scan
389         */
390        public void setContextScan(ContextScanDefinition contextScan) {
391            this.contextScan = contextScan;
392        }
393    
394        public CamelPropertyPlaceholderDefinition getCamelPropertyPlaceholder() {
395            return camelPropertyPlaceholder;
396        }
397    
398        public void setCamelPropertyPlaceholder(CamelPropertyPlaceholderDefinition camelPropertyPlaceholder) {
399            this.camelPropertyPlaceholder = camelPropertyPlaceholder;
400        }
401    
402        public void setCamelJMXAgent(CamelJMXAgentDefinition agent) {
403            camelJMXAgent = agent;
404        }
405    
406        public String getTrace() {
407            return trace;
408        }
409    
410        public void setTrace(String trace) {
411            this.trace = trace;
412        }
413    
414        public String getStreamCache() {
415            return streamCache;
416        }
417    
418        public void setStreamCache(String streamCache) {
419            this.streamCache = streamCache;
420        }
421    
422        public String getDelayer() {
423            return delayer;
424        }
425    
426        public void setDelayer(String delayer) {
427            this.delayer = delayer;
428        }
429    
430        public String getHandleFault() {
431            return handleFault;
432        }
433    
434        public void setHandleFault(String handleFault) {
435            this.handleFault = handleFault;
436        }
437    
438        public String getAutoStartup() {
439            return autoStartup;
440        }
441    
442        public void setAutoStartup(String autoStartup) {
443            this.autoStartup = autoStartup;
444        }
445    
446        public Boolean getLazyLoadTypeConverters() {
447            return lazyLoadTypeConverters;
448        }
449    
450        public void setLazyLoadTypeConverters(Boolean lazyLoadTypeConverters) {
451            this.lazyLoadTypeConverters = lazyLoadTypeConverters;
452        }
453    
454        public CamelJMXAgentDefinition getCamelJMXAgent() {
455            return camelJMXAgent;
456        }
457    
458        public List<RouteBuilderDefinition> getBuilderRefs() {
459            return builderRefs;
460        }
461    
462        public void setBuilderRefs(List<RouteBuilderDefinition> builderRefs) {
463            this.builderRefs = builderRefs;
464        }
465    
466        public List<RouteContextRefDefinition> getRouteRefs() {
467            return routeRefs;
468        }
469    
470        public void setRouteRefs(List<RouteContextRefDefinition> routeRefs) {
471            this.routeRefs = routeRefs;
472        }
473    
474        public String getErrorHandlerRef() {
475            return errorHandlerRef;
476        }
477    
478        /**
479         * Sets the name of the error handler object used to default the error handling strategy
480         *
481         * @param errorHandlerRef the Spring bean ref of the error handler
482         */
483        public void setErrorHandlerRef(String errorHandlerRef) {
484            this.errorHandlerRef = errorHandlerRef;
485        }
486    
487        public void setDataFormats(DataFormatsDefinition dataFormats) {
488            this.dataFormats = dataFormats;
489        }
490    
491        public DataFormatsDefinition getDataFormats() {
492            return dataFormats;
493        }
494    
495        public void setOnExceptions(List<OnExceptionDefinition> onExceptions) {
496            this.onExceptions = onExceptions;
497        }
498    
499        public List<OnExceptionDefinition> getOnExceptions() {
500            return onExceptions;
501        }
502    
503        public List<OnCompletionDefinition> getOnCompletions() {
504            return onCompletions;
505        }
506    
507        public void setOnCompletions(List<OnCompletionDefinition> onCompletions) {
508            this.onCompletions = onCompletions;
509        }
510    
511        public ShutdownRoute getShutdownRoute() {
512            return shutdownRoute;
513        }
514    
515        public void setShutdownRoute(ShutdownRoute shutdownRoute) {
516            this.shutdownRoute = shutdownRoute;
517        }
518    
519        public ShutdownRunningTask getShutdownRunningTask() {
520            return shutdownRunningTask;
521        }
522    
523        public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
524            this.shutdownRunningTask = shutdownRunningTask;
525        }
526    
527        public List<ThreadPoolProfileDefinition> getThreadPoolProfiles() {
528            return threadPoolProfiles;
529        }
530    
531        public void setThreadPoolProfiles(List<ThreadPoolProfileDefinition> threadPoolProfiles) {
532            this.threadPoolProfiles = threadPoolProfiles;
533        }
534    
535        public String getDependsOn() {
536            return dependsOn;
537        }
538    
539        public void setDependsOn(String dependsOn) {
540            this.dependsOn = dependsOn;
541        }
542        
543        public boolean isImplicitId() {
544            return implicitId;
545        }
546        
547        public void setImplicitId(boolean flag) {
548            implicitId = flag;
549        }
550    
551    }