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