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