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 import javax.xml.bind.annotation.XmlAccessType;
023 import javax.xml.bind.annotation.XmlAccessorType;
024 import javax.xml.bind.annotation.XmlAttribute;
025 import javax.xml.bind.annotation.XmlElement;
026 import javax.xml.bind.annotation.XmlElements;
027 import javax.xml.bind.annotation.XmlRootElement;
028 import javax.xml.bind.annotation.XmlTransient;
029
030 import org.apache.camel.CamelContext;
031 import org.apache.camel.RoutesBuilder;
032 import org.apache.camel.ShutdownRoute;
033 import org.apache.camel.ShutdownRunningTask;
034 import org.apache.camel.builder.RouteBuilder;
035 import org.apache.camel.component.properties.PropertiesComponent;
036 import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
037 import org.apache.camel.core.xml.CamelJMXAgentDefinition;
038 import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition;
039 import org.apache.camel.core.xml.CamelProxyFactoryDefinition;
040 import org.apache.camel.core.xml.CamelServiceExporterDefinition;
041 import org.apache.camel.model.ContextScanDefinition;
042 import org.apache.camel.model.InterceptDefinition;
043 import org.apache.camel.model.InterceptFromDefinition;
044 import org.apache.camel.model.InterceptSendToEndpointDefinition;
045 import org.apache.camel.model.OnCompletionDefinition;
046 import org.apache.camel.model.OnExceptionDefinition;
047 import org.apache.camel.model.PackageScanDefinition;
048 import org.apache.camel.model.RouteBuilderDefinition;
049 import org.apache.camel.model.RouteContextRefDefinition;
050 import org.apache.camel.model.RouteDefinition;
051 import org.apache.camel.model.ThreadPoolProfileDefinition;
052 import org.apache.camel.model.config.PropertiesDefinition;
053 import org.apache.camel.model.dataformat.DataFormatsDefinition;
054 import org.apache.camel.spi.PackageScanFilter;
055 import org.apache.camel.spi.Registry;
056 import org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer;
057 import org.apache.camel.util.CamelContextHelper;
058 import org.slf4j.Logger;
059 import org.slf4j.LoggerFactory;
060 import org.springframework.beans.factory.DisposableBean;
061 import org.springframework.beans.factory.FactoryBean;
062 import org.springframework.beans.factory.InitializingBean;
063 import org.springframework.beans.factory.config.BeanPostProcessor;
064 import org.springframework.context.ApplicationContext;
065 import org.springframework.context.ApplicationContextAware;
066 import org.springframework.context.ApplicationEvent;
067 import org.springframework.context.ApplicationListener;
068 import org.springframework.context.event.ContextRefreshedEvent;
069
070 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
071
072 /**
073 * A Spring {@link FactoryBean} to create and initialize a
074 * {@link SpringCamelContext} and install routes either explicitly configured in
075 * Spring XML or found by searching the classpath for Java classes which extend
076 * {@link RouteBuilder} using the nested {@link #setPackages(String[])}.
077 *
078 * @version
079 */
080 @XmlRootElement(name = "camelContext")
081 @XmlAccessorType(XmlAccessType.FIELD)
082 @SuppressWarnings("unused")
083 public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<SpringCamelContext>
084 implements FactoryBean<SpringCamelContext>, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {
085 private static final Logger LOG = LoggerFactory.getLogger(CamelContextFactoryBean.class);
086
087 @XmlAttribute(name = "depends-on", required = false)
088 private String dependsOn;
089 @XmlAttribute(required = false)
090 private String trace;
091 @XmlAttribute(required = false)
092 private String streamCache;
093 @XmlAttribute(required = false)
094 private String delayer;
095 @XmlAttribute(required = false)
096 private String handleFault;
097 @XmlAttribute(required = false)
098 private String errorHandlerRef;
099 @XmlAttribute(required = false)
100 private String autoStartup;
101 @XmlAttribute(required = false)
102 private String shutdownEager;
103 @XmlAttribute(required = false)
104 private String useMDCLogging;
105 @XmlAttribute(required = false)
106 private String useBreadcrumb;
107 @XmlAttribute(required = false)
108 private String managementNamePattern;
109 @XmlAttribute(required = false)
110 private ShutdownRoute shutdownRoute;
111 @XmlAttribute(required = false)
112 private ShutdownRunningTask shutdownRunningTask;
113 @XmlAttribute(required = false)
114 @Deprecated
115 private Boolean lazyLoadTypeConverters;
116 @XmlElement(name = "properties", required = false)
117 private PropertiesDefinition properties;
118 @XmlElement(name = "propertyPlaceholder", type = CamelPropertyPlaceholderDefinition.class, required = false)
119 private CamelPropertyPlaceholderDefinition camelPropertyPlaceholder;
120 @XmlElement(name = "package", required = false)
121 private String[] packages = {};
122 @XmlElement(name = "packageScan", type = PackageScanDefinition.class, required = false)
123 private PackageScanDefinition packageScan;
124 @XmlElement(name = "contextScan", type = ContextScanDefinition.class, required = false)
125 private ContextScanDefinition contextScan;
126 @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class, required = false)
127 private CamelJMXAgentDefinition camelJMXAgent;
128 @XmlElements({
129 @XmlElement(name = "template", type = CamelProducerTemplateFactoryBean.class, required = false),
130 @XmlElement(name = "consumerTemplate", type = CamelConsumerTemplateFactoryBean.class, required = false),
131 @XmlElement(name = "proxy", type = CamelProxyFactoryDefinition.class, required = false),
132 @XmlElement(name = "export", type = CamelServiceExporterDefinition.class, required = false),
133 @XmlElement(name = "errorHandler", type = ErrorHandlerDefinition.class, required = false)})
134 private List<?> beans;
135 @XmlElement(name = "routeBuilder", required = false)
136 private List<RouteBuilderDefinition> builderRefs = new ArrayList<RouteBuilderDefinition>();
137 @XmlElement(name = "routeContextRef", required = false)
138 private List<RouteContextRefDefinition> routeRefs = new ArrayList<RouteContextRefDefinition>();
139 @XmlElement(name = "threadPoolProfile", required = false)
140 private List<ThreadPoolProfileDefinition> threadPoolProfiles;
141 @XmlElement(name = "threadPool", required = false)
142 private List<CamelThreadPoolFactoryBean> threadPools;
143 @XmlElement(name = "endpoint", required = false)
144 private List<CamelEndpointFactoryBean> endpoints;
145 @XmlElement(name = "dataFormats", required = false)
146 private DataFormatsDefinition dataFormats;
147 @XmlElement(name = "redeliveryPolicyProfile", required = false)
148 private List<CamelRedeliveryPolicyFactoryBean> redeliveryPolicies;
149 @XmlElement(name = "onException", required = false)
150 private List<OnExceptionDefinition> onExceptions = new ArrayList<OnExceptionDefinition>();
151 @XmlElement(name = "onCompletion", required = false)
152 private List<OnCompletionDefinition> onCompletions = new ArrayList<OnCompletionDefinition>();
153 @XmlElement(name = "intercept", required = false)
154 private List<InterceptDefinition> intercepts = new ArrayList<InterceptDefinition>();
155 @XmlElement(name = "interceptFrom", required = false)
156 private List<InterceptFromDefinition> interceptFroms = new ArrayList<InterceptFromDefinition>();
157 @XmlElement(name = "interceptSendToEndpoint", required = false)
158 private List<InterceptSendToEndpointDefinition> interceptSendToEndpoints = new ArrayList<InterceptSendToEndpointDefinition>();
159 @XmlElement(name = "route", required = false)
160 private List<RouteDefinition> routes = new ArrayList<RouteDefinition>();
161 @XmlTransient
162 private SpringCamelContext context;
163 @XmlTransient
164 private ClassLoader contextClassLoaderOnStart;
165 @XmlTransient
166 private ApplicationContext applicationContext;
167 @XmlTransient
168 private BeanPostProcessor beanPostProcessor;
169 @XmlTransient
170 private boolean implicitId;
171
172
173 @Override
174 public Class<SpringCamelContext> getObjectType() {
175 return SpringCamelContext.class;
176 }
177
178 protected <S> S getBeanForType(Class<S> clazz) {
179 S bean = null;
180 String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true);
181 if (names.length == 1) {
182 bean = getApplicationContext().getBean(names[0], clazz);
183 }
184 if (bean == null) {
185 ApplicationContext parentContext = getApplicationContext().getParent();
186 if (parentContext != null) {
187 names = parentContext.getBeanNamesForType(clazz, true, true);
188 if (names.length == 1) {
189 bean = parentContext.getBean(names[0], clazz);
190 }
191 }
192 }
193 return bean;
194 }
195
196 @Override
197 protected void findRouteBuildersByPackageScan(String[] packages, PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception {
198 // add filter to class resolver which then will filter
199 getContext().getPackageScanClassResolver().addFilter(filter);
200
201 PackageScanRouteBuilderFinder finder = new PackageScanRouteBuilderFinder(getContext(), packages, getContextClassLoaderOnStart(),
202 getBeanPostProcessor(), getContext().getPackageScanClassResolver());
203 finder.appendBuilders(builders);
204
205 // and remove the filter
206 getContext().getPackageScanClassResolver().removeFilter(filter);
207 }
208
209 @Override
210 protected void findRouteBuildersByContextScan(PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception {
211 ContextScanRouteBuilderFinder finder = new ContextScanRouteBuilderFinder(getContext(), filter);
212 finder.appendBuilders(builders);
213 }
214
215 protected void initBeanPostProcessor(SpringCamelContext context) {
216 if (beanPostProcessor != null) {
217 if (beanPostProcessor instanceof ApplicationContextAware) {
218 ((ApplicationContextAware) beanPostProcessor).setApplicationContext(applicationContext);
219 }
220 if (beanPostProcessor instanceof CamelBeanPostProcessor) {
221 ((CamelBeanPostProcessor) beanPostProcessor).setCamelContext(getContext());
222 }
223 }
224 }
225
226 protected void postProcessBeforeInit(RouteBuilder builder) {
227 if (beanPostProcessor != null) {
228 // Inject the annotated resource
229 beanPostProcessor.postProcessBeforeInitialization(builder, builder.toString());
230 }
231 }
232
233 @Override
234 public void afterPropertiesSet() throws Exception {
235 super.afterPropertiesSet();
236
237 Boolean shutdownEager = CamelContextHelper.parseBoolean(getContext(), getShutdownEager());
238 if (shutdownEager != null) {
239 LOG.debug("Using shutdownEager: " + shutdownEager);
240 getContext().setShutdownEager(shutdownEager);
241 }
242 }
243
244 protected void initCustomRegistry(SpringCamelContext context) {
245 Registry registry = getBeanForType(Registry.class);
246 if (registry != null) {
247 LOG.info("Using custom Registry: " + registry);
248 context.setRegistry(registry);
249 }
250 }
251
252 @Override
253 protected void initPropertyPlaceholder() throws Exception {
254 super.initPropertyPlaceholder();
255
256 Map<String, BridgePropertyPlaceholderConfigurer> beans = applicationContext.getBeansOfType(BridgePropertyPlaceholderConfigurer.class);
257 if (beans.size() == 1) {
258 // setup properties component that uses this beans
259 BridgePropertyPlaceholderConfigurer configurer = beans.values().iterator().next();
260 String id = beans.keySet().iterator().next();
261 LOG.info("Bridging Camel and Spring property placeholder configurer with id: " + id);
262
263 // get properties component
264 PropertiesComponent pc = getContext().getComponent("properties", PropertiesComponent.class);
265 // replace existing resolver with us
266 configurer.setResolver(pc.getPropertiesResolver());
267 configurer.setParser(pc.getPropertiesParser());
268 String ref = "ref:" + id;
269 // use the bridge to handle the resolve and parsing
270 pc.setPropertiesResolver(configurer);
271 pc.setPropertiesParser(configurer);
272 // and update locations to have our as ref first
273 String[] locations = pc.getLocations();
274 String[] updatedLocations;
275 if (locations != null && locations.length > 0) {
276 updatedLocations = new String[locations.length + 1];
277 updatedLocations[0] = ref;
278 System.arraycopy(locations, 0, updatedLocations, 1, locations.length);
279 } else {
280 updatedLocations = new String[]{ref};
281 }
282 pc.setLocations(updatedLocations);
283 } else if (beans.size() > 1) {
284 LOG.warn("Cannot bridge Camel and Spring property placeholders, as exact only 1 bean of type BridgePropertyPlaceholderConfigurer"
285 + " must be defined, was {} beans defined.", beans.size());
286 }
287 }
288
289 public void onApplicationEvent(ApplicationEvent event) {
290 // From Spring 3.0.1, The BeanFactory applicationEventListener
291 // and Bean's applicationEventListener will be called,
292 // So we just delegate the onApplicationEvent call here.
293
294 SpringCamelContext context = getContext(false);
295 if (context != null) {
296 // let the spring camel context handle the events
297 context.onApplicationEvent(event);
298 } else {
299 LOG.debug("Publishing spring-event: {}", event);
300
301 if (event instanceof ContextRefreshedEvent) {
302 // now lets start the CamelContext so that all its possible
303 // dependencies are initialized
304 try {
305 LOG.trace("Starting the context now");
306 getContext().start();
307 } catch (Exception e) {
308 throw wrapRuntimeCamelException(e);
309 }
310 }
311 }
312 }
313
314 // Properties
315 // -------------------------------------------------------------------------
316
317 public ApplicationContext getApplicationContext() {
318 if (applicationContext == null) {
319 throw new IllegalArgumentException("No applicationContext has been injected!");
320 }
321 return applicationContext;
322 }
323
324 public void setApplicationContext(ApplicationContext applicationContext) {
325 this.applicationContext = applicationContext;
326 }
327
328 public void setBeanPostProcessor(BeanPostProcessor postProcessor) {
329 this.beanPostProcessor = postProcessor;
330 }
331
332 public BeanPostProcessor getBeanPostProcessor() {
333 return beanPostProcessor;
334 }
335
336 // Implementation methods
337 // -------------------------------------------------------------------------
338
339 /**
340 * Create the context
341 */
342 protected SpringCamelContext createContext() {
343 SpringCamelContext ctx = newCamelContext();
344 ctx.setName(getId());
345 return ctx;
346 }
347
348 protected SpringCamelContext newCamelContext() {
349 return new SpringCamelContext(getApplicationContext());
350 }
351
352 public SpringCamelContext getContext(boolean create) {
353 if (context == null && create) {
354 context = createContext();
355 }
356 return context;
357 }
358
359 public void setContext(SpringCamelContext context) {
360 this.context = context;
361 }
362
363 public List<RouteDefinition> getRoutes() {
364 return routes;
365 }
366
367 public void setRoutes(List<RouteDefinition> routes) {
368 this.routes = routes;
369 }
370
371 public List<CamelEndpointFactoryBean> getEndpoints() {
372 return endpoints;
373 }
374
375 public List<CamelRedeliveryPolicyFactoryBean> getRedeliveryPolicies() {
376 return redeliveryPolicies;
377 }
378
379 public List<InterceptDefinition> getIntercepts() {
380 return intercepts;
381 }
382
383 public void setIntercepts(List<InterceptDefinition> intercepts) {
384 this.intercepts = intercepts;
385 }
386
387 public List<InterceptFromDefinition> getInterceptFroms() {
388 return interceptFroms;
389 }
390
391 public void setInterceptFroms(List<InterceptFromDefinition> interceptFroms) {
392 this.interceptFroms = interceptFroms;
393 }
394
395 public List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints() {
396 return interceptSendToEndpoints;
397 }
398
399 public void setInterceptSendToEndpoints(List<InterceptSendToEndpointDefinition> interceptSendToEndpoints) {
400 this.interceptSendToEndpoints = interceptSendToEndpoints;
401 }
402
403 public PropertiesDefinition getProperties() {
404 return properties;
405 }
406
407 public void setProperties(PropertiesDefinition properties) {
408 this.properties = properties;
409 }
410
411 public String[] getPackages() {
412 return packages;
413 }
414
415 /**
416 * Sets the package names to be recursively searched for Java classes which
417 * extend {@link org.apache.camel.builder.RouteBuilder} to be auto-wired up to the
418 * {@link CamelContext} as a route. Note that classes are excluded if
419 * they are specifically configured in the spring.xml
420 * <p/>
421 * A more advanced configuration can be done using {@link #setPackageScan(org.apache.camel.model.PackageScanDefinition)}
422 *
423 * @param packages the package names which are recursively searched
424 * @see #setPackageScan(org.apache.camel.model.PackageScanDefinition)
425 */
426 public void setPackages(String[] packages) {
427 this.packages = packages;
428 }
429
430 public PackageScanDefinition getPackageScan() {
431 return packageScan;
432 }
433
434 /**
435 * Sets the package scanning information. Package scanning allows for the
436 * automatic discovery of certain camel classes at runtime for inclusion
437 * e.g. {@link org.apache.camel.builder.RouteBuilder} implementations
438 *
439 * @param packageScan the package scan
440 */
441 public void setPackageScan(PackageScanDefinition packageScan) {
442 this.packageScan = packageScan;
443 }
444
445 public ContextScanDefinition getContextScan() {
446 return contextScan;
447 }
448
449 /**
450 * Sets the context scanning (eg Spring's ApplicationContext) information.
451 * Context scanning allows for the automatic discovery of Camel routes runtime for inclusion
452 * e.g. {@link org.apache.camel.builder.RouteBuilder} implementations
453 *
454 * @param contextScan the context scan
455 */
456 public void setContextScan(ContextScanDefinition contextScan) {
457 this.contextScan = contextScan;
458 }
459
460 public CamelPropertyPlaceholderDefinition getCamelPropertyPlaceholder() {
461 return camelPropertyPlaceholder;
462 }
463
464 public void setCamelPropertyPlaceholder(CamelPropertyPlaceholderDefinition camelPropertyPlaceholder) {
465 this.camelPropertyPlaceholder = camelPropertyPlaceholder;
466 }
467
468 public void setCamelJMXAgent(CamelJMXAgentDefinition agent) {
469 camelJMXAgent = agent;
470 }
471
472 public String getTrace() {
473 return trace;
474 }
475
476 public void setTrace(String trace) {
477 this.trace = trace;
478 }
479
480 public String getStreamCache() {
481 return streamCache;
482 }
483
484 public void setStreamCache(String streamCache) {
485 this.streamCache = streamCache;
486 }
487
488 public String getDelayer() {
489 return delayer;
490 }
491
492 public void setDelayer(String delayer) {
493 this.delayer = delayer;
494 }
495
496 public String getHandleFault() {
497 return handleFault;
498 }
499
500 public void setHandleFault(String handleFault) {
501 this.handleFault = handleFault;
502 }
503
504 public String getAutoStartup() {
505 return autoStartup;
506 }
507
508 public void setAutoStartup(String autoStartup) {
509 this.autoStartup = autoStartup;
510 }
511
512 public String getShutdownEager() {
513 return shutdownEager;
514 }
515
516 public void setShutdownEager(String shutdownEager) {
517 this.shutdownEager = shutdownEager;
518 }
519
520 public String getUseMDCLogging() {
521 return useMDCLogging;
522 }
523
524 public void setUseMDCLogging(String useMDCLogging) {
525 this.useMDCLogging = useMDCLogging;
526 }
527
528 public String getUseBreadcrumb() {
529 return useBreadcrumb;
530 }
531
532 public void setUseBreadcrumb(String useBreadcrumb) {
533 this.useBreadcrumb = useBreadcrumb;
534 }
535
536 public String getManagementNamePattern() {
537 return managementNamePattern;
538 }
539
540 public void setManagementNamePattern(String managementNamePattern) {
541 this.managementNamePattern = managementNamePattern;
542 }
543
544 @Deprecated
545 public Boolean getLazyLoadTypeConverters() {
546 return lazyLoadTypeConverters;
547 }
548
549 @Deprecated
550 public void setLazyLoadTypeConverters(Boolean lazyLoadTypeConverters) {
551 this.lazyLoadTypeConverters = lazyLoadTypeConverters;
552 }
553
554 public CamelJMXAgentDefinition getCamelJMXAgent() {
555 return camelJMXAgent;
556 }
557
558 public List<RouteBuilderDefinition> getBuilderRefs() {
559 return builderRefs;
560 }
561
562 public void setBuilderRefs(List<RouteBuilderDefinition> builderRefs) {
563 this.builderRefs = builderRefs;
564 }
565
566 public List<RouteContextRefDefinition> getRouteRefs() {
567 return routeRefs;
568 }
569
570 public void setRouteRefs(List<RouteContextRefDefinition> routeRefs) {
571 this.routeRefs = routeRefs;
572 }
573
574 public String getErrorHandlerRef() {
575 return errorHandlerRef;
576 }
577
578 /**
579 * Sets the name of the error handler object used to default the error handling strategy
580 *
581 * @param errorHandlerRef the Spring bean ref of the error handler
582 */
583 public void setErrorHandlerRef(String errorHandlerRef) {
584 this.errorHandlerRef = errorHandlerRef;
585 }
586
587 public void setDataFormats(DataFormatsDefinition dataFormats) {
588 this.dataFormats = dataFormats;
589 }
590
591 public DataFormatsDefinition getDataFormats() {
592 return dataFormats;
593 }
594
595 public void setOnExceptions(List<OnExceptionDefinition> onExceptions) {
596 this.onExceptions = onExceptions;
597 }
598
599 public List<OnExceptionDefinition> getOnExceptions() {
600 return onExceptions;
601 }
602
603 public List<OnCompletionDefinition> getOnCompletions() {
604 return onCompletions;
605 }
606
607 public void setOnCompletions(List<OnCompletionDefinition> onCompletions) {
608 this.onCompletions = onCompletions;
609 }
610
611 public ShutdownRoute getShutdownRoute() {
612 return shutdownRoute;
613 }
614
615 public void setShutdownRoute(ShutdownRoute shutdownRoute) {
616 this.shutdownRoute = shutdownRoute;
617 }
618
619 public ShutdownRunningTask getShutdownRunningTask() {
620 return shutdownRunningTask;
621 }
622
623 public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
624 this.shutdownRunningTask = shutdownRunningTask;
625 }
626
627 public List<ThreadPoolProfileDefinition> getThreadPoolProfiles() {
628 return threadPoolProfiles;
629 }
630
631 public void setThreadPoolProfiles(List<ThreadPoolProfileDefinition> threadPoolProfiles) {
632 this.threadPoolProfiles = threadPoolProfiles;
633 }
634
635 public String getDependsOn() {
636 return dependsOn;
637 }
638
639 public void setDependsOn(String dependsOn) {
640 this.dependsOn = dependsOn;
641 }
642
643 public boolean isImplicitId() {
644 return implicitId;
645 }
646
647 public void setImplicitId(boolean flag) {
648 implicitId = flag;
649 }
650
651 }