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 }