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: 62939 $ 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 public Boolean getShouldStartContext() { 468 return shouldStartContext; 469 } 470 471 public void setShouldStartContext(Boolean shouldStartContext) { 472 this.shouldStartContext = shouldStartContext; 473 } 474 475 // Implementation methods 476 // ------------------------------------------------------------------------- 477 478 /** 479 * Create the context 480 */ 481 protected SpringCamelContext createContext() { 482 SpringCamelContext ctx = new SpringCamelContext(getApplicationContext()); 483 ctx.setName(getId()); 484 if (trace != null) { 485 ctx.setTrace(trace); 486 } 487 if (delay != null) { 488 ctx.setDelay(delay); 489 } 490 if (errorHandlerRef != null) { 491 ErrorHandlerBuilder errorHandlerBuilder = (ErrorHandlerBuilder) getApplicationContext().getBean(errorHandlerRef, ErrorHandlerBuilder.class); 492 if (errorHandlerBuilder == null) { 493 throw new IllegalArgumentException("Could not find bean: " + errorHandlerRef); 494 } 495 ctx.setErrorHandlerBuilder(errorHandlerBuilder); 496 } 497 498 if (shouldStartContext != null) { 499 ctx.setShouldStartContext(shouldStartContext); 500 } 501 502 return ctx; 503 } 504 505 /** 506 * Strategy to install all available routes into the context 507 */ 508 protected void installRoutes() throws Exception { 509 if (autowireRouteBuilders != null && autowireRouteBuilders.booleanValue()) { 510 Map builders = getApplicationContext().getBeansOfType(RouteBuilder.class, true, true); 511 if (builders != null) { 512 for (Object builder : builders.values()) { 513 getContext().addRoutes((RouteBuilder) builder); 514 } 515 } 516 } 517 for (Routes routeBuilder : additionalBuilders) { 518 getContext().addRoutes(routeBuilder); 519 } 520 if (routeBuilder != null) { 521 getContext().addRoutes(routeBuilder); 522 } 523 524 // lets add route builders added from references 525 if (builderRefs != null) { 526 for (RouteBuilderRef builderRef : builderRefs) { 527 RouteBuilder builder = builderRef.createRouteBuilder(getContext()); 528 getContext().addRoutes(builder); 529 } 530 } 531 } 532 533 /** 534 * Strategy method to try find {@link RouteBuilder} instances on the 535 * classpath 536 */ 537 protected void findRouteBuiders() throws Exception, InstantiationException { 538 if (packages != null && packages.length > 0) { 539 RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), packages, contextClassLoaderOnStart, getBeanPostProcessor()); 540 finder.appendBuilders(additionalBuilders); 541 } 542 } 543 544 public void setDataFormats(DataFormatsType dataFormats) { 545 this.dataFormats = dataFormats; 546 } 547 548 public DataFormatsType getDataFormats() { 549 return dataFormats; 550 } 551 }