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.HashSet; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.Set; 024 025 import javax.xml.bind.annotation.XmlAccessType; 026 import javax.xml.bind.annotation.XmlAccessorType; 027 import javax.xml.bind.annotation.XmlAttribute; 028 import javax.xml.bind.annotation.XmlElement; 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.CamelContext; 034 import org.apache.camel.CamelException; 035 import org.apache.camel.RoutesBuilder; 036 import org.apache.camel.ShutdownRoute; 037 import org.apache.camel.ShutdownRunningTask; 038 import org.apache.camel.builder.ErrorHandlerBuilderRef; 039 import org.apache.camel.builder.RouteBuilder; 040 import org.apache.camel.component.properties.PropertiesComponent; 041 import org.apache.camel.component.properties.PropertiesResolver; 042 import org.apache.camel.management.DefaultManagementAgent; 043 import org.apache.camel.management.DefaultManagementLifecycleStrategy; 044 import org.apache.camel.management.DefaultManagementStrategy; 045 import org.apache.camel.management.ManagedManagementStrategy; 046 import org.apache.camel.model.FromDefinition; 047 import org.apache.camel.model.IdentifiedType; 048 import org.apache.camel.model.InterceptDefinition; 049 import org.apache.camel.model.InterceptFromDefinition; 050 import org.apache.camel.model.InterceptSendToEndpointDefinition; 051 import org.apache.camel.model.OnCompletionDefinition; 052 import org.apache.camel.model.OnExceptionDefinition; 053 import org.apache.camel.model.PackageScanDefinition; 054 import org.apache.camel.model.ProcessorDefinition; 055 import org.apache.camel.model.RouteBuilderDefinition; 056 import org.apache.camel.model.RouteContainer; 057 import org.apache.camel.model.RouteContextRefDefinition; 058 import org.apache.camel.model.RouteDefinition; 059 import org.apache.camel.model.ThreadPoolProfileDefinition; 060 import org.apache.camel.model.ToDefinition; 061 import org.apache.camel.model.TransactedDefinition; 062 import org.apache.camel.model.config.PropertiesDefinition; 063 import org.apache.camel.model.dataformat.DataFormatsDefinition; 064 import org.apache.camel.processor.interceptor.Delayer; 065 import org.apache.camel.processor.interceptor.HandleFault; 066 import org.apache.camel.processor.interceptor.TraceFormatter; 067 import org.apache.camel.processor.interceptor.Tracer; 068 import org.apache.camel.spi.ClassResolver; 069 import org.apache.camel.spi.EventFactory; 070 import org.apache.camel.spi.EventNotifier; 071 import org.apache.camel.spi.ExecutorServiceStrategy; 072 import org.apache.camel.spi.FactoryFinderResolver; 073 import org.apache.camel.spi.InflightRepository; 074 import org.apache.camel.spi.InterceptStrategy; 075 import org.apache.camel.spi.LifecycleStrategy; 076 import org.apache.camel.spi.ManagementStrategy; 077 import org.apache.camel.spi.PackageScanClassResolver; 078 import org.apache.camel.spi.Registry; 079 import org.apache.camel.spi.RouteContext; 080 import org.apache.camel.spi.ShutdownStrategy; 081 import org.apache.camel.spi.ThreadPoolProfile; 082 import org.apache.camel.util.CamelContextHelper; 083 import org.apache.camel.util.EndpointHelper; 084 import org.apache.camel.util.ObjectHelper; 085 import org.apache.commons.logging.Log; 086 import org.apache.commons.logging.LogFactory; 087 import org.springframework.beans.factory.DisposableBean; 088 import org.springframework.beans.factory.FactoryBean; 089 import org.springframework.beans.factory.InitializingBean; 090 import org.springframework.beans.factory.config.BeanPostProcessor; 091 import org.springframework.context.ApplicationContext; 092 import org.springframework.context.ApplicationContextAware; 093 import org.springframework.context.ApplicationEvent; 094 import org.springframework.context.ApplicationListener; 095 import org.springframework.context.event.ContextRefreshedEvent; 096 097 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException; 098 099 /** 100 * A Spring {@link FactoryBean} to create and initialize a 101 * {@link SpringCamelContext} and install routes either explicitly configured in 102 * Spring XML or found by searching the classpath for Java classes which extend 103 * {@link RouteBuilder} using the nested {@link #setPackages(String[])}. 104 * 105 * @version $Revision: 945532 $ 106 */ 107 @XmlRootElement(name = "camelContext") 108 @XmlAccessorType(XmlAccessType.FIELD) 109 @SuppressWarnings("unused") 110 public class CamelContextFactoryBean extends IdentifiedType implements RouteContainer, FactoryBean, InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener { 111 private static final Log LOG = LogFactory.getLog(CamelContextFactoryBean.class); 112 113 @XmlAttribute(name = "depends-on", required = false) 114 private String dependsOn; 115 @XmlAttribute(required = false) 116 private String trace; 117 @XmlAttribute(required = false) 118 private String streamCache = "false"; 119 @XmlAttribute(required = false) 120 private String delayer; 121 @XmlAttribute(required = false) 122 private String handleFault; 123 @XmlAttribute(required = false) 124 private String errorHandlerRef; 125 @XmlAttribute(required = false) 126 private String autoStartup = "true"; 127 @XmlAttribute(required = false) 128 private ShutdownRoute shutdownRoute; 129 @XmlAttribute(required = false) 130 private ShutdownRunningTask shutdownRunningTask; 131 @XmlElement(name = "properties", required = false) 132 private PropertiesDefinition properties; 133 @XmlElement(name = "propertyPlaceholder", type = CamelPropertyPlaceholderDefinition.class, required = false) 134 private CamelPropertyPlaceholderDefinition camelPropertyPlaceholder; 135 @XmlElement(name = "package", required = false) 136 private String[] packages = {}; 137 @XmlElement(name = "packageScan", type = PackageScanDefinition.class, required = false) 138 private PackageScanDefinition packageScan; 139 @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class, required = false) 140 private CamelJMXAgentDefinition camelJMXAgent; 141 @XmlElements({ 142 @XmlElement(name = "beanPostProcessor", type = CamelBeanPostProcessor.class, required = false), 143 @XmlElement(name = "template", type = CamelProducerTemplateFactoryBean.class, required = false), 144 @XmlElement(name = "consumerTemplate", type = CamelConsumerTemplateFactoryBean.class, required = false), 145 @XmlElement(name = "proxy", type = CamelProxyFactoryDefinition.class, required = false), 146 @XmlElement(name = "export", type = CamelServiceExporterDefinition.class, required = false), 147 @XmlElement(name = "errorHandler", type = ErrorHandlerDefinition.class, required = false)}) 148 private List beans; 149 @XmlElement(name = "routeBuilder", required = false) 150 private List<RouteBuilderDefinition> builderRefs = new ArrayList<RouteBuilderDefinition>(); 151 @XmlElement(name = "routeContextRef", required = false) 152 private List<RouteContextRefDefinition> routeRefs = new ArrayList<RouteContextRefDefinition>(); 153 @XmlElement(name = "threadPoolProfile", required = false) 154 private List<ThreadPoolProfileDefinition> threadPoolProfiles; 155 @XmlElement(name = "threadPool", required = false) 156 private List<CamelThreadPoolFactoryBean> threadPools; 157 @XmlElement(name = "endpoint", required = false) 158 private List<CamelEndpointFactoryBean> endpoints; 159 @XmlElement(name = "dataFormats", required = false) 160 private DataFormatsDefinition dataFormats; 161 @XmlElement(name = "onException", required = false) 162 private List<OnExceptionDefinition> onExceptions = new ArrayList<OnExceptionDefinition>(); 163 @XmlElement(name = "onCompletion", required = false) 164 private List<OnCompletionDefinition> onCompletions = new ArrayList<OnCompletionDefinition>(); 165 @XmlElement(name = "intercept", required = false) 166 private List<InterceptDefinition> intercepts = new ArrayList<InterceptDefinition>(); 167 @XmlElement(name = "interceptFrom", required = false) 168 private List<InterceptFromDefinition> interceptFroms = new ArrayList<InterceptFromDefinition>(); 169 @XmlElement(name = "interceptSendToEndpoint", required = false) 170 private List<InterceptSendToEndpointDefinition> interceptSendToEndpoints = new ArrayList<InterceptSendToEndpointDefinition>(); 171 @XmlElement(name = "route", required = false) 172 private List<RouteDefinition> routes = new ArrayList<RouteDefinition>(); 173 @XmlTransient 174 private SpringCamelContext context; 175 @XmlTransient 176 private List<RoutesBuilder> builders = new ArrayList<RoutesBuilder>(); 177 @XmlTransient 178 private ApplicationContext applicationContext; 179 @XmlTransient 180 private ClassLoader contextClassLoaderOnStart; 181 @XmlTransient 182 private BeanPostProcessor beanPostProcessor; 183 184 public CamelContextFactoryBean() { 185 // Lets keep track of the class loader for when we actually do start things up 186 contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader(); 187 } 188 189 public Object getObject() throws Exception { 190 return getContext(); 191 } 192 193 public Class getObjectType() { 194 return SpringCamelContext.class; 195 } 196 197 public boolean isSingleton() { 198 return true; 199 } 200 201 public ClassLoader getContextClassLoaderOnStart() { 202 return contextClassLoaderOnStart; 203 } 204 205 public void afterPropertiesSet() throws Exception { 206 if (ObjectHelper.isEmpty(getId())) { 207 throw new IllegalArgumentException("Id must be set"); 208 } 209 210 if (properties != null) { 211 getContext().setProperties(properties.asMap()); 212 } 213 214 // set the resolvers first 215 PackageScanClassResolver packageResolver = getBeanForType(PackageScanClassResolver.class); 216 if (packageResolver != null) { 217 LOG.info("Using custom PackageScanClassResolver: " + packageResolver); 218 getContext().setPackageScanClassResolver(packageResolver); 219 } 220 ClassResolver classResolver = getBeanForType(ClassResolver.class); 221 if (classResolver != null) { 222 LOG.info("Using custom ClassResolver: " + classResolver); 223 getContext().setClassResolver(classResolver); 224 } 225 FactoryFinderResolver factoryFinderResolver = getBeanForType(FactoryFinderResolver.class); 226 if (factoryFinderResolver != null) { 227 LOG.info("Using custom FactoryFinderResolver: " + factoryFinderResolver); 228 getContext().setFactoryFinderResolver(factoryFinderResolver); 229 } 230 ExecutorServiceStrategy executorServiceStrategy = getBeanForType(ExecutorServiceStrategy.class); 231 if (executorServiceStrategy != null) { 232 LOG.info("Using custom ExecutorServiceStrategy: " + executorServiceStrategy); 233 getContext().setExecutorServiceStrategy(executorServiceStrategy); 234 } 235 236 // set the custom registry if defined 237 Registry registry = getBeanForType(Registry.class); 238 if (registry != null) { 239 LOG.info("Using custom Registry: " + registry); 240 getContext().setRegistry(registry); 241 } 242 243 // setup property placeholder so we got it as early as possible 244 initPropertyPlaceholder(); 245 246 // setup JMX agent at first 247 initJMXAgent(); 248 249 Tracer tracer = getBeanForType(Tracer.class); 250 if (tracer != null) { 251 // use formatter if there is a TraceFormatter bean defined 252 TraceFormatter formatter = getBeanForType(TraceFormatter.class); 253 if (formatter != null) { 254 tracer.setFormatter(formatter); 255 } 256 LOG.info("Using custom Tracer: " + tracer); 257 getContext().addInterceptStrategy(tracer); 258 } 259 260 HandleFault handleFault = getBeanForType(HandleFault.class); 261 if (handleFault != null) { 262 LOG.info("Using custom HandleFault: " + handleFault); 263 getContext().addInterceptStrategy(handleFault); 264 } 265 266 Delayer delayer = getBeanForType(Delayer.class); 267 if (delayer != null) { 268 LOG.info("Using custom Delayer: " + delayer); 269 getContext().addInterceptStrategy(delayer); 270 } 271 272 InflightRepository inflightRepository = getBeanForType(InflightRepository.class); 273 if (delayer != null) { 274 LOG.info("Using custom InflightRepository: " + inflightRepository); 275 getContext().setInflightRepository(inflightRepository); 276 } 277 278 ManagementStrategy managementStrategy = getBeanForType(ManagementStrategy.class); 279 if (managementStrategy != null) { 280 LOG.info("Using custom ManagementStrategy: " + managementStrategy); 281 getContext().setManagementStrategy(managementStrategy); 282 } 283 284 EventFactory eventFactory = getBeanForType(EventFactory.class); 285 if (eventFactory != null) { 286 LOG.info("Using custom EventFactory: " + eventFactory); 287 getContext().getManagementStrategy().setEventFactory(eventFactory); 288 } 289 290 // set the event notifier strategies if defined 291 Map<String, EventNotifier> eventNotifiers = getContext().getRegistry().lookupByType(EventNotifier.class); 292 if (eventNotifiers != null && !eventNotifiers.isEmpty()) { 293 for (String id : eventNotifiers.keySet()) { 294 EventNotifier notifier = eventNotifiers.get(id); 295 // do not add if already added, for instance a tracer that is also an InterceptStrategy class 296 if (!getContext().getManagementStrategy().getEventNotifiers().contains(notifier)) { 297 LOG.info("Using custom EventNotifier with id: " + id + " and implementation: " + notifier); 298 getContext().getManagementStrategy().addEventNotifier(notifier); 299 } 300 } 301 } 302 303 ShutdownStrategy shutdownStrategy = getBeanForType(ShutdownStrategy.class); 304 if (shutdownStrategy != null) { 305 LOG.info("Using custom ShutdownStrategy: " + shutdownStrategy); 306 getContext().setShutdownStrategy(shutdownStrategy); 307 } 308 309 // add global interceptors 310 Map<String, InterceptStrategy> interceptStrategies = getContext().getRegistry().lookupByType(InterceptStrategy.class); 311 if (interceptStrategies != null && !interceptStrategies.isEmpty()) { 312 for (String id : interceptStrategies.keySet()) { 313 InterceptStrategy strategy = interceptStrategies.get(id); 314 // do not add if already added, for instance a tracer that is also an InterceptStrategy class 315 if (!getContext().getInterceptStrategies().contains(strategy)) { 316 LOG.info("Using custom InterceptStrategy with id: " + id + " and implementation: " + strategy); 317 getContext().addInterceptStrategy(strategy); 318 } 319 } 320 } 321 322 // set the lifecycle strategy if defined 323 Map<String, LifecycleStrategy> lifecycleStrategies = getContext().getRegistry().lookupByType(LifecycleStrategy.class); 324 if (lifecycleStrategies != null && !lifecycleStrategies.isEmpty()) { 325 for (String id : lifecycleStrategies.keySet()) { 326 LifecycleStrategy strategy = lifecycleStrategies.get(id); 327 // do not add if already added, for instance a tracer that is also an InterceptStrategy class 328 if (!getContext().getLifecycleStrategies().contains(strategy)) { 329 LOG.info("Using custom LifecycleStrategy with id: " + id + " and implementation: " + strategy); 330 getContext().addLifecycleStrategy(strategy); 331 } 332 } 333 } 334 335 // set the default thread pool profile if defined 336 initThreadPoolProfiles(getContext()); 337 338 // Set the application context and camelContext for the beanPostProcessor 339 if (beanPostProcessor != null) { 340 if (beanPostProcessor instanceof ApplicationContextAware) { 341 ((ApplicationContextAware)beanPostProcessor).setApplicationContext(applicationContext); 342 } 343 if (beanPostProcessor instanceof CamelBeanPostProcessor) { 344 ((CamelBeanPostProcessor)beanPostProcessor).setCamelContext(getContext()); 345 } 346 } 347 348 initSpringCamelContext(getContext()); 349 350 // must init route refs before we prepare the routes below 351 initRouteRefs(); 352 353 // do special preparation for some concepts such as interceptors and policies 354 // this is needed as JAXB does not build exactly the same model definition as Spring DSL would do 355 // using route builders. So we have here a little custom code to fix the JAXB gaps 356 for (RouteDefinition route : routes) { 357 358 // abstracts is the cross cutting concerns 359 List<ProcessorDefinition> abstracts = new ArrayList<ProcessorDefinition>(); 360 361 // upper is the cross cutting concerns such as interceptors, error handlers etc 362 List<ProcessorDefinition> upper = new ArrayList<ProcessorDefinition>(); 363 364 // lower is the regular route 365 List<ProcessorDefinition> lower = new ArrayList<ProcessorDefinition>(); 366 367 prepareRouteForInit(route, abstracts, lower); 368 369 // toAsync should fix up itself at first 370 initToAsync(lower); 371 372 // interceptors should be first for the cross cutting concerns 373 initInterceptors(route, upper); 374 // then on completion 375 initOnCompletions(abstracts, upper); 376 // then transactions 377 initTransacted(abstracts, lower); 378 // then on exception 379 initOnExceptions(abstracts, upper); 380 381 // rebuild route as upper + lower 382 route.clearOutput(); 383 route.getOutputs().addAll(upper); 384 route.getOutputs().addAll(lower); 385 386 // configure parents 387 initParent(route); 388 } 389 390 if (dataFormats != null) { 391 getContext().setDataFormats(dataFormats.asMap()); 392 } 393 394 // lets force any lazy creation 395 getContext().addRouteDefinitions(routes); 396 397 if (LOG.isDebugEnabled()) { 398 LOG.debug("Found JAXB created routes: " + getRoutes()); 399 } 400 findRouteBuilders(); 401 installRoutes(); 402 } 403 404 private void prepareRouteForInit(RouteDefinition route, List<ProcessorDefinition> abstracts, 405 List<ProcessorDefinition> lower) { 406 // filter the route into abstracts and lower 407 for (ProcessorDefinition output : route.getOutputs()) { 408 if (output.isAbstract()) { 409 abstracts.add(output); 410 } else { 411 lower.add(output); 412 } 413 } 414 } 415 416 private void initParent(RouteDefinition route) { 417 for (ProcessorDefinition output : route.getOutputs()) { 418 output.setParent(route); 419 if (output.getOutputs() != null) { 420 // recursive the outputs 421 initParent(output); 422 } 423 } 424 } 425 426 @SuppressWarnings("unchecked") 427 private void initParent(ProcessorDefinition parent) { 428 List<ProcessorDefinition> children = parent.getOutputs(); 429 for (ProcessorDefinition child : children) { 430 child.setParent(parent); 431 if (child.getOutputs() != null) { 432 // recursive the children 433 initParent(child); 434 } 435 } 436 } 437 438 private void initToAsync(List<ProcessorDefinition> lower) { 439 List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>(); 440 ToDefinition toAsync = null; 441 442 for (ProcessorDefinition output : lower) { 443 if (toAsync != null) { 444 // add this output on toAsync 445 toAsync.getOutputs().add(output); 446 } else { 447 // regular outputs 448 outputs.add(output); 449 } 450 451 if (output instanceof ToDefinition) { 452 ToDefinition to = (ToDefinition) output; 453 if (to.isAsync() != null && to.isAsync()) { 454 // new current to async 455 toAsync = to; 456 } 457 } 458 } 459 460 // rebuild outputs 461 lower.clear(); 462 lower.addAll(outputs); 463 } 464 465 private void initOnExceptions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) { 466 // add global on exceptions if any 467 if (onExceptions != null && !onExceptions.isEmpty()) { 468 abstracts.addAll(onExceptions); 469 } 470 471 // now add onExceptions to the route 472 for (ProcessorDefinition output : abstracts) { 473 if (output instanceof OnExceptionDefinition) { 474 // on exceptions must be added at top, so the route flow is correct as 475 // on exceptions should be the first outputs 476 upper.add(0, output); 477 } 478 } 479 } 480 481 private void initInterceptors(RouteDefinition route, List<ProcessorDefinition> upper) { 482 // configure intercept 483 for (InterceptDefinition intercept : getIntercepts()) { 484 intercept.afterPropertiesSet(); 485 // add as first output so intercept is handled before the actual route and that gives 486 // us the needed head start to init and be able to intercept all the remaining processing steps 487 upper.add(0, intercept); 488 } 489 490 // configure intercept from 491 for (InterceptFromDefinition intercept : getInterceptFroms()) { 492 493 // should we only apply interceptor for a given endpoint uri 494 boolean match = true; 495 if (intercept.getUri() != null) { 496 match = false; 497 for (FromDefinition input : route.getInputs()) { 498 if (EndpointHelper.matchEndpoint(input.getUri(), intercept.getUri())) { 499 match = true; 500 break; 501 } 502 } 503 } 504 505 if (match) { 506 intercept.afterPropertiesSet(); 507 // add as first output so intercept is handled before the actual route and that gives 508 // us the needed head start to init and be able to intercept all the remaining processing steps 509 upper.add(0, intercept); 510 } 511 } 512 513 // configure intercept send to endpoint 514 for (InterceptSendToEndpointDefinition intercept : getInterceptSendToEndpoints()) { 515 intercept.afterPropertiesSet(); 516 // add as first output so intercept is handled before the actual route and that gives 517 // us the needed head start to init and be able to intercept all the remaining processing steps 518 upper.add(0, intercept); 519 } 520 } 521 522 private void initOnCompletions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) { 523 List<OnCompletionDefinition> completions = new ArrayList<OnCompletionDefinition>(); 524 525 // find the route scoped onCompletions 526 for (ProcessorDefinition out : abstracts) { 527 if (out instanceof OnCompletionDefinition) { 528 completions.add((OnCompletionDefinition) out); 529 } 530 } 531 532 // only add global onCompletion if there are no route already 533 if (completions.isEmpty()) { 534 completions = getOnCompletions(); 535 } 536 537 // are there any completions to init at all? 538 if (completions.isEmpty()) { 539 return; 540 } 541 542 upper.addAll(completions); 543 } 544 545 private void initTransacted(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> lower) { 546 TransactedDefinition transacted = null; 547 548 // add to correct type 549 for (ProcessorDefinition type : abstracts) { 550 if (type instanceof TransactedDefinition) { 551 if (transacted == null) { 552 transacted = (TransactedDefinition) type; 553 } else { 554 throw new IllegalArgumentException("The route can only have one transacted defined"); 555 } 556 } 557 } 558 559 if (transacted != null) { 560 // the outputs should be moved to the transacted policy 561 transacted.getOutputs().addAll(lower); 562 // and add it as the single output 563 lower.clear(); 564 lower.add(transacted); 565 } 566 } 567 568 private void initJMXAgent() throws Exception { 569 if (camelJMXAgent != null && camelJMXAgent.isAgentDisabled()) { 570 LOG.info("JMXAgent disabled"); 571 // clear the existing lifecycle strategies define by the DefaultCamelContext constructor 572 getContext().getLifecycleStrategies().clear(); 573 // no need to add a lifecycle strategy as we do not need one as JMX is disabled 574 getContext().setManagementStrategy(new DefaultManagementStrategy()); 575 } else if (camelJMXAgent != null) { 576 LOG.info("JMXAgent enabled: " + camelJMXAgent); 577 DefaultManagementAgent agent = new DefaultManagementAgent(getContext()); 578 agent.setConnectorPort(parseInteger(camelJMXAgent.getConnectorPort())); 579 agent.setCreateConnector(parseBoolean(camelJMXAgent.getCreateConnector())); 580 agent.setMBeanObjectDomainName(parseText(camelJMXAgent.getMbeanObjectDomainName())); 581 agent.setMBeanServerDefaultDomain(parseText(camelJMXAgent.getMbeanServerDefaultDomain())); 582 agent.setRegistryPort(parseInteger(camelJMXAgent.getRegistryPort())); 583 agent.setServiceUrlPath(parseText(camelJMXAgent.getServiceUrlPath())); 584 agent.setUsePlatformMBeanServer(parseBoolean(camelJMXAgent.getUsePlatformMBeanServer())); 585 agent.setOnlyRegisterProcessorWithCustomId(parseBoolean(camelJMXAgent.getOnlyRegisterProcessorWithCustomId())); 586 587 ManagementStrategy managementStrategy = new ManagedManagementStrategy(agent); 588 getContext().setManagementStrategy(managementStrategy); 589 590 // clear the existing lifecycle strategies define by the DefaultCamelContext constructor 591 getContext().getLifecycleStrategies().clear(); 592 getContext().addLifecycleStrategy(new DefaultManagementLifecycleStrategy(getContext())); 593 // set additional configuration from camelJMXAgent 594 boolean onlyId = agent.getOnlyRegisterProcessorWithCustomId() != null && agent.getOnlyRegisterProcessorWithCustomId(); 595 getContext().getManagementStrategy().onlyManageProcessorWithCustomId(onlyId); 596 getContext().getManagementStrategy().setStatisticsLevel(camelJMXAgent.getStatisticsLevel()); 597 } 598 } 599 600 private void initPropertyPlaceholder() throws Exception { 601 if (getCamelPropertyPlaceholder() != null) { 602 CamelPropertyPlaceholderDefinition def = getCamelPropertyPlaceholder(); 603 604 PropertiesComponent pc = new PropertiesComponent(); 605 pc.setLocation(def.getLocation()); 606 607 // if using a custom resolver 608 if (ObjectHelper.isNotEmpty(def.getPropertiesResolverRef())) { 609 PropertiesResolver resolver = CamelContextHelper.mandatoryLookup(getContext(), def.getPropertiesResolverRef(), 610 PropertiesResolver.class); 611 pc.setPropertiesResolver(resolver); 612 } 613 614 // register the properties component 615 getContext().addComponent("properties", pc); 616 } 617 } 618 619 private void initRouteRefs() throws Exception { 620 // add route refs to existing routes 621 if (routeRefs != null) { 622 for (RouteContextRefDefinition ref : routeRefs) { 623 List<RouteDefinition> defs = ref.lookupRoutes(getContext()); 624 for (RouteDefinition def : defs) { 625 if (LOG.isDebugEnabled()) { 626 LOG.debug("Adding route from " + ref + " -> " + def); 627 } 628 // add in top as they are most likely to be common/shared 629 // which you may want to start first 630 routes.add(0, def); 631 } 632 } 633 } 634 } 635 636 private <T> T getBeanForType(Class<T> clazz) { 637 T bean = null; 638 String[] names = getApplicationContext().getBeanNamesForType(clazz, true, true); 639 if (names.length == 1) { 640 bean = (T) getApplicationContext().getBean(names[0], clazz); 641 } 642 if (bean == null) { 643 ApplicationContext parentContext = getApplicationContext().getParent(); 644 if (parentContext != null) { 645 names = parentContext.getBeanNamesForType(clazz, true, true); 646 if (names.length == 1) { 647 bean = (T) parentContext.getBean(names[0], clazz); 648 } 649 } 650 } 651 return bean; 652 } 653 654 public void destroy() throws Exception { 655 getContext().stop(); 656 } 657 658 public void onApplicationEvent(ApplicationEvent event) { 659 // From Spring 3.0.1, The BeanFactory applicationEventListener 660 // and Bean's applicationEventListener will be called, 661 // So we just delegate the onApplicationEvent call here. 662 663 if (context != null) { 664 // let the spring camel context handle the events 665 context.onApplicationEvent(event); 666 } else { 667 if (LOG.isDebugEnabled()) { 668 LOG.debug("Publishing spring-event: " + event); 669 } 670 671 if (event instanceof ContextRefreshedEvent) { 672 // now lets start the CamelContext so that all its possible 673 // dependencies are initialized 674 try { 675 LOG.debug("Starting the context now!"); 676 getContext().start(); 677 } catch (Exception e) { 678 throw wrapRuntimeCamelException(e); 679 } 680 } 681 } 682 } 683 684 private String parseText(String text) throws Exception { 685 // ensure we support property placeholders 686 return getContext().resolvePropertyPlaceholders(text); 687 } 688 689 private Integer parseInteger(String text) throws Exception { 690 // ensure we support property placeholders 691 String s = getContext().resolvePropertyPlaceholders(text); 692 if (s != null) { 693 try { 694 return new Integer(s); 695 } catch (NumberFormatException e) { 696 if (s.equals(text)) { 697 throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e); 698 } else { 699 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e); 700 } 701 } 702 } 703 return null; 704 } 705 706 private Long parseLong(String text) throws Exception { 707 // ensure we support property placeholders 708 String s = getContext().resolvePropertyPlaceholders(text); 709 if (s != null) { 710 try { 711 return new Long(s); 712 } catch (NumberFormatException e) { 713 if (s.equals(text)) { 714 throw new IllegalArgumentException("Error parsing [" + s + "] as a Long.", e); 715 } else { 716 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Long.", e); 717 } 718 } 719 } 720 return null; 721 } 722 723 private Boolean parseBoolean(String text) throws Exception { 724 // ensure we support property placeholders 725 String s = getContext().resolvePropertyPlaceholders(text); 726 if (s != null) { 727 s = s.trim().toLowerCase(); 728 if (s.equals("true") || s.equals("false")) { 729 return new Boolean(s); 730 } else { 731 if (s.equals(text)) { 732 throw new IllegalArgumentException("Error parsing [" + s + "] as a Boolean."); 733 } else { 734 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Boolean."); 735 } 736 } 737 } 738 return null; 739 } 740 741 // Properties 742 // ------------------------------------------------------------------------- 743 public SpringCamelContext getContext() throws Exception { 744 if (context == null) { 745 context = createContext(); 746 } 747 return context; 748 } 749 750 public void setContext(SpringCamelContext context) { 751 this.context = context; 752 } 753 754 public List<RouteDefinition> getRoutes() { 755 return routes; 756 } 757 758 public void setRoutes(List<RouteDefinition> routes) { 759 this.routes = routes; 760 } 761 762 public List<InterceptDefinition> getIntercepts() { 763 return intercepts; 764 } 765 766 public void setIntercepts(List<InterceptDefinition> intercepts) { 767 this.intercepts = intercepts; 768 } 769 770 public List<InterceptFromDefinition> getInterceptFroms() { 771 return interceptFroms; 772 } 773 774 public void setInterceptFroms(List<InterceptFromDefinition> interceptFroms) { 775 this.interceptFroms = interceptFroms; 776 } 777 778 public List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints() { 779 return interceptSendToEndpoints; 780 } 781 782 public void setInterceptSendToEndpoints(List<InterceptSendToEndpointDefinition> interceptSendToEndpoints) { 783 this.interceptSendToEndpoints = interceptSendToEndpoints; 784 } 785 786 public ApplicationContext getApplicationContext() { 787 if (applicationContext == null) { 788 throw new IllegalArgumentException("No applicationContext has been injected!"); 789 } 790 return applicationContext; 791 } 792 793 public void setApplicationContext(ApplicationContext applicationContext) { 794 this.applicationContext = applicationContext; 795 } 796 797 public PropertiesDefinition getProperties() { 798 return properties; 799 } 800 801 public void setProperties(PropertiesDefinition properties) { 802 this.properties = properties; 803 } 804 805 public String[] getPackages() { 806 return packages; 807 } 808 809 /** 810 * Sets the package names to be recursively searched for Java classes which 811 * extend {@link RouteBuilder} to be auto-wired up to the 812 * {@link SpringCamelContext} as a route. Note that classes are excluded if 813 * they are specifically configured in the spring.xml 814 * <p/> 815 * A more advanced configuration can be done using {@link #setPackageScan(org.apache.camel.model.PackageScanDefinition)} 816 * 817 * @param packages the package names which are recursively searched 818 * @see #setPackageScan(org.apache.camel.model.PackageScanDefinition) 819 */ 820 public void setPackages(String[] packages) { 821 this.packages = packages; 822 } 823 824 public PackageScanDefinition getPackageScan() { 825 return packageScan; 826 } 827 828 /** 829 * Sets the package scanning information. Package scanning allows for the 830 * automatic discovery of certain camel classes at runtime for inclusion 831 * e.g. {@link RouteBuilder} implementations 832 * 833 * @param packageScan the package scan 834 */ 835 public void setPackageScan(PackageScanDefinition packageScan) { 836 this.packageScan = packageScan; 837 } 838 839 public CamelPropertyPlaceholderDefinition getCamelPropertyPlaceholder() { 840 return camelPropertyPlaceholder; 841 } 842 843 public void setCamelPropertyPlaceholder(CamelPropertyPlaceholderDefinition camelPropertyPlaceholder) { 844 this.camelPropertyPlaceholder = camelPropertyPlaceholder; 845 } 846 847 public void setBeanPostProcessor(BeanPostProcessor postProcessor) { 848 this.beanPostProcessor = postProcessor; 849 } 850 851 public BeanPostProcessor getBeanPostProcessor() { 852 return beanPostProcessor; 853 } 854 855 public void setCamelJMXAgent(CamelJMXAgentDefinition agent) { 856 camelJMXAgent = agent; 857 } 858 859 public String getTrace() { 860 return trace; 861 } 862 863 public void setTrace(String trace) { 864 this.trace = trace; 865 } 866 867 public String getStreamCache() { 868 return streamCache; 869 } 870 871 public void setStreamCache(String streamCache) { 872 this.streamCache = streamCache; 873 } 874 875 public String getDelayer() { 876 return delayer; 877 } 878 879 public void setDelayer(String delayer) { 880 this.delayer = delayer; 881 } 882 883 public String getHandleFault() { 884 return handleFault; 885 } 886 887 public void setHandleFault(String handleFault) { 888 this.handleFault = handleFault; 889 } 890 891 public String getAutoStartup() { 892 return autoStartup; 893 } 894 895 public void setAutoStartup(String autoStartup) { 896 this.autoStartup = autoStartup; 897 } 898 899 public CamelJMXAgentDefinition getCamelJMXAgent() { 900 return camelJMXAgent; 901 } 902 903 public List<RouteBuilderDefinition> getBuilderRefs() { 904 return builderRefs; 905 } 906 907 public void setBuilderRefs(List<RouteBuilderDefinition> builderRefs) { 908 this.builderRefs = builderRefs; 909 } 910 911 public List<RouteContextRefDefinition> getRouteRefs() { 912 return routeRefs; 913 } 914 915 public void setRouteRefs(List<RouteContextRefDefinition> routeRefs) { 916 this.routeRefs = routeRefs; 917 } 918 919 public String getErrorHandlerRef() { 920 return errorHandlerRef; 921 } 922 923 /** 924 * Sets the name of the error handler object used to default the error handling strategy 925 * 926 * @param errorHandlerRef the Spring bean ref of the error handler 927 */ 928 public void setErrorHandlerRef(String errorHandlerRef) { 929 this.errorHandlerRef = errorHandlerRef; 930 } 931 932 public void setDataFormats(DataFormatsDefinition dataFormats) { 933 this.dataFormats = dataFormats; 934 } 935 936 public DataFormatsDefinition getDataFormats() { 937 return dataFormats; 938 } 939 940 public void setOnExceptions(List<OnExceptionDefinition> onExceptions) { 941 this.onExceptions = onExceptions; 942 } 943 944 public List<OnExceptionDefinition> getOnExceptions() { 945 return onExceptions; 946 } 947 948 public List<OnCompletionDefinition> getOnCompletions() { 949 return onCompletions; 950 } 951 952 public void setOnCompletions(List<OnCompletionDefinition> onCompletions) { 953 this.onCompletions = onCompletions; 954 } 955 956 public ShutdownRoute getShutdownRoute() { 957 return shutdownRoute; 958 } 959 960 public void setShutdownRoute(ShutdownRoute shutdownRoute) { 961 this.shutdownRoute = shutdownRoute; 962 } 963 964 public ShutdownRunningTask getShutdownRunningTask() { 965 return shutdownRunningTask; 966 } 967 968 public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) { 969 this.shutdownRunningTask = shutdownRunningTask; 970 } 971 972 public List<ThreadPoolProfileDefinition> getThreadPoolProfiles() { 973 return threadPoolProfiles; 974 } 975 976 public void setThreadPoolProfiles(List<ThreadPoolProfileDefinition> threadPoolProfiles) { 977 this.threadPoolProfiles = threadPoolProfiles; 978 } 979 980 public String getDependsOn() { 981 return dependsOn; 982 } 983 984 public void setDependsOn(String dependsOn) { 985 this.dependsOn = dependsOn; 986 } 987 988 // Implementation methods 989 // ------------------------------------------------------------------------- 990 991 /** 992 * Create the context 993 */ 994 protected SpringCamelContext createContext() { 995 SpringCamelContext ctx = newCamelContext(); 996 ctx.setName(getId()); 997 return ctx; 998 } 999 1000 /** 1001 * Initializes the context 1002 * 1003 * @param ctx the context 1004 * @throws Exception is thrown if error occurred 1005 */ 1006 protected void initSpringCamelContext(SpringCamelContext ctx) throws Exception { 1007 if (streamCache != null) { 1008 ctx.setStreamCaching(parseBoolean(getStreamCache())); 1009 } 1010 if (trace != null) { 1011 ctx.setTracing(parseBoolean(getTrace())); 1012 } 1013 if (delayer != null) { 1014 ctx.setDelayer(parseLong(getDelayer())); 1015 } 1016 if (handleFault != null) { 1017 ctx.setHandleFault(parseBoolean(getHandleFault())); 1018 } 1019 if (errorHandlerRef != null) { 1020 ctx.setErrorHandlerBuilder(new ErrorHandlerBuilderRef(getErrorHandlerRef())); 1021 } 1022 if (autoStartup != null) { 1023 ctx.setAutoStartup(parseBoolean(getAutoStartup())); 1024 } 1025 if (shutdownRoute != null) { 1026 ctx.setShutdownRoute(getShutdownRoute()); 1027 } 1028 if (shutdownRunningTask != null) { 1029 ctx.setShutdownRunningTask(getShutdownRunningTask()); 1030 } 1031 } 1032 1033 protected SpringCamelContext newCamelContext() { 1034 return new SpringCamelContext(getApplicationContext()); 1035 } 1036 1037 private void initThreadPoolProfiles(CamelContext context) { 1038 Set<String> defaultIds = new HashSet<String>(); 1039 1040 // lookup and use custom profiles from the registry 1041 Map<String, ThreadPoolProfile> profiles = context.getRegistry().lookupByType(ThreadPoolProfile.class); 1042 if (profiles != null && !profiles.isEmpty()) { 1043 for (String id : profiles.keySet()) { 1044 ThreadPoolProfile profile = profiles.get(id); 1045 // do not add if already added, for instance a tracer that is also an InterceptStrategy class 1046 if (profile.isDefaultProfile()) { 1047 LOG.info("Using custom default ThreadPoolProfile with id: " + id + " and implementation: " + profile); 1048 context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile); 1049 defaultIds.add(id); 1050 } else { 1051 context.getExecutorServiceStrategy().registerThreadPoolProfile(profile); 1052 } 1053 } 1054 } 1055 1056 // use custom profiles defined in the CamelContext 1057 if (threadPoolProfiles != null && !threadPoolProfiles.isEmpty()) { 1058 for (ThreadPoolProfileDefinition profile : threadPoolProfiles) { 1059 if (profile.isDefaultProfile()) { 1060 LOG.info("Using custom default ThreadPoolProfile with id: " + profile.getId() + " and implementation: " + profile); 1061 context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile); 1062 defaultIds.add(profile.getId()); 1063 } else { 1064 context.getExecutorServiceStrategy().registerThreadPoolProfile(profile); 1065 } 1066 } 1067 } 1068 1069 // validate at most one is defined 1070 if (defaultIds.size() > 1) { 1071 throw new IllegalArgumentException("Only exactly one default ThreadPoolProfile is allowed, was " + defaultIds.size() + " ids: " + defaultIds); 1072 } 1073 } 1074 1075 /** 1076 * Strategy to install all available routes into the context 1077 */ 1078 protected void installRoutes() throws Exception { 1079 List<RouteBuilder> builders = new ArrayList<RouteBuilder>(); 1080 1081 // lets add route builders added from references 1082 if (builderRefs != null) { 1083 for (RouteBuilderDefinition builderRef : builderRefs) { 1084 RouteBuilder builder = builderRef.createRouteBuilder(getContext()); 1085 if (builder != null) { 1086 builders.add(builder); 1087 } else { 1088 // support to get the route here 1089 RoutesBuilder routes = builderRef.createRoutes(getContext()); 1090 if (routes != null) { 1091 this.builders.add(routes); 1092 } else { 1093 // Throw the exception that we can't find any build here 1094 throw new CamelException("Cannot find any routes with this RouteBuilder reference: " + builderRef); 1095 } 1096 } 1097 } 1098 } 1099 1100 // install already configured routes 1101 for (RoutesBuilder routeBuilder : this.builders) { 1102 getContext().addRoutes(routeBuilder); 1103 } 1104 1105 // install builders 1106 for (RouteBuilder builder : builders) { 1107 if (beanPostProcessor != null) { 1108 // Inject the annotated resource 1109 beanPostProcessor.postProcessBeforeInitialization(builder, builder.toString()); 1110 } 1111 getContext().addRoutes(builder); 1112 } 1113 } 1114 1115 /** 1116 * Strategy method to try find {@link RouteBuilder} instances on the classpath 1117 */ 1118 protected void findRouteBuilders() throws Exception { 1119 PackageScanClassResolver resolver = getContext().getPackageScanClassResolver(); 1120 addPackageElementContentsToScanDefinition(); 1121 1122 PackageScanDefinition packageScanDef = getPackageScan(); 1123 if (packageScanDef != null && packageScanDef.getPackages().size() > 0) { 1124 // use package scan filter 1125 PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter(); 1126 // support property placeholders in include and exclude 1127 for (String include : packageScanDef.getIncludes()) { 1128 include = getContext().resolvePropertyPlaceholders(include); 1129 filter.addIncludePattern(include); 1130 } 1131 for (String exclude : packageScanDef.getExcludes()) { 1132 exclude = getContext().resolvePropertyPlaceholders(exclude); 1133 filter.addExcludePattern(exclude); 1134 } 1135 resolver.addFilter(filter); 1136 1137 String[] normalized = normalizePackages(getContext(), packageScanDef.getPackages()); 1138 RouteBuilderFinder finder = new RouteBuilderFinder(getContext(), normalized, getContextClassLoaderOnStart(), 1139 getBeanPostProcessor(), getContext().getPackageScanClassResolver()); 1140 finder.appendBuilders(builders); 1141 } 1142 } 1143 1144 private void addPackageElementContentsToScanDefinition() { 1145 PackageScanDefinition packageScanDef = getPackageScan(); 1146 1147 if (getPackages() != null && getPackages().length > 0) { 1148 if (packageScanDef == null) { 1149 packageScanDef = new PackageScanDefinition(); 1150 setPackageScan(packageScanDef); 1151 } 1152 1153 for (String pkg : getPackages()) { 1154 packageScanDef.getPackages().add(pkg); 1155 } 1156 } 1157 } 1158 1159 private String[] normalizePackages(CamelContext context, List<String> unnormalized) throws Exception { 1160 List<String> packages = new ArrayList<String>(); 1161 for (String name : unnormalized) { 1162 // it may use property placeholders 1163 name = context.resolvePropertyPlaceholders(name); 1164 name = ObjectHelper.normalizeClassName(name); 1165 if (ObjectHelper.isNotEmpty(name)) { 1166 if (LOG.isTraceEnabled()) { 1167 LOG.trace("Using package: " + name + " to scan for RouteBuilder classes"); 1168 } 1169 packages.add(name); 1170 } 1171 } 1172 return packages.toArray(new String[packages.size()]); 1173 } 1174 1175 }