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    
023    import javax.management.MBeanServer;
024    import javax.xml.bind.annotation.XmlAccessType;
025    import javax.xml.bind.annotation.XmlAccessorType;
026    import javax.xml.bind.annotation.XmlAttribute;
027    import javax.xml.bind.annotation.XmlElement;
028    import javax.xml.bind.annotation.XmlElementRef;
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.RuntimeCamelException;
034    import org.apache.camel.builder.RouteBuilder;
035    import org.apache.camel.model.IdentifiedType;
036    import org.apache.camel.model.RouteBuilderRef;
037    import org.apache.camel.model.RouteContainer;
038    import org.apache.camel.model.RouteType;
039    import org.apache.camel.model.dataformat.DataFormatType;
040    import org.apache.camel.spi.InstrumentationAgent;
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.springframework.beans.factory.DisposableBean;
044    import org.springframework.beans.factory.FactoryBean;
045    import org.springframework.beans.factory.InitializingBean;
046    import org.springframework.context.ApplicationContext;
047    import org.springframework.context.ApplicationContextAware;
048    import org.springframework.context.ApplicationEvent;
049    import org.springframework.context.ApplicationListener;
050    import org.springframework.context.event.ContextRefreshedEvent;
051    
052    /**
053     * A Spring {@link FactoryBean} to create and initialize a
054     * {@link SpringCamelContext} and install routes either explicitly configured in
055     * Spring XML or found by searching the classpath for Java classes which extend
056     * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
057     *
058     * @version $Revision: 641676 $
059     */
060    @XmlRootElement(name = "camelContext")
061    @XmlAccessorType(XmlAccessType.FIELD)
062    public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener {
063        private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class);
064        @XmlAttribute(required = false)
065        private Boolean useJmx;
066        @XmlAttribute(required = false)
067        private String mbeanServer;
068        @XmlAttribute(required = false)
069        private Boolean autowireRouteBuilders = Boolean.TRUE;
070        @XmlElement(name = "package", required = false)
071        private String[] packages = {};
072        @XmlElements({
073            @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false),
074            @XmlElement(name = "template", type = CamelTemplateFactoryBean.class, required = false),
075            @XmlElement(name = "proxy", type = CamelProxyFactoryType.class, required = false),
076            @XmlElement(name = "export", type = CamelServiceExporterType.class, required = false),
077            @XmlElement(name = "jmxAgent", required = false)})
078        private List beans;
079        @XmlElement(name = "routeBuilderRef", required = false)
080        private List<RouteBuilderRef> builderRefs = new ArrayList<RouteBuilderRef>();
081        @XmlElement(name = "endpoint", required = false)
082        private List<EndpointFactoryBean> endpoints;
083        @XmlElementRef
084        private List<DataFormatType> dataFormats;
085        @XmlElement(name = "route", required = false)
086        private List<RouteType> routes = new ArrayList<RouteType>();
087        @XmlTransient
088        private SpringCamelContext context;
089        @XmlTransient
090        private RouteBuilder routeBuilder;
091        @XmlTransient
092        private List<RouteBuilder> additionalBuilders = new ArrayList<RouteBuilder>();
093        @XmlTransient
094        private ApplicationContext applicationContext;
095        @XmlTransient
096        private ClassLoader contextClassLoaderOnStart;
097        @XmlTransient
098        private InstrumentationAgent instrumentationAgent;
099    
100        public CamelContextFactoryBean() {
101    
102            // Lets keep track of the class loader for when we actually do start things up
103            contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader();
104        }
105    
106        public Object getObject() throws Exception {
107            return getContext();
108        }
109    
110        public Class getObjectType() {
111            return SpringCamelContext.class;
112        }
113    
114        public boolean isSingleton() {
115            return true;
116        }
117    
118        public void afterPropertiesSet() throws Exception {
119            // lets force any lazy creation
120            getContext().addRouteDefinitions(routes);
121    
122            if (instrumentationAgent == null && isJmxEnabled()) {
123                SpringInstrumentationAgent agent = new SpringInstrumentationAgent();
124                agent.setCamelContext(getContext());
125                String name = getMbeanServer();
126                if (name != null) {
127                    MBeanServer mbeanServer = (MBeanServer) getApplicationContext().getBean(name, MBeanServer.class);
128                    agent.setMBeanServer(mbeanServer);
129                }
130                instrumentationAgent = agent;
131                instrumentationAgent.start();
132            }
133    
134            LOG.debug("Found JAXB created routes: " + getRoutes());
135    
136            findRouteBuiders();
137            installRoutes();
138        }
139    
140        public void destroy() throws Exception {
141            getContext().stop();
142        }
143    
144        public void onApplicationEvent(ApplicationEvent event) {
145            if (LOG.isDebugEnabled()) {
146                LOG.debug("Publishing event: " + event);
147            }
148    
149            if (event instanceof ContextRefreshedEvent) {
150                // now lets start the CamelContext so that all its possible
151                // dependencies are initailized
152                try {
153                    LOG.debug("Starting the context now!");
154                    getContext().start();
155                } catch (Exception e) {
156                    throw new RuntimeCamelException(e);
157                }
158            }
159            /*
160             * if (context != null) { context.onApplicationEvent(event); }
161             */
162        }
163    
164        // Properties
165        // -------------------------------------------------------------------------
166        public SpringCamelContext getContext() throws Exception {
167            if (context == null) {
168                context = createContext();
169            }
170            return context;
171        }
172    
173        public void setContext(SpringCamelContext context) {
174            this.context = context;
175        }
176    
177        public List<RouteType> getRoutes() {
178            return routes;
179        }
180    
181        public void setRoutes(List<RouteType> routes) {
182            this.routes = routes;
183        }
184    
185        public RouteBuilder getRouteBuilder() {
186            return routeBuilder;
187        }
188    
189        /**
190         * Set a single {@link RouteBuilder} to be used to create the default routes
191         * on startup
192         */
193        public void setRouteBuilder(RouteBuilder routeBuilder) {
194            this.routeBuilder = routeBuilder;
195        }
196    
197        /**
198         * Set a collection of {@link RouteBuilder} instances to be used to create
199         * the default routes on startup
200         */
201        public void setRouteBuilders(RouteBuilder[] builders) {
202            for (RouteBuilder builder : builders) {
203                additionalBuilders.add(builder);
204            }
205        }
206    
207        public ApplicationContext getApplicationContext() {
208            if (applicationContext == null) {
209                throw new IllegalArgumentException("No applicationContext has been injected!");
210            }
211            return applicationContext;
212        }
213    
214        public void setApplicationContext(ApplicationContext applicationContext) {
215            this.applicationContext = applicationContext;
216        }
217    
218        public String[] getPackages() {
219            return packages;
220        }
221    
222        /**
223         * Sets the package names to be recursively searched for Java classes which
224         * extend {@link RouteBuilder} to be auto-wired up to the
225         * {@link SpringCamelContext} as a route. Note that classes are excluded if
226         * they are specifically configured in the spring.xml
227         *
228         * @param packages the package names which are recursively searched
229         */
230        public void setPackages(String[] packages) {
231            this.packages = packages;
232        }
233    
234        public String getMbeanServer() {
235            return mbeanServer;
236        }
237    
238        public void setMbeanServer(String mbeanServer) {
239            this.mbeanServer = mbeanServer;
240        }
241    
242        public boolean isJmxEnabled() {
243            return useJmx != null && useJmx.booleanValue();
244        }
245    
246        public Boolean getUseJmx() {
247            return useJmx;
248        }
249    
250        public void setUseJmx(Boolean useJmx) {
251            this.useJmx = useJmx;
252        }
253    
254        public List<RouteBuilderRef> getBuilderRefs() {
255            return builderRefs;
256        }
257    
258        public void setBuilderRefs(List<RouteBuilderRef> builderRefs) {
259            this.builderRefs = builderRefs;
260        }
261    
262        /**
263         * If enabled this will force all {@link RouteBuilder} classes configured in the Spring
264         * {@link ApplicationContext} to be registered automatically with this CamelContext.
265         */
266        public void setAutowireRouteBuilders(Boolean autowireRouteBuilders) {
267            this.autowireRouteBuilders = autowireRouteBuilders;
268        }
269    
270        // Implementation methods
271        // -------------------------------------------------------------------------
272    
273        /**
274         * Create the context
275         */
276        protected SpringCamelContext createContext() {
277            SpringCamelContext ctx = new SpringCamelContext(getApplicationContext());
278            ctx.setName(getId());
279            return ctx;
280        }
281    
282        /**
283         * Strategy to install all available routes into the context
284         */
285        protected void installRoutes() throws Exception {
286            if (autowireRouteBuilders != null && autowireRouteBuilders.booleanValue()) {
287                Map builders = getApplicationContext().getBeansOfType(RouteBuilder.class, true, true);
288                if (builders != null) {
289                    for (Object builder : builders.values()) {
290                        getContext().addRoutes((RouteBuilder) builder);
291                    }
292                }
293            }
294            for (RouteBuilder routeBuilder : additionalBuilders) {
295                getContext().addRoutes(routeBuilder);
296            }
297            if (routeBuilder != null) {
298                getContext().addRoutes(routeBuilder);
299            }
300    
301            // lets add route builders added from references
302            if (builderRefs != null) {
303                for (RouteBuilderRef builderRef : builderRefs) {
304                    RouteBuilder builder = builderRef.createRouteBuilder(getContext());
305                    getContext().addRoutes(builder);
306                }
307            }
308        }
309    
310        /**
311         * Strategy method to try find {@link RouteBuilder} instances on the
312         * classpath
313         */
314        protected void findRouteBuiders() throws Exception, InstantiationException {
315            if (packages != null && packages.length > 0) {
316                RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), packages, contextClassLoaderOnStart);
317                finder.appendBuilders(additionalBuilders);
318            }
319        }
320    }