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