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.impl; 018 019 import java.io.IOException; 020 import java.util.ArrayList; 021 import java.util.Collection; 022 import java.util.HashMap; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.concurrent.Callable; 026 027 import javax.naming.Context; 028 029 import org.apache.camel.CamelContext; 030 import org.apache.camel.Component; 031 import org.apache.camel.Endpoint; 032 import org.apache.camel.Exchange; 033 import org.apache.camel.Processor; 034 import org.apache.camel.ProducerTemplate; 035 import org.apache.camel.ResolveEndpointFailedException; 036 import org.apache.camel.Route; 037 import org.apache.camel.Routes; 038 import org.apache.camel.RuntimeCamelException; 039 import org.apache.camel.Service; 040 import org.apache.camel.TypeConverter; 041 import org.apache.camel.builder.ErrorHandlerBuilder; 042 import org.apache.camel.impl.converter.DefaultTypeConverter; 043 import org.apache.camel.management.InstrumentationLifecycleStrategy; 044 import org.apache.camel.management.JmxSystemPropertyKeys; 045 import org.apache.camel.model.RouteType; 046 import org.apache.camel.processor.interceptor.Tracer; 047 import org.apache.camel.spi.ComponentResolver; 048 import org.apache.camel.spi.ExchangeConverter; 049 import org.apache.camel.spi.Injector; 050 import org.apache.camel.spi.InterceptStrategy; 051 import org.apache.camel.spi.Language; 052 import org.apache.camel.spi.LanguageResolver; 053 import org.apache.camel.spi.LifecycleStrategy; 054 import org.apache.camel.spi.Registry; 055 import org.apache.camel.util.FactoryFinder; 056 import org.apache.camel.util.NoFactoryAvailableException; 057 import org.apache.camel.util.ObjectHelper; 058 import org.apache.camel.util.ReflectionInjector; 059 import org.apache.camel.util.SystemHelper; 060 import org.apache.commons.logging.Log; 061 import org.apache.commons.logging.LogFactory; 062 063 import static org.apache.camel.util.ServiceHelper.startServices; 064 import static org.apache.camel.util.ServiceHelper.stopServices; 065 066 067 /** 068 * Represents the context used to configure routes and the policies to use. 069 * 070 * @version $Revision: 45781 $ 071 */ 072 public class DefaultCamelContext extends ServiceSupport implements CamelContext, Service { 073 private static final transient Log LOG = LogFactory.getLog(DefaultCamelContext.class); 074 private static final String NAME_PREFIX = "camel-"; 075 private static int nameSuffix; 076 077 private String name; 078 private final Map<String, Endpoint> endpoints = new HashMap<String, Endpoint>(); 079 private final Map<String, Component> components = new HashMap<String, Component>(); 080 private List<Route> routes; 081 private List<Service> servicesToClose = new ArrayList<Service>(); 082 private TypeConverter typeConverter; 083 private ExchangeConverter exchangeConverter; 084 private Injector injector; 085 private ComponentResolver componentResolver; 086 private boolean autoCreateComponents = true; 087 private LanguageResolver languageResolver = new DefaultLanguageResolver(); 088 private Registry registry; 089 private LifecycleStrategy lifecycleStrategy; 090 private List<RouteType> routeDefinitions = new ArrayList<RouteType>(); 091 private List<InterceptStrategy> interceptStrategies = new ArrayList<InterceptStrategy>(); 092 private Boolean trace; 093 private ErrorHandlerBuilder errorHandlerBuilder; 094 095 public DefaultCamelContext() { 096 name = NAME_PREFIX + ++nameSuffix; 097 098 if (Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED)) { 099 LOG.info("JMX is disabled. Using DefaultLifecycleStrategy."); 100 lifecycleStrategy = new DefaultLifecycleStrategy(); 101 } else { 102 try { 103 LOG.info("JMX enabled. Using InstrumentationLifecycleStrategy."); 104 lifecycleStrategy = new InstrumentationLifecycleStrategy(); 105 } catch (NoClassDefFoundError e) { 106 // if we can't instantiate the JMX enabled strategy then fallback to default 107 // could be because of missing .jars on the classpath 108 LOG.warn("Could not find needed classes for JMX lifecycle strategy." 109 + " Needed class is in spring-context.jar using Spring 2.5 or newer (" 110 + " spring-jmx.jar using Spring 2.0.x)." 111 + " NoClassDefFoundError: " + e.getMessage()); 112 } catch (Exception e) { 113 LOG.warn("Could not create JMX lifecycle strategy, caused by: " + e.getMessage()); 114 } 115 // if not created then fallback to default 116 if (lifecycleStrategy == null) { 117 LOG.warn("Not possible to use JMX lifecycle strategy. Using DefaultLifecycleStrategy instead."); 118 lifecycleStrategy = new DefaultLifecycleStrategy(); 119 } 120 } 121 } 122 123 /** 124 * Creates the {@link CamelContext} using the given JNDI context as the 125 * registry 126 * 127 * @param jndiContext 128 */ 129 public DefaultCamelContext(Context jndiContext) { 130 this(); 131 setJndiContext(jndiContext); 132 } 133 134 /** 135 * Creates the {@link CamelContext} using the given registry 136 */ 137 public DefaultCamelContext(Registry registry) { 138 this(); 139 this.registry = registry; 140 } 141 142 public String getName() { 143 return name; 144 } 145 146 /** 147 * Sets the name of the this context. 148 */ 149 public void setName(String name) { 150 this.name = name; 151 } 152 153 public void addComponent(String componentName, final Component component) { 154 if (component == null) { 155 throw new IllegalArgumentException("Component cannot be null"); 156 } 157 synchronized (components) { 158 if (components.containsKey(componentName)) { 159 throw new IllegalArgumentException("Component previously added: " + componentName); 160 } 161 component.setCamelContext(this); 162 components.put(componentName, component); 163 } 164 } 165 166 public Component getComponent(String name) { 167 // synchronize the look up and auto create so that 2 threads can't 168 // concurrently auto create the same component. 169 synchronized (components) { 170 Component component = components.get(name); 171 if (component == null && autoCreateComponents) { 172 try { 173 component = getComponentResolver().resolveComponent(name, this); 174 if (component != null) { 175 addComponent(name, component); 176 if (isStarted()) { 177 // If the component is looked up after the context 178 // is started, 179 // lets start it up. 180 startServices(component); 181 } 182 } 183 } catch (Exception e) { 184 throw new RuntimeCamelException("Could not auto create component: " + name, e); 185 } 186 } 187 return component; 188 } 189 } 190 191 public <T extends Component> T getComponent(String name, Class<T> componentType) { 192 Component component = getComponent(name); 193 if (componentType.isInstance(component)) { 194 return componentType.cast(component); 195 } else { 196 throw new IllegalArgumentException("The component is not of type: " + componentType + " but is: " 197 + component); 198 } 199 } 200 201 public Component removeComponent(String componentName) { 202 synchronized (components) { 203 return components.remove(componentName); 204 } 205 } 206 207 public Component getOrCreateComponent(String componentName, Callable<Component> factory) { 208 synchronized (components) { 209 Component component = components.get(componentName); 210 if (component == null) { 211 try { 212 component = factory.call(); 213 if (component == null) { 214 throw new RuntimeCamelException("Factory failed to create the " + componentName 215 + " component, it returned null."); 216 } 217 components.put(componentName, component); 218 component.setCamelContext(this); 219 } catch (Exception e) { 220 throw new RuntimeCamelException("Factory failed to create the " + componentName 221 + " component", e); 222 } 223 } 224 return component; 225 } 226 } 227 228 // Endpoint Management Methods 229 // ----------------------------------------------------------------------- 230 231 public Collection<Endpoint> getSingletonEndpoints() { 232 synchronized (endpoints) { 233 return new ArrayList<Endpoint>(endpoints.values()); 234 } 235 } 236 237 public Endpoint addSingletonEndpoint(String uri, Endpoint endpoint) throws Exception { 238 Endpoint oldEndpoint; 239 synchronized (endpoints) { 240 startServices(endpoint); 241 oldEndpoint = endpoints.remove(uri); 242 endpoints.put(uri, endpoint); 243 stopServices(oldEndpoint); 244 } 245 return oldEndpoint; 246 } 247 248 public Endpoint removeSingletonEndpoint(String uri) throws Exception { 249 Endpoint oldEndpoint; 250 synchronized (endpoints) { 251 oldEndpoint = endpoints.remove(uri); 252 stopServices(oldEndpoint); 253 } 254 return oldEndpoint; 255 } 256 257 public Endpoint getEndpoint(String uri) { 258 Endpoint answer; 259 synchronized (endpoints) { 260 answer = endpoints.get(uri); 261 if (answer == null) { 262 try { 263 264 // Use the URI prefix to find the component. 265 String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2); 266 if (splitURI[1] != null) { 267 String scheme = splitURI[0]; 268 Component component = getComponent(scheme); 269 270 // Ask the component to resolve the endpoint. 271 if (component != null) { 272 // Have the component create the endpoint if it can. 273 answer = component.createEndpoint(uri); 274 275 if (answer != null && LOG.isDebugEnabled()) { 276 LOG.debug(uri + " converted to endpoint: " + answer + " by component: " + component); 277 } 278 } 279 } 280 if (answer == null) { 281 answer = createEndpoint(uri); 282 } 283 284 // If it's a singleton then auto register it. 285 if (answer != null) { 286 addService(answer); 287 288 if (answer.isSingleton()) { 289 endpoints.put(uri, answer); 290 291 // TODO we should support non-singletons in the lifecycle 292 lifecycleStrategy.onEndpointAdd(answer); 293 } 294 } 295 } catch (Exception e) { 296 LOG.debug("Failed to resolve endpoint " + uri + ". Reason: " + e, e); 297 throw new ResolveEndpointFailedException(uri, e); 298 } 299 } 300 } 301 return answer; 302 } 303 304 305 public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) { 306 Endpoint endpoint = getEndpoint(name); 307 if (endpointType.isInstance(endpoint)) { 308 return endpointType.cast(endpoint); 309 } else { 310 throw new IllegalArgumentException("The endpoint is not of type: " + endpointType + " but is: " 311 + endpoint); 312 } 313 } 314 315 // Route Management Methods 316 // ----------------------------------------------------------------------- 317 public List<Route> getRoutes() { 318 if (routes == null) { 319 routes = new ArrayList<Route>(); 320 } 321 return routes; 322 } 323 324 public void setRoutes(List<Route> routes) { 325 this.routes = routes; 326 } 327 328 public void addRoutes(Collection<Route> routes) throws Exception { 329 if (this.routes == null) { 330 this.routes = new ArrayList<Route>(routes); 331 } else { 332 this.routes.addAll(routes); 333 } 334 lifecycleStrategy.onRoutesAdd(routes); 335 if (shouldStartRoutes()) { 336 startRoutes(routes); 337 } 338 } 339 340 public void addRoutes(Routes builder) throws Exception { 341 // lets now add the routes from the builder 342 builder.setContext(this); 343 List<Route> routeList = builder.getRouteList(); 344 LOG.debug("Adding routes from: " + builder + " routes: " + routeList); 345 addRoutes(routeList); 346 } 347 348 public void addRouteDefinitions(Collection<RouteType> routeDefinitions) throws Exception { 349 this.routeDefinitions.addAll(routeDefinitions); 350 if (shouldStartRoutes()) { 351 startRouteDefinitions(routeDefinitions); 352 } 353 354 } 355 356 /** 357 * Adds a service, starting it so that it will be stopped with this context 358 */ 359 public void addService(Object object) throws Exception { 360 if (object instanceof Service) { 361 Service service = (Service) object; 362 service.start(); 363 servicesToClose.add(service); 364 } 365 } 366 367 // Helper methods 368 // ----------------------------------------------------------------------- 369 370 public Language resolveLanguage(String language) { 371 return getLanguageResolver().resolveLanguage(language, this); 372 } 373 374 // Properties 375 // ----------------------------------------------------------------------- 376 public ExchangeConverter getExchangeConverter() { 377 if (exchangeConverter == null) { 378 exchangeConverter = createExchangeConverter(); 379 } 380 return exchangeConverter; 381 } 382 383 public void setExchangeConverter(ExchangeConverter exchangeConverter) { 384 this.exchangeConverter = exchangeConverter; 385 } 386 387 public TypeConverter getTypeConverter() { 388 if (typeConverter == null) { 389 typeConverter = createTypeConverter(); 390 } 391 return typeConverter; 392 } 393 394 public void setTypeConverter(TypeConverter typeConverter) { 395 this.typeConverter = typeConverter; 396 } 397 398 public Injector getInjector() { 399 if (injector == null) { 400 injector = createInjector(); 401 } 402 return injector; 403 } 404 405 public void setInjector(Injector injector) { 406 this.injector = injector; 407 } 408 409 public ComponentResolver getComponentResolver() { 410 if (componentResolver == null) { 411 componentResolver = createComponentResolver(); 412 } 413 return componentResolver; 414 } 415 416 public void setComponentResolver(ComponentResolver componentResolver) { 417 this.componentResolver = componentResolver; 418 } 419 420 public LanguageResolver getLanguageResolver() { 421 return languageResolver; 422 } 423 424 public void setLanguageResolver(LanguageResolver languageResolver) { 425 this.languageResolver = languageResolver; 426 } 427 428 public boolean isAutoCreateComponents() { 429 return autoCreateComponents; 430 } 431 432 public void setAutoCreateComponents(boolean autoCreateComponents) { 433 this.autoCreateComponents = autoCreateComponents; 434 } 435 436 public Registry getRegistry() { 437 if (registry == null) { 438 registry = createRegistry(); 439 } 440 return registry; 441 } 442 443 /** 444 * Sets the registry to the given JNDI context 445 * 446 * @param jndiContext is the JNDI context to use as the registry 447 * 448 * @see #setRegistry(org.apache.camel.spi.Registry) 449 */ 450 public void setJndiContext(Context jndiContext) { 451 setRegistry(new JndiRegistry(jndiContext)); 452 } 453 454 public void setRegistry(Registry registry) { 455 this.registry = registry; 456 } 457 458 public LifecycleStrategy getLifecycleStrategy() { 459 return lifecycleStrategy; 460 } 461 462 public void setLifecycleStrategy(LifecycleStrategy lifecycleStrategy) { 463 this.lifecycleStrategy = lifecycleStrategy; 464 } 465 466 public List<RouteType> getRouteDefinitions() { 467 return routeDefinitions; 468 } 469 470 public List<InterceptStrategy> getInterceptStrategies() { 471 return interceptStrategies; 472 } 473 474 public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) { 475 this.interceptStrategies = interceptStrategies; 476 } 477 478 public void addInterceptStrategy(InterceptStrategy interceptStrategy) { 479 getInterceptStrategies().add(interceptStrategy); 480 } 481 482 /** 483 * Returns true if tracing has been enabled or disabled via the {@link #setTrace(Boolean)} method 484 * or it has not been specified then default to the <b>camel.trace</b> system property 485 */ 486 public boolean getTrace() { 487 final Boolean value = getTracing(); 488 if (value != null) { 489 return value; 490 } else { 491 return SystemHelper.isSystemProperty("camel.trace"); 492 } 493 } 494 495 public Boolean getTracing() { 496 return trace; 497 } 498 499 public void setTrace(Boolean trace) { 500 this.trace = trace; 501 } 502 503 public <E extends Exchange> ProducerTemplate<E> createProducerTemplate() { 504 return new DefaultProducerTemplate<E>(this); 505 } 506 507 public ErrorHandlerBuilder getErrorHandlerBuilder() { 508 return errorHandlerBuilder; 509 } 510 511 /** 512 * Sets the default error handler builder which is inherited by the routes 513 */ 514 public void setErrorHandlerBuilder(ErrorHandlerBuilder errorHandlerBuilder) { 515 this.errorHandlerBuilder = errorHandlerBuilder; 516 } 517 518 // Implementation methods 519 // ----------------------------------------------------------------------- 520 521 protected void doStart() throws Exception { 522 if (getTrace()) { 523 // lets check if we already have already been configured and if not add the default 524 boolean found = false; 525 final List<InterceptStrategy> list = getInterceptStrategies(); 526 for (InterceptStrategy strategy : list) { 527 if (strategy instanceof Tracer) { 528 found = true; 529 } 530 } 531 if (!found) { 532 addInterceptStrategy(new Tracer()); 533 } 534 } 535 lifecycleStrategy.onContextStart(this); 536 537 forceLazyInitialization(); 538 if (components != null) { 539 for (Component component : components.values()) { 540 startServices(component); 541 } 542 } 543 startRouteDefinitions(routeDefinitions); 544 startRoutes(routes); 545 } 546 547 protected void startRouteDefinitions(Collection<RouteType> list) throws Exception { 548 if (list != null) { 549 Collection<Route> routes = new ArrayList<Route>(); 550 for (RouteType route : list) { 551 route.addRoutes(this, routes); 552 } 553 addRoutes(routes); 554 } 555 } 556 557 protected void doStop() throws Exception { 558 stopServices(servicesToClose); 559 if (components != null) { 560 for (Component component : components.values()) { 561 stopServices(component); 562 } 563 } 564 } 565 566 protected void startRoutes(Collection<Route> routeList) throws Exception { 567 if (routeList != null) { 568 for (Route<Exchange> route : routeList) { 569 List<Service> services = route.getServicesForRoute(); 570 for (Service service : services) { 571 addService(service); 572 } 573 } 574 } 575 } 576 577 /** 578 * Lets force some lazy initialization to occur upfront before we start any 579 * components and create routes 580 */ 581 protected void forceLazyInitialization() { 582 getExchangeConverter(); 583 getInjector(); 584 getLanguageResolver(); 585 getTypeConverter(); 586 } 587 588 /** 589 * Lazily create a default implementation 590 */ 591 protected ExchangeConverter createExchangeConverter() { 592 return new DefaultExchangeConverter(); 593 } 594 595 /** 596 * Lazily create a default implementation 597 */ 598 protected TypeConverter createTypeConverter() { 599 return new DefaultTypeConverter(getInjector()); 600 } 601 602 /** 603 * Lazily create a default implementation 604 */ 605 protected Injector createInjector() { 606 FactoryFinder finder = new FactoryFinder(); 607 try { 608 return (Injector) finder.newInstance("Injector"); 609 } catch (NoFactoryAvailableException e) { 610 // lets use the default 611 return new ReflectionInjector(); 612 } catch (IllegalAccessException e) { 613 throw new RuntimeCamelException(e); 614 } catch (InstantiationException e) { 615 throw new RuntimeCamelException(e); 616 } catch (IOException e) { 617 throw new RuntimeCamelException(e); 618 } catch (ClassNotFoundException e) { 619 throw new RuntimeCamelException(e); 620 } 621 } 622 623 /** 624 * Lazily create a default implementation 625 */ 626 protected ComponentResolver createComponentResolver() { 627 return new DefaultComponentResolver(); 628 } 629 630 /** 631 * Lazily create a default implementation 632 */ 633 protected Registry createRegistry() { 634 return new JndiRegistry(); 635 } 636 637 /** 638 * A pluggable strategy to allow an endpoint to be created without requiring 639 * a component to be its factory, such as for looking up the URI inside some 640 * {@link Registry} 641 * 642 * @param uri the uri for the endpoint to be created 643 * @return the newly created endpoint or null if it could not be resolved 644 */ 645 protected Endpoint createEndpoint(String uri) { 646 Object value = getRegistry().lookup(uri); 647 if (value instanceof Endpoint) { 648 return (Endpoint) value; 649 } else if (value instanceof Processor) { 650 return new ProcessorEndpoint(uri, this, (Processor) value); 651 } else if (value != null) { 652 return convertBeanToEndpoint(uri, value); 653 } 654 return null; 655 } 656 657 /** 658 * Attempt to convert the bean from a {@link Registry} to an endpoint using 659 * some kind of transformation or wrapper 660 * 661 * @param uri the uri for the endpoint (and name in the registry) 662 * @param bean the bean to be converted to an endpoint, which will be not null 663 * @return a new endpoint 664 */ 665 protected Endpoint convertBeanToEndpoint(String uri, Object bean) { 666 throw new IllegalArgumentException("uri: " + uri + " bean: " + bean 667 + " could not be converted to an Endpoint"); 668 } 669 670 /** 671 * Should we start newly added routes? 672 */ 673 protected boolean shouldStartRoutes() { 674 return isStarted() && !isStarting(); 675 } 676 677 678 }