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