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.IdentifiedType; 038 import org.apache.camel.model.InterceptDefinition; 039 import org.apache.camel.model.OnExceptionDefinition; 040 import org.apache.camel.model.ProceedDefinition; 041 import org.apache.camel.model.ProcessorDefinition; 042 import org.apache.camel.model.RouteBuilderDefinition; 043 import org.apache.camel.model.RouteContainer; 044 import org.apache.camel.model.RouteDefinition; 045 import org.apache.camel.model.config.PropertiesDefinition; 046 import org.apache.camel.model.dataformat.DataFormatsDefinition; 047 import org.apache.camel.processor.interceptor.Debugger; 048 import org.apache.camel.processor.interceptor.Delayer; 049 import org.apache.camel.processor.interceptor.TraceFormatter; 050 import org.apache.camel.processor.interceptor.Tracer; 051 import org.apache.camel.spi.ClassResolver; 052 import org.apache.camel.spi.LifecycleStrategy; 053 import org.apache.camel.spi.PackageScanClassResolver; 054 import org.apache.camel.spi.Registry; 055 import org.apache.camel.util.ProcessorTypeHelper; 056 import org.apache.commons.logging.Log; 057 import org.apache.commons.logging.LogFactory; 058 import org.springframework.beans.factory.DisposableBean; 059 import org.springframework.beans.factory.FactoryBean; 060 import org.springframework.beans.factory.InitializingBean; 061 import org.springframework.beans.factory.config.BeanPostProcessor; 062 import org.springframework.context.ApplicationContext; 063 import org.springframework.context.ApplicationContextAware; 064 import org.springframework.context.ApplicationEvent; 065 import org.springframework.context.ApplicationListener; 066 import org.springframework.context.event.ContextRefreshedEvent; 067 068 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException; 069 070 /** 071 * A Spring {@link FactoryBean} to create and initialize a 072 * {@link SpringCamelContext} and install routes either explicitly configured in 073 * Spring XML or found by searching the classpath for Java classes which extend 074 * {@link RouteBuilder} using the nested {@link #setPackages(String[])}. 075 * 076 * @version $Revision: 751648 $ 077 */ 078 @XmlRootElement(name = "camelContext") 079 @XmlAccessorType(XmlAccessType.FIELD) 080 public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener { 081 private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class); 082 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 = "properties", required = false) 094 private PropertiesDefinition properties; 095 @XmlElement(name = "package", required = false) 096 private String[] packages = {}; 097 @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class, required = false) 098 private CamelJMXAgentDefinition camelJMXAgent; 099 @XmlElements({ 100 @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false), 101 @XmlElement(name = "template", type = CamelTemplateFactoryBean.class, required = false), 102 @XmlElement(name = "proxy", type = CamelProxyFactoryDefinition.class, required = false), 103 @XmlElement(name = "export", type = CamelServiceExporterDefinition.class, required = false)}) 104 private List beans; 105 @XmlElement(name = "routeBuilder", required = false) 106 private List<RouteBuilderDefinition> builderRefs = new ArrayList<RouteBuilderDefinition>(); 107 @XmlElement(name = "endpoint", required = false) 108 private List<EndpointFactoryBean> endpoints; 109 @XmlElement(name = "dataFormats", required = false) 110 private DataFormatsDefinition dataFormats; 111 @XmlElement(name = "onException", required = false) 112 private List<OnExceptionDefinition> exceptionClauses = new ArrayList<OnExceptionDefinition>(); 113 @XmlElement(name = "intercept", required = false) 114 private List<InterceptDefinition> intercepts = new ArrayList<InterceptDefinition>(); 115 @XmlElement(name = "route", required = false) 116 private List<RouteDefinition> routes = new ArrayList<RouteDefinition>(); 117 @XmlTransient 118 private SpringCamelContext context; 119 @XmlTransient 120 private RouteBuilder routeBuilder; 121 @XmlTransient 122 private List<Routes> additionalBuilders = new ArrayList<Routes>(); 123 @XmlTransient 124 private ApplicationContext applicationContext; 125 @XmlTransient 126 private ClassLoader contextClassLoaderOnStart; 127 @XmlTransient 128 private BeanPostProcessor beanPostProcessor; 129 130 public CamelContextFactoryBean() { 131 // Lets keep track of the class loader for when we actually do start things up 132 contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader(); 133 } 134 135 public Object getObject() throws Exception { 136 return getContext(); 137 } 138 139 public Class getObjectType() { 140 return SpringCamelContext.class; 141 } 142 143 public boolean isSingleton() { 144 return true; 145 } 146 147 public ClassLoader getContextClassLoaderOnStart() { 148 return contextClassLoaderOnStart; 149 } 150 151 public List<Routes> getAdditionalBuilders() { 152 return additionalBuilders; 153 } 154 155 public void afterPropertiesSet() throws Exception { 156 // TODO there should be a neater way to do this! 157 if (properties != null) { 158 getContext().setProperties(properties.asMap()); 159 } 160 // set the resolvers first 161 PackageScanClassResolver packageResolver = getBeanForType(PackageScanClassResolver.class); 162 if (packageResolver != null) { 163 getContext().setPackageScanClassResolver(packageResolver); 164 } 165 ClassResolver classResolver = getBeanForType(ClassResolver.class); 166 if (classResolver != null) { 167 getContext().setClassResolver(classResolver); 168 } 169 170 Debugger debugger = getBeanForType(Debugger.class); 171 if (debugger != null) { 172 getContext().addInterceptStrategy(debugger); 173 } 174 175 Tracer tracer = getBeanForType(Tracer.class); 176 if (tracer != null) { 177 // use formatter if there is a TraceFormatter bean defined 178 TraceFormatter formatter = getBeanForType(TraceFormatter.class); 179 if (formatter != null) { 180 tracer.setFormatter(formatter); 181 } 182 getContext().addInterceptStrategy(tracer); 183 } 184 185 Delayer delayer = getBeanForType(Delayer.class); 186 if (delayer != null) { 187 getContext().addInterceptStrategy(delayer); 188 } 189 190 // set the lifecycle strategy if defined 191 LifecycleStrategy lifecycleStrategy = getBeanForType(LifecycleStrategy.class); 192 if (lifecycleStrategy != null) { 193 getContext().setLifecycleStrategy(lifecycleStrategy); 194 } 195 196 // set the strategy if defined 197 Registry registry = getBeanForType(Registry.class); 198 if (registry != null) { 199 getContext().setRegistry(registry); 200 } 201 202 // Set the application context and camelContext for the beanPostProcessor 203 if (beanPostProcessor != null) { 204 if (beanPostProcessor instanceof ApplicationContextAware) { 205 ((ApplicationContextAware)beanPostProcessor).setApplicationContext(applicationContext); 206 } 207 if (beanPostProcessor instanceof CamelBeanPostProcessor) { 208 ((CamelBeanPostProcessor)beanPostProcessor).setCamelContext(getContext()); 209 } 210 } 211 212 // setup the intercepts 213 for (RouteDefinition route : routes) { 214 215 if (exceptionClauses != null) { 216 route.getOutputs().addAll(exceptionClauses); 217 } 218 219 for (InterceptDefinition intercept : intercepts) { 220 List<ProcessorDefinition<?>> outputs = new ArrayList<ProcessorDefinition<?>>(); 221 List<ProcessorDefinition<?>> exceptionHandlers = new ArrayList<ProcessorDefinition<?>>(); 222 for (ProcessorDefinition output : route.getOutputs()) { 223 if (output instanceof OnExceptionDefinition) { 224 exceptionHandlers.add(output); 225 } else { 226 outputs.add(output); 227 } 228 } 229 230 // clearing the outputs 231 route.clearOutput(); 232 233 // add exception handlers as top children 234 route.getOutputs().addAll(exceptionHandlers); 235 236 // add the interceptor but we must do some pre configuration beforehand 237 intercept.afterPropertiesSet(); 238 InterceptDefinition proxy = intercept.createProxy(); 239 route.addOutput(proxy); 240 route.pushBlock(proxy.getProceed()); 241 242 // if there is a proceed in the interceptor proxy then we should add 243 // the current outputs to out route so we will proceed and continue to route to them 244 ProceedDefinition proceed = ProcessorTypeHelper.findFirstTypeInOutputs(proxy.getOutputs(), ProceedDefinition.class); 245 if (proceed != null) { 246 proceed.getOutputs().addAll(outputs); 247 } 248 } 249 250 } 251 252 if (dataFormats != null) { 253 getContext().setDataFormats(dataFormats.asMap()); 254 } 255 256 // lets force any lazy creation 257 getContext().addRouteDefinitions(routes); 258 259 if (camelJMXAgent != null && camelJMXAgent.isDisabled()) { 260 LOG.debug("JMXAgent disabled"); 261 getContext().setLifecycleStrategy(new DefaultLifecycleStrategy()); 262 } else if (camelJMXAgent != null) { 263 LOG.debug("JMXAgent enabled"); 264 265 if (lifecycleStrategy != null) { 266 LOG.warn("lifecycleStrategy will be overriden by InstrumentationLifecycleStrategy"); 267 } 268 269 DefaultInstrumentationAgent agent = new DefaultInstrumentationAgent(); 270 agent.setConnectorPort(camelJMXAgent.getConnectorPort()); 271 agent.setCreateConnector(camelJMXAgent.isCreateConnector()); 272 agent.setMBeanObjectDomainName(camelJMXAgent.getMbeanObjectDomainName()); 273 agent.setMBeanServerDefaultDomain(camelJMXAgent.getMbeanServerDefaultDomain()); 274 agent.setRegistryPort(camelJMXAgent.getRegistryPort()); 275 agent.setServiceUrlPath(camelJMXAgent.getServiceUrlPath()); 276 agent.setUsePlatformMBeanServer(camelJMXAgent.isUsePlatformMBeanServer()); 277 278 getContext().setLifecycleStrategy(new InstrumentationLifecycleStrategy(agent)); 279 } 280 281 if (LOG.isDebugEnabled()) { 282 LOG.debug("Found JAXB created routes: " + getRoutes()); 283 } 284 findRouteBuilders(); 285 installRoutes(); 286 } 287 288 @SuppressWarnings("unchecked") 289 private <T> T getBeanForType(Class<T> clazz) { 290 T bean = null; 291 String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true); 292 if (names.length == 1) { 293 bean = (T) getApplicationContext().getBean(names[0], clazz); 294 } 295 if (bean == null) { 296 ApplicationContext parentContext = getApplicationContext().getParent(); 297 if (parentContext != null) { 298 names = parentContext.getBeanNamesForType(clazz, true, true); 299 if (names.length == 1) { 300 bean = (T) parentContext.getBean(names[0], clazz); 301 } 302 } 303 } 304 return bean; 305 306 } 307 308 public void destroy() throws Exception { 309 getContext().stop(); 310 } 311 312 public void onApplicationEvent(ApplicationEvent event) { 313 if (LOG.isDebugEnabled()) { 314 LOG.debug("Publishing spring-event: " + event); 315 } 316 317 if (event instanceof ContextRefreshedEvent) { 318 // now lets start the CamelContext so that all its possible 319 // dependencies are initialized 320 try { 321 LOG.debug("Starting the context now!"); 322 getContext().start(); 323 } catch (Exception e) { 324 throw wrapRuntimeCamelException(e); 325 } 326 } 327 } 328 329 // Properties 330 // ------------------------------------------------------------------------- 331 public SpringCamelContext getContext() throws Exception { 332 if (context == null) { 333 context = createContext(); 334 } 335 return context; 336 } 337 338 public void setContext(SpringCamelContext context) { 339 this.context = context; 340 } 341 342 public List<RouteDefinition> getRoutes() { 343 return routes; 344 } 345 346 public void setRoutes(List<RouteDefinition> routes) { 347 this.routes = routes; 348 } 349 350 public List<InterceptDefinition> getIntercepts() { 351 return intercepts; 352 } 353 354 public void setIntercepts(List<InterceptDefinition> intercepts) { 355 this.intercepts = intercepts; 356 } 357 358 public RouteBuilder getRouteBuilder() { 359 return routeBuilder; 360 } 361 362 /** 363 * Set a single {@link RouteBuilder} to be used to create the default routes 364 * on startup 365 */ 366 public void setRouteBuilder(RouteBuilder routeBuilder) { 367 this.routeBuilder = routeBuilder; 368 } 369 370 /** 371 * Set a collection of {@link RouteBuilder} instances to be used to create 372 * the default routes on startup 373 */ 374 public void setRouteBuilders(RouteBuilder[] builders) { 375 for (RouteBuilder builder : builders) { 376 additionalBuilders.add(builder); 377 } 378 } 379 380 public ApplicationContext getApplicationContext() { 381 if (applicationContext == null) { 382 throw new IllegalArgumentException("No applicationContext has been injected!"); 383 } 384 return applicationContext; 385 } 386 387 public void setApplicationContext(ApplicationContext applicationContext) { 388 this.applicationContext = applicationContext; 389 } 390 391 public PropertiesDefinition getProperties() { 392 return properties; 393 } 394 395 public void setProperties(PropertiesDefinition properties) { 396 this.properties = properties; 397 } 398 399 public String[] getPackages() { 400 return packages; 401 } 402 403 /** 404 * Sets the package names to be recursively searched for Java classes which 405 * extend {@link RouteBuilder} to be auto-wired up to the 406 * {@link SpringCamelContext} as a route. Note that classes are excluded if 407 * they are specifically configured in the spring.xml 408 * 409 * @param packages the package names which are recursively searched 410 */ 411 public void setPackages(String[] packages) { 412 this.packages = packages; 413 } 414 415 public void setBeanPostProcessor(BeanPostProcessor postProcessor) { 416 this.beanPostProcessor = postProcessor; 417 } 418 419 public BeanPostProcessor getBeanPostProcessor() { 420 return beanPostProcessor; 421 } 422 423 public void setCamelJMXAgent(CamelJMXAgentDefinition 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 CamelJMXAgentDefinition getCamelJMXAgent() { 444 return camelJMXAgent; 445 } 446 447 public List<RouteBuilderDefinition> getBuilderRefs() { 448 return builderRefs; 449 } 450 451 public void setBuilderRefs(List<RouteBuilderDefinition> 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) { 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 (RouteBuilderDefinition 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(), 549 getBeanPostProcessor(), getContext().getPackageScanClassResolver()); 550 finder.appendBuilders(getAdditionalBuilders()); 551 } 552 } 553 554 public void setDataFormats(DataFormatsDefinition dataFormats) { 555 this.dataFormats = dataFormats; 556 } 557 558 public DataFormatsDefinition getDataFormats() { 559 return dataFormats; 560 } 561 562 public void setExceptionClauses(List<OnExceptionDefinition> exceptionClauses) { 563 this.exceptionClauses = exceptionClauses; 564 } 565 566 public List<OnExceptionDefinition> getExceptionClauses() { 567 return exceptionClauses; 568 } 569 }