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 }