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