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