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