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