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