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