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    
022    import javax.xml.bind.annotation.XmlAccessType;
023    import javax.xml.bind.annotation.XmlAccessorType;
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.RuntimeCamelException;
030    import org.apache.camel.builder.RouteBuilder;
031    import org.apache.camel.model.IdentifiedType;
032    import org.apache.camel.model.RouteContainer;
033    import org.apache.camel.model.RouteType;
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    
037    import org.springframework.beans.factory.DisposableBean;
038    import org.springframework.beans.factory.FactoryBean;
039    import org.springframework.beans.factory.InitializingBean;
040    import org.springframework.context.ApplicationContext;
041    import org.springframework.context.ApplicationContextAware;
042    import org.springframework.context.ApplicationEvent;
043    import org.springframework.context.ApplicationListener;
044    import org.springframework.context.event.ContextRefreshedEvent;
045    
046    /**
047     * A Spring {@link FactoryBean} to create and initialize a
048     * {@link SpringCamelContext} and install routes either explicitly configured in
049     * Spring XML or found by searching the classpath for Java classes which extend
050     * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
051     * 
052     * @version $Revision: 565270 $
053     */
054    @XmlRootElement(name = "camelContext")
055    @XmlAccessorType(XmlAccessType.FIELD)
056    public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener {
057        private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class);
058        @XmlElement(name = "package", required = false)
059        private String[] packages = {};
060        @XmlElements({@XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false), @XmlElement(name = "proxy", type = CamelProxyFactoryType.class, required = false),
061                       @XmlElement(name = "export", type = CamelServiceExporterType.class, required = false) })
062        private List beans;
063        @XmlElement(name = "endpoint", required = false)
064        private List<EndpointFactoryBean> endpoints;
065        @XmlElement(name = "route", required = false)
066        private List<RouteType> routes = new ArrayList<RouteType>();
067        @XmlTransient
068        private SpringCamelContext context;
069        @XmlTransient
070        private RouteBuilder routeBuilder;
071        @XmlTransient
072        private List<RouteBuilder> additionalBuilders = new ArrayList<RouteBuilder>();
073        @XmlTransient
074        private ApplicationContext applicationContext;
075    
076        public Object getObject() throws Exception {
077            return getContext();
078        }
079    
080        public Class getObjectType() {
081            return SpringCamelContext.class;
082        }
083    
084        public boolean isSingleton() {
085            return true;
086        }
087    
088        public void afterPropertiesSet() throws Exception {
089    
090            // lets force any lazy creation
091            getContext();
092    
093            LOG.debug("Found JAXB created routes: " + getRoutes());
094            String[] names = applicationContext.getBeanNamesForType(SpringInstrumentationAgent.class);
095            if (names.length == 1) {
096                applicationContext.getBean(names[0], SpringInstrumentationAgent.class);
097            }
098            
099            findRouteBuiders();
100            installRoutes();
101        }
102    
103        public void destroy() throws Exception {
104            getContext().stop();
105        }
106    
107        public void onApplicationEvent(ApplicationEvent event) {
108            if (LOG.isDebugEnabled()) {
109                LOG.debug("Publishing event: " + event);
110            }
111    
112            if (event instanceof ContextRefreshedEvent) {
113                // now lets start the CamelContext so that all its possible
114                // dependencies are initailized
115                try {
116                    LOG.debug("Starting the context now!");
117                    getContext().start();
118                } catch (Exception e) {
119                    throw new RuntimeCamelException(e);
120                }
121            }
122            /*
123             * if (context != null) { context.onApplicationEvent(event); }
124             */
125        }
126    
127        // Properties
128        // -------------------------------------------------------------------------
129        public SpringCamelContext getContext() throws Exception {
130            if (context == null) {
131                context = new SpringCamelContext(getApplicationContext());
132            }
133            return context;
134        }
135    
136        public void setContext(SpringCamelContext context) {
137            this.context = context;
138        }
139    
140        public List<RouteType> getRoutes() {
141            return routes;
142        }
143    
144        public void setRoutes(List<RouteType> routes) {
145            this.routes = routes;
146        }
147    
148        public RouteBuilder getRouteBuilder() {
149            return routeBuilder;
150        }
151    
152        /**
153         * Set a single {@link RouteBuilder} to be used to create the default routes
154         * on startup
155         */
156        public void setRouteBuilder(RouteBuilder routeBuilder) {
157            this.routeBuilder = routeBuilder;
158        }
159    
160        /**
161         * Set a collection of {@link RouteBuilder} instances to be used to create
162         * the default routes on startup
163         */
164        public void setRouteBuilders(RouteBuilder[] builders) {
165            for (RouteBuilder builder : builders) {
166                additionalBuilders.add(builder);
167            }
168        }
169    
170        public ApplicationContext getApplicationContext() {
171            return applicationContext;
172        }
173    
174        public void setApplicationContext(ApplicationContext applicationContext) {
175            this.applicationContext = applicationContext;
176        }
177    
178        public String[] getPackages() {
179            return packages;
180        }
181    
182        /**
183         * Sets the package names to be recursively searched for Java classes which
184         * extend {@link RouteBuilder} to be auto-wired up to the
185         * {@link SpringCamelContext} as a route. Note that classes are excluded if
186         * they are specifically configured in the spring.xml
187         * 
188         * @param packages the package names which are recursively searched
189         */
190        public void setPackages(String[] packages) {
191            this.packages = packages;
192        }
193    
194        // Implementation methods
195        // -------------------------------------------------------------------------
196    
197        /**
198         * Strategy to install all available routes into the context
199         */
200        protected void installRoutes() throws Exception {
201            for (RouteBuilder routeBuilder : additionalBuilders) {
202                getContext().addRoutes(routeBuilder);
203            }
204            if (routeBuilder != null) {
205                getContext().addRoutes(routeBuilder);
206            }
207            for (RouteType route : routes) {
208                route.addRoutes(getContext());
209            }
210        }
211    
212        /**
213         * Strategy method to try find {@link RouteBuilder} instances on the
214         * classpath
215         */
216        protected void findRouteBuiders() throws Exception, InstantiationException {
217            if (packages != null && packages.length > 0) {
218                RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), packages);
219                finder.appendBuilders(additionalBuilders);
220            }
221        }
222    
223    }