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.xml.bind.annotation.XmlAccessType;
024 import javax.xml.bind.annotation.XmlAccessorType;
025 import javax.xml.bind.annotation.XmlAttribute;
026 import javax.xml.bind.annotation.XmlElement;
027 import javax.xml.bind.annotation.XmlElementRef;
028 import javax.xml.bind.annotation.XmlElements;
029 import javax.xml.bind.annotation.XmlRootElement;
030 import javax.xml.bind.annotation.XmlTransient;
031
032 import org.apache.camel.RuntimeCamelException;
033 import org.apache.camel.builder.ErrorHandlerBuilder;
034 import org.apache.camel.builder.RouteBuilder;
035 import org.apache.camel.impl.DefaultLifecycleStrategy;
036 import org.apache.camel.management.DefaultInstrumentationAgent;
037 import org.apache.camel.management.InstrumentationLifecycleStrategy;
038 import org.apache.camel.model.IdentifiedType;
039 import org.apache.camel.model.RouteBuilderRef;
040 import org.apache.camel.model.RouteContainer;
041 import org.apache.camel.model.RouteType;
042 import org.apache.camel.model.dataformat.DataFormatType;
043 import org.apache.camel.processor.interceptor.Debugger;
044 import org.apache.camel.processor.interceptor.Tracer;
045 import org.apache.camel.spi.LifecycleStrategy;
046 import org.apache.camel.spi.Registry;
047 import org.apache.commons.logging.Log;
048 import org.apache.commons.logging.LogFactory;
049 import org.springframework.beans.factory.DisposableBean;
050 import org.springframework.beans.factory.FactoryBean;
051 import org.springframework.beans.factory.InitializingBean;
052 import org.springframework.beans.factory.config.BeanPostProcessor;
053 import org.springframework.context.ApplicationContext;
054 import org.springframework.context.ApplicationContextAware;
055 import org.springframework.context.ApplicationEvent;
056 import org.springframework.context.ApplicationListener;
057 import org.springframework.context.event.ContextRefreshedEvent;
058
059 /**
060 * A Spring {@link FactoryBean} to create and initialize a
061 * {@link SpringCamelContext} and install routes either explicitly configured in
062 * Spring XML or found by searching the classpath for Java classes which extend
063 * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
064 *
065 * @version $Revision: 674383 $
066 */
067 @XmlRootElement(name = "camelContext")
068 @XmlAccessorType(XmlAccessType.FIELD)
069 public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener {
070 private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class);
071
072 @XmlAttribute(required = false)
073 private Boolean useJmx = Boolean.TRUE;
074 @XmlAttribute(required = false)
075 private Boolean autowireRouteBuilders = Boolean.TRUE;
076 @XmlAttribute(required = false)
077 private Boolean trace;
078 @XmlAttribute(required = false)
079 private String errorHandlerRef;
080 @XmlElement(name = "package", required = false)
081 private String[] packages = {};
082 @XmlElement(name = "jmxAgent", type = CamelJMXAgentType.class, required = false)
083 private CamelJMXAgentType camelJMXAgent;
084 @XmlElements({
085 @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false),
086 @XmlElement(name = "template", type = CamelTemplateFactoryBean.class, required = false),
087 @XmlElement(name = "proxy", type = CamelProxyFactoryType.class, required = false),
088 @XmlElement(name = "export", type = CamelServiceExporterType.class, required = false)})
089 private List beans;
090 @XmlElement(name = "routeBuilderRef", required = false)
091 private List<RouteBuilderRef> builderRefs = new ArrayList<RouteBuilderRef>();
092 @XmlElement(name = "endpoint", required = false)
093 private List<EndpointFactoryBean> endpoints;
094 @XmlElementRef
095 private List<DataFormatType> dataFormats;
096 @XmlElement(name = "route", required = false)
097 private List<RouteType> routes = new ArrayList<RouteType>();
098 @XmlTransient
099 private SpringCamelContext context;
100 @XmlTransient
101 private RouteBuilder routeBuilder;
102 @XmlTransient
103 private List<RouteBuilder> additionalBuilders = new ArrayList<RouteBuilder>();
104 @XmlTransient
105 private ApplicationContext applicationContext;
106 @XmlTransient
107 private ClassLoader contextClassLoaderOnStart;
108 @XmlTransient
109 private BeanPostProcessor beanPostProcessor;
110
111 public CamelContextFactoryBean() {
112 // Lets keep track of the class loader for when we actually do start things up
113 contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader();
114 }
115
116 public Object getObject() throws Exception {
117 return getContext();
118 }
119
120 public Class getObjectType() {
121 return SpringCamelContext.class;
122 }
123
124 public boolean isSingleton() {
125 return true;
126 }
127
128 public void afterPropertiesSet() throws Exception {
129 // lets see if we can find a debugger to add
130 // TODO there should be a neater way to do this!
131 Debugger debugger = getBeanForType(Debugger.class);
132 if (debugger != null) {
133 getContext().addInterceptStrategy(debugger);
134 }
135 Tracer tracer = getBeanForType(Tracer.class);
136 if (tracer != null) {
137 getContext().addInterceptStrategy(tracer);
138 }
139
140 // set the lifecycle strategy if defined
141 LifecycleStrategy lifecycleStrategy = getBeanForType(LifecycleStrategy.class);
142 if (lifecycleStrategy != null) {
143 getContext().setLifecycleStrategy(lifecycleStrategy);
144 }
145
146 // set the strategy if defined
147 Registry registry = getBeanForType(Registry.class);
148 if (registry != null) {
149 getContext().setRegistry(registry);
150 }
151
152 // Set the application context and camelContext for the beanPostProcessor
153 if (beanPostProcessor != null) {
154 if (beanPostProcessor instanceof ApplicationContextAware) {
155 ((ApplicationContextAware)beanPostProcessor).setApplicationContext(applicationContext);
156 }
157 if (beanPostProcessor instanceof CamelBeanPostProcessor) {
158 ((CamelBeanPostProcessor)beanPostProcessor).setCamelContext(getContext());
159 }
160 }
161
162 // lets force any lazy creation
163 getContext().addRouteDefinitions(routes);
164
165 if (!isJmxEnabled()
166 || (camelJMXAgent != null && camelJMXAgent.isDisabled() != null && camelJMXAgent.isDisabled())) {
167 LOG.debug("JMXAgent disabled");
168 getContext().setLifecycleStrategy(new DefaultLifecycleStrategy());
169 } else if (camelJMXAgent != null) {
170 LOG.debug("JMXAgent enabled");
171
172 if (lifecycleStrategy != null) {
173 LOG.warn("lifecycleStrategy will be overriden by InstrumentationLifecycleStrategy");
174 }
175
176 DefaultInstrumentationAgent agent = new DefaultInstrumentationAgent();
177 agent.setConnectorPort(camelJMXAgent.getConnectorPort());
178 agent.setCreateConnector(camelJMXAgent.isCreateConnector());
179 agent.setMBeanObjectDomainName(camelJMXAgent.getMbeanObjectDomainName());
180 agent.setMBeanServerDefaultDomain(camelJMXAgent.getMbeanServerDefaultDomain());
181 agent.setRegistryPort(camelJMXAgent.getRegistryPort());
182 agent.setServiceUrlPath(camelJMXAgent.getServiceUrlPath());
183 agent.setUsePlatformMBeanServer(camelJMXAgent.isUsePlatformMBeanServer());
184
185 getContext().setLifecycleStrategy(new InstrumentationLifecycleStrategy(agent));
186 }
187
188 if (LOG.isDebugEnabled()) {
189 LOG.debug("Found JAXB created routes: " + getRoutes());
190 }
191 findRouteBuiders();
192 installRoutes();
193 }
194
195 private <T> T getBeanForType(Class<T> clazz) {
196 T bean = null;
197 String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true);
198 if (names.length == 1) {
199 bean = (T) getApplicationContext().getBean(names[0], clazz);
200 }
201 if (bean == null) {
202 ApplicationContext parentContext = getApplicationContext().getParent();
203 if (parentContext != null) {
204 names = parentContext.getBeanNamesForType(clazz, true, true);
205 if (names.length == 1) {
206 bean = (T) parentContext.getBean(names[0], clazz);
207 }
208 }
209 }
210 return bean;
211
212 }
213
214 public void destroy() throws Exception {
215 getContext().stop();
216 }
217
218 public void onApplicationEvent(ApplicationEvent event) {
219 if (LOG.isDebugEnabled()) {
220 LOG.debug("Publishing event: " + event);
221 }
222
223 if (event instanceof ContextRefreshedEvent) {
224 // now lets start the CamelContext so that all its possible
225 // dependencies are initailized
226 try {
227 LOG.debug("Starting the context now!");
228 getContext().start();
229 } catch (Exception e) {
230 throw new RuntimeCamelException(e);
231 }
232 }
233 /*
234 * if (context != null) { context.onApplicationEvent(event); }
235 */
236 }
237
238 // Properties
239 // -------------------------------------------------------------------------
240 public SpringCamelContext getContext() throws Exception {
241 if (context == null) {
242 context = createContext();
243 }
244 return context;
245 }
246
247 public void setContext(SpringCamelContext context) {
248 this.context = context;
249 }
250
251 public List<RouteType> getRoutes() {
252 return routes;
253 }
254
255 public void setRoutes(List<RouteType> routes) {
256 this.routes = routes;
257 }
258
259 public RouteBuilder getRouteBuilder() {
260 return routeBuilder;
261 }
262
263 /**
264 * Set a single {@link RouteBuilder} to be used to create the default routes
265 * on startup
266 */
267 public void setRouteBuilder(RouteBuilder routeBuilder) {
268 this.routeBuilder = routeBuilder;
269 }
270
271 /**
272 * Set a collection of {@link RouteBuilder} instances to be used to create
273 * the default routes on startup
274 */
275 public void setRouteBuilders(RouteBuilder[] builders) {
276 for (RouteBuilder builder : builders) {
277 additionalBuilders.add(builder);
278 }
279 }
280
281 public ApplicationContext getApplicationContext() {
282 if (applicationContext == null) {
283 throw new IllegalArgumentException("No applicationContext has been injected!");
284 }
285 return applicationContext;
286 }
287
288 public void setApplicationContext(ApplicationContext applicationContext) {
289 this.applicationContext = applicationContext;
290 }
291
292 public String[] getPackages() {
293 return packages;
294 }
295
296 /**
297 * Sets the package names to be recursively searched for Java classes which
298 * extend {@link RouteBuilder} to be auto-wired up to the
299 * {@link SpringCamelContext} as a route. Note that classes are excluded if
300 * they are specifically configured in the spring.xml
301 *
302 * @param packages the package names which are recursively searched
303 */
304 public void setPackages(String[] packages) {
305 this.packages = packages;
306 }
307
308 public void setBeanPostProcessor(BeanPostProcessor postProcessor) {
309 this.beanPostProcessor = postProcessor;
310 }
311
312 public BeanPostProcessor getBeanPostProcessor() {
313 return beanPostProcessor;
314 }
315
316 /**
317 * This method merely retrieves the value of the "useJmx" attribute and does
318 * not consider the "disabled" flag in jmxAgent element. The useJmx
319 * attribute will be removed in 2.0. Please the jmxAgent element instead.
320 *
321 * @deprecated Please the jmxAgent element instead. Will be removed in Camel 2.0.
322 */
323 public boolean isJmxEnabled() {
324 return useJmx.booleanValue();
325 }
326
327 public Boolean getUseJmx() {
328 return useJmx;
329 }
330
331 /**
332 * @deprecated Please the jmxAgent element instead. Will be removed in Camel 2.0.
333 */
334 public void setUseJmx(Boolean useJmx) {
335 this.useJmx = useJmx;
336 }
337
338 public void setCamelJMXAgent(CamelJMXAgentType agent) {
339 camelJMXAgent = agent;
340 }
341
342 public Boolean getTrace() {
343 return trace;
344 }
345
346 public void setTrace(Boolean trace) {
347 this.trace = trace;
348 }
349
350 public CamelJMXAgentType getCamelJMXAgent() {
351 return camelJMXAgent;
352 }
353
354 public List<RouteBuilderRef> getBuilderRefs() {
355 return builderRefs;
356 }
357
358 public void setBuilderRefs(List<RouteBuilderRef> builderRefs) {
359 this.builderRefs = builderRefs;
360 }
361
362 /**
363 * If enabled this will force all {@link RouteBuilder} classes configured in the Spring
364 * {@link ApplicationContext} to be registered automatically with this CamelContext.
365 */
366 public void setAutowireRouteBuilders(Boolean autowireRouteBuilders) {
367 this.autowireRouteBuilders = autowireRouteBuilders;
368 }
369
370 public String getErrorHandlerRef() {
371 return errorHandlerRef;
372 }
373
374 /**
375 * Sets the name of the error handler object used to default the error handling strategy
376 *
377 * @param errorHandlerRef the Spring bean ref of the error handler
378 */
379 public void setErrorHandlerRef(String errorHandlerRef) {
380 this.errorHandlerRef = errorHandlerRef;
381 }
382
383
384 // Implementation methods
385 // -------------------------------------------------------------------------
386
387 /**
388 * Create the context
389 */
390 protected SpringCamelContext createContext() {
391 SpringCamelContext ctx = new SpringCamelContext(getApplicationContext());
392 ctx.setName(getId());
393 if (trace != null) {
394 ctx.setTrace(trace);
395 }
396 if (errorHandlerRef != null) {
397 ErrorHandlerBuilder errorHandlerBuilder = (ErrorHandlerBuilder) getApplicationContext().getBean(errorHandlerRef, ErrorHandlerBuilder.class);
398 if (errorHandlerBuilder == null) {
399 throw new IllegalArgumentException("Could not find bean: " + errorHandlerRef);
400 }
401 ctx.setErrorHandlerBuilder(errorHandlerBuilder);
402 }
403 return ctx;
404 }
405
406 /**
407 * Strategy to install all available routes into the context
408 */
409 protected void installRoutes() throws Exception {
410 if (autowireRouteBuilders != null && autowireRouteBuilders.booleanValue()) {
411 Map builders = getApplicationContext().getBeansOfType(RouteBuilder.class, true, true);
412 if (builders != null) {
413 for (Object builder : builders.values()) {
414 getContext().addRoutes((RouteBuilder) builder);
415 }
416 }
417 }
418 for (RouteBuilder routeBuilder : additionalBuilders) {
419 getContext().addRoutes(routeBuilder);
420 }
421 if (routeBuilder != null) {
422 getContext().addRoutes(routeBuilder);
423 }
424
425 // lets add route builders added from references
426 if (builderRefs != null) {
427 for (RouteBuilderRef builderRef : builderRefs) {
428 RouteBuilder builder = builderRef.createRouteBuilder(getContext());
429 getContext().addRoutes(builder);
430 }
431 }
432 }
433
434 /**
435 * Strategy method to try find {@link RouteBuilder} instances on the
436 * classpath
437 */
438 protected void findRouteBuiders() throws Exception, InstantiationException {
439 if (packages != null && packages.length > 0) {
440 RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), packages, contextClassLoaderOnStart, getBeanPostProcessor());
441 finder.appendBuilders(additionalBuilders);
442 }
443 }
444 }