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