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