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.model.dataformat.DataFormatType;
047 import org.apache.camel.processor.interceptor.Delayer;
048 import org.apache.camel.processor.interceptor.TraceFormatter;
049 import org.apache.camel.processor.interceptor.Tracer;
050 import org.apache.camel.spi.ComponentResolver;
051 import org.apache.camel.spi.ExchangeConverter;
052 import org.apache.camel.spi.Injector;
053 import org.apache.camel.spi.InterceptStrategy;
054 import org.apache.camel.spi.Language;
055 import org.apache.camel.spi.LanguageResolver;
056 import org.apache.camel.spi.LifecycleStrategy;
057 import org.apache.camel.spi.Registry;
058 import org.apache.camel.util.CamelContextHelper;
059 import org.apache.camel.util.FactoryFinder;
060 import org.apache.camel.util.NoFactoryAvailableException;
061 import org.apache.camel.util.ObjectHelper;
062 import org.apache.camel.util.ReflectionInjector;
063 import org.apache.camel.util.SystemHelper;
064 import org.apache.commons.logging.Log;
065 import org.apache.commons.logging.LogFactory;
066
067 import static org.apache.camel.util.ServiceHelper.startServices;
068 import static org.apache.camel.util.ServiceHelper.stopServices;
069
070
071 /**
072 * Represents the context used to configure routes and the policies to use.
073 *
074 * @version $Revision: 53280 $
075 */
076 public class DefaultCamelContext extends ServiceSupport implements CamelContext, Service {
077 private static final transient Log LOG = LogFactory.getLog(DefaultCamelContext.class);
078 private static final String NAME_PREFIX = "camel-";
079 private static int nameSuffix;
080
081 private String name;
082 private final Map<String, Endpoint> endpoints = new HashMap<String, Endpoint>();
083 private final Map<String, Component> components = new HashMap<String, Component>();
084 private List<Route> routes;
085 private List<Service> servicesToClose = new ArrayList<Service>();
086 private TypeConverter typeConverter;
087 private ExchangeConverter exchangeConverter;
088 private Injector injector;
089 private ComponentResolver componentResolver;
090 private boolean autoCreateComponents = true;
091 private LanguageResolver languageResolver = new DefaultLanguageResolver();
092 private Registry registry;
093 private LifecycleStrategy lifecycleStrategy;
094 private List<RouteType> routeDefinitions = new ArrayList<RouteType>();
095 private List<InterceptStrategy> interceptStrategies = new ArrayList<InterceptStrategy>();
096 private Boolean trace;
097 private Long delay;
098 private ErrorHandlerBuilder errorHandlerBuilder;
099 private Map<String, DataFormatType> dataFormats = new HashMap<String, DataFormatType>();
100
101 public DefaultCamelContext() {
102 name = NAME_PREFIX + ++nameSuffix;
103
104 if (Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED)) {
105 LOG.info("JMX is disabled. Using DefaultLifecycleStrategy.");
106 lifecycleStrategy = new DefaultLifecycleStrategy();
107 } else {
108 try {
109 LOG.info("JMX enabled. Using InstrumentationLifecycleStrategy.");
110 lifecycleStrategy = new InstrumentationLifecycleStrategy();
111 } catch (NoClassDefFoundError e) {
112 // if we can't instantiate the JMX enabled strategy then fallback to default
113 // could be because of missing .jars on the classpath
114 LOG.warn("Could not find needed classes for JMX lifecycle strategy."
115 + " Needed class is in spring-context.jar using Spring 2.5 or newer ("
116 + " spring-jmx.jar using Spring 2.0.x)."
117 + " NoClassDefFoundError: " + e.getMessage());
118 } catch (Exception e) {
119 LOG.warn("Could not create JMX lifecycle strategy, caused by: " + e.getMessage());
120 }
121 // if not created then fallback to default
122 if (lifecycleStrategy == null) {
123 LOG.warn("Not possible to use JMX lifecycle strategy. Using DefaultLifecycleStrategy instead.");
124 lifecycleStrategy = new DefaultLifecycleStrategy();
125 }
126 }
127 }
128
129 /**
130 * Creates the {@link CamelContext} using the given JNDI context as the
131 * registry
132 *
133 * @param jndiContext
134 */
135 public DefaultCamelContext(Context jndiContext) {
136 this();
137 setJndiContext(jndiContext);
138 }
139
140 /**
141 * Creates the {@link CamelContext} using the given registry
142 */
143 public DefaultCamelContext(Registry registry) {
144 this();
145 this.registry = registry;
146 }
147
148 public String getName() {
149 return name;
150 }
151
152 /**
153 * Sets the name of the this context.
154 */
155 public void setName(String name) {
156 this.name = name;
157 }
158
159 public void addComponent(String componentName, final Component component) {
160 if (component == null) {
161 throw new IllegalArgumentException("Component cannot be null");
162 }
163 synchronized (components) {
164 if (components.containsKey(componentName)) {
165 throw new IllegalArgumentException("Component previously added: " + componentName);
166 }
167 component.setCamelContext(this);
168 components.put(componentName, component);
169 }
170 }
171
172 public Component getComponent(String name) {
173 // synchronize the look up and auto create so that 2 threads can't
174 // concurrently auto create the same component.
175 synchronized (components) {
176 Component component = components.get(name);
177 if (component == null && autoCreateComponents) {
178 try {
179 component = getComponentResolver().resolveComponent(name, this);
180 if (component != null) {
181 addComponent(name, component);
182 if (isStarted()) {
183 // If the component is looked up after the context
184 // is started,
185 // lets start it up.
186 startServices(component);
187 }
188 }
189 } catch (Exception e) {
190 throw new RuntimeCamelException("Could not auto create component: " + name, e);
191 }
192 }
193 return component;
194 }
195 }
196
197 public <T extends Component> T getComponent(String name, Class<T> componentType) {
198 Component component = getComponent(name);
199 if (componentType.isInstance(component)) {
200 return componentType.cast(component);
201 } else {
202 throw new IllegalArgumentException("The component is not of type: " + componentType + " but is: "
203 + component);
204 }
205 }
206
207 public Component removeComponent(String componentName) {
208 synchronized (components) {
209 return components.remove(componentName);
210 }
211 }
212
213 public Component getOrCreateComponent(String componentName, Callable<Component> factory) {
214 synchronized (components) {
215 Component component = components.get(componentName);
216 if (component == null) {
217 try {
218 component = factory.call();
219 if (component == null) {
220 throw new RuntimeCamelException("Factory failed to create the " + componentName
221 + " component, it returned null.");
222 }
223 components.put(componentName, component);
224 component.setCamelContext(this);
225 } catch (Exception e) {
226 throw new RuntimeCamelException("Factory failed to create the " + componentName
227 + " component", e);
228 }
229 }
230 return component;
231 }
232 }
233
234 // Endpoint Management Methods
235 // -----------------------------------------------------------------------
236
237 public Collection<Endpoint> getEndpoints() {
238 synchronized (endpoints) {
239 return new ArrayList<Endpoint>(endpoints.values());
240 }
241 }
242
243 public Collection<Endpoint> getEndpoints(String uri) {
244 Collection<Endpoint> answer = new ArrayList<Endpoint>();
245 Collection<Endpoint> coll;
246 synchronized (endpoints) {
247 Endpoint ep = endpoints.get(uri);
248 if (ep != null) {
249 answer.add(ep);
250 return answer;
251 }
252 coll = new ArrayList<Endpoint>(endpoints.values());
253 }
254 for (Endpoint ep : coll) {
255 if (!ep.isSingleton() && uri.equals(ep.getEndpointUri())) {
256 answer.add(ep);
257 }
258 }
259 return answer;
260 }
261
262 public Collection<Endpoint> getSingletonEndpoints() {
263 Collection<Endpoint> answer = new ArrayList<Endpoint>();
264 Collection<Endpoint> coll = getEndpoints();
265 for (Endpoint ep : coll) {
266 if (ep.isSingleton()) {
267 answer.add(ep);
268 }
269 }
270 return answer;
271 }
272
273 public Endpoint addEndpoint(String uri, Endpoint endpoint) throws Exception {
274 Endpoint oldEndpoint;
275 synchronized (endpoints) {
276 startServices(endpoint);
277 oldEndpoint = endpoints.remove(uri);
278 endpoints.put(CamelContextHelper.getEndpointKey(uri, endpoint), endpoint);
279 if (oldEndpoint != null) {
280 stopServices(oldEndpoint);
281 }
282 }
283 return oldEndpoint;
284 }
285
286 public Collection<Endpoint> removeEndpoints(String uri) throws Exception {
287 Collection<Endpoint> answer = new ArrayList<Endpoint>();
288 synchronized (endpoints) {
289 Endpoint oldEndpoint = endpoints.remove(uri);
290 if (oldEndpoint != null) {
291 answer.add(oldEndpoint);
292 stopServices(oldEndpoint);
293 } else {
294 for (Map.Entry entry : endpoints.entrySet()) {
295 oldEndpoint = (Endpoint)entry.getValue();
296 if (!oldEndpoint.isSingleton() && uri.equals(oldEndpoint.getEndpointUri())) {
297 answer.add(oldEndpoint);
298 stopServices(oldEndpoint);
299 endpoints.remove(entry.getKey());
300 }
301 }
302 }
303 }
304 return answer;
305 }
306
307 public Endpoint addSingletonEndpoint(String uri, Endpoint endpoint) throws Exception {
308 return addEndpoint(uri, endpoint);
309 }
310
311 public Endpoint removeSingletonEndpoint(String uri) throws Exception {
312 Collection<Endpoint> answer = removeEndpoints(uri);
313 return (Endpoint) (answer.size() > 0 ? answer.toArray()[0] : null);
314 }
315
316 public Endpoint getEndpoint(String uri) {
317 Endpoint<?> answer;
318 synchronized (endpoints) {
319 answer = endpoints.get(uri);
320 if (answer == null) {
321 try {
322
323 // Use the URI prefix to find the component.
324 String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
325 if (splitURI[1] != null) {
326 String scheme = splitURI[0];
327 Component<?> component = getComponent(scheme);
328
329 // Ask the component to resolve the endpoint.
330 if (component != null) {
331 // Have the component create the endpoint if it can.
332 answer = component.createEndpoint(uri);
333
334 if (answer != null && LOG.isDebugEnabled()) {
335 LOG.debug(uri + " converted to endpoint: " + answer + " by component: " + component);
336 }
337 }
338 }
339 if (answer == null) {
340 answer = createEndpoint(uri);
341 }
342
343 // If it's a singleton then auto register it.
344 if (answer != null) {
345 addService(answer);
346
347 endpoints.put(CamelContextHelper.getEndpointKey(uri, answer), answer);
348 lifecycleStrategy.onEndpointAdd(answer);
349 }
350 } catch (Exception e) {
351 LOG.debug("Failed to resolve endpoint " + uri + ". Reason: " + e, e);
352 throw new ResolveEndpointFailedException(uri, e);
353 }
354 }
355 }
356 return answer;
357 }
358
359 public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
360 Endpoint endpoint = getEndpoint(name);
361 if (endpointType.isInstance(endpoint)) {
362 return endpointType.cast(endpoint);
363 } else {
364 throw new IllegalArgumentException("The endpoint is not of type: " + endpointType + " but is: "
365 + endpoint);
366 }
367 }
368
369 // Route Management Methods
370 // -----------------------------------------------------------------------
371 public List<Route> getRoutes() {
372 if (routes == null) {
373 routes = new ArrayList<Route>();
374 }
375 return routes;
376 }
377
378 public void setRoutes(List<Route> routes) {
379 this.routes = routes;
380 throw new UnsupportedOperationException("overriding existing routes is not supported yet, use addRoutes instead");
381 }
382
383 public void addRoutes(Collection<Route> routes) throws Exception {
384 if (this.routes == null) {
385 this.routes = new ArrayList<Route>();
386 }
387
388 if (routes != null) {
389 this.routes.addAll(routes);
390
391 lifecycleStrategy.onRoutesAdd(routes);
392 if (shouldStartRoutes()) {
393 startRoutes(routes);
394 }
395 }
396 }
397
398 public void addRoutes(Routes builder) throws Exception {
399 // lets now add the routes from the builder
400 builder.setContext(this);
401 List<Route> routeList = builder.getRouteList();
402 if (LOG.isDebugEnabled()) {
403 LOG.debug("Adding routes from: " + builder + " routes: " + routeList);
404 }
405 addRoutes(routeList);
406 }
407
408 public void addRouteDefinitions(Collection<RouteType> routeDefinitions) throws Exception {
409 this.routeDefinitions.addAll(routeDefinitions);
410 if (shouldStartRoutes()) {
411 startRouteDefinitions(routeDefinitions);
412 }
413
414 }
415
416 /**
417 * Adds a service, starting it so that it will be stopped with this context
418 */
419 public void addService(Object object) throws Exception {
420 if (object instanceof Service) {
421 Service service = (Service) object;
422 getLifecycleStrategy().onServiceAdd(this, service);
423 service.start();
424 servicesToClose.add(service);
425 }
426 }
427
428 // Helper methods
429 // -----------------------------------------------------------------------
430
431 public Language resolveLanguage(String language) {
432 return getLanguageResolver().resolveLanguage(language, this);
433 }
434
435 // Properties
436 // -----------------------------------------------------------------------
437 public ExchangeConverter getExchangeConverter() {
438 if (exchangeConverter == null) {
439 exchangeConverter = createExchangeConverter();
440 }
441 return exchangeConverter;
442 }
443
444 public void setExchangeConverter(ExchangeConverter exchangeConverter) {
445 this.exchangeConverter = exchangeConverter;
446 }
447
448 public TypeConverter getTypeConverter() {
449 if (typeConverter == null) {
450 typeConverter = createTypeConverter();
451 }
452 return typeConverter;
453 }
454
455 public void setTypeConverter(TypeConverter typeConverter) {
456 this.typeConverter = typeConverter;
457 }
458
459 public Injector getInjector() {
460 if (injector == null) {
461 injector = createInjector();
462 }
463 return injector;
464 }
465
466 public void setInjector(Injector injector) {
467 this.injector = injector;
468 }
469
470 public ComponentResolver getComponentResolver() {
471 if (componentResolver == null) {
472 componentResolver = createComponentResolver();
473 }
474 return componentResolver;
475 }
476
477 public void setComponentResolver(ComponentResolver componentResolver) {
478 this.componentResolver = componentResolver;
479 }
480
481 public LanguageResolver getLanguageResolver() {
482 return languageResolver;
483 }
484
485 public void setLanguageResolver(LanguageResolver languageResolver) {
486 this.languageResolver = languageResolver;
487 }
488
489 public boolean isAutoCreateComponents() {
490 return autoCreateComponents;
491 }
492
493 public void setAutoCreateComponents(boolean autoCreateComponents) {
494 this.autoCreateComponents = autoCreateComponents;
495 }
496
497 public Registry getRegistry() {
498 if (registry == null) {
499 registry = createRegistry();
500 }
501 return registry;
502 }
503
504 /**
505 * Sets the registry to the given JNDI context
506 *
507 * @param jndiContext is the JNDI context to use as the registry
508 *
509 * @see #setRegistry(org.apache.camel.spi.Registry)
510 */
511 public void setJndiContext(Context jndiContext) {
512 setRegistry(new JndiRegistry(jndiContext));
513 }
514
515 public void setRegistry(Registry registry) {
516 this.registry = registry;
517 }
518
519 public LifecycleStrategy getLifecycleStrategy() {
520 return lifecycleStrategy;
521 }
522
523 public void setLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
524 this.lifecycleStrategy = lifecycleStrategy;
525 }
526
527 public List<RouteType> getRouteDefinitions() {
528 return routeDefinitions;
529 }
530
531 public List<InterceptStrategy> getInterceptStrategies() {
532 return interceptStrategies;
533 }
534
535 public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
536 this.interceptStrategies = interceptStrategies;
537 }
538
539 public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
540 getInterceptStrategies().add(interceptStrategy);
541 }
542
543 /**
544 * Returns true if tracing has been enabled or disabled via the {@link #setTrace(Boolean)} method
545 * or it has not been specified then default to the <b>camel.trace</b> system property
546 */
547 public boolean getTrace() {
548 final Boolean value = getTracing();
549 if (value != null) {
550 return value;
551 } else {
552 return SystemHelper.isSystemProperty("camel.trace");
553 }
554 }
555
556 public Boolean getTracing() {
557 return trace;
558 }
559
560 public void setTrace(Boolean trace) {
561 this.trace = trace;
562 }
563
564 /**
565 * Returns the delay in millis if delaying has been enabled or disabled via the {@link #setDelay(Long)} method
566 * or it has not been specified then default to the <b>camel.delay</b> system property
567 */
568 public long getDelay() {
569 final Long value = getDelaying();
570 if (value != null) {
571 return value;
572 } else {
573 String prop = SystemHelper.getSystemProperty("camel.delay");
574 return prop != null ? Long.getLong(prop) : 0;
575 }
576 }
577
578 public Long getDelaying() {
579 return delay;
580 }
581
582 public void setDelay(Long delay) {
583 this.delay = delay;
584 }
585
586 public <E extends Exchange> ProducerTemplate<E> createProducerTemplate() {
587 return new DefaultProducerTemplate<E>(this);
588 }
589
590 public ErrorHandlerBuilder getErrorHandlerBuilder() {
591 return errorHandlerBuilder;
592 }
593
594 /**
595 * Sets the default error handler builder which is inherited by the routes
596 */
597 public void setErrorHandlerBuilder(ErrorHandlerBuilder errorHandlerBuilder) {
598 this.errorHandlerBuilder = errorHandlerBuilder;
599 }
600
601 // Implementation methods
602 // -----------------------------------------------------------------------
603
604 protected void doStart() throws Exception {
605 if (getTrace()) {
606 // only add a new tracer if not already configued
607 if (Tracer.getTracer(this) == null) {
608 Tracer tracer = new Tracer();
609 // lets see if we have a formatter if so use it
610 TraceFormatter formatter = this.getRegistry().lookup("traceFormatter", TraceFormatter.class);
611 if (formatter != null) {
612 tracer.setFormatter(formatter);
613 }
614 addInterceptStrategy(tracer);
615 }
616 }
617
618 if (getDelay() > 0) {
619 // only add a new delayer if not already configued
620 if (Delayer.getDelayer(this) == null) {
621 addInterceptStrategy(new Delayer(getDelay()));
622 }
623 }
624
625 lifecycleStrategy.onContextStart(this);
626
627 forceLazyInitialization();
628 if (components != null) {
629 for (Component component : components.values()) {
630 startServices(component);
631 }
632 }
633 startRouteDefinitions(routeDefinitions);
634 startRoutes(routes);
635 }
636
637 protected void startRouteDefinitions(Collection<RouteType> list) throws Exception {
638 if (list != null) {
639 Collection<Route> routes = new ArrayList<Route>();
640 for (RouteType route : list) {
641 route.addRoutes(this, routes);
642 }
643 addRoutes(routes);
644 }
645 }
646
647 protected void doStop() throws Exception {
648 stopServices(servicesToClose);
649 if (components != null) {
650 for (Component component : components.values()) {
651 stopServices(component);
652 }
653 }
654 }
655
656 protected void startRoutes(Collection<Route> routeList) throws Exception {
657 if (routeList != null) {
658 for (Route<Exchange> route : routeList) {
659 List<Service> services = route.getServicesForRoute();
660 for (Service service : services) {
661 addService(service);
662 }
663 }
664 }
665 }
666
667 /**
668 * Lets force some lazy initialization to occur upfront before we start any
669 * components and create routes
670 */
671 protected void forceLazyInitialization() {
672 getExchangeConverter();
673 getInjector();
674 getLanguageResolver();
675 getTypeConverter();
676 }
677
678 /**
679 * Lazily create a default implementation
680 */
681 protected ExchangeConverter createExchangeConverter() {
682 return new DefaultExchangeConverter();
683 }
684
685 /**
686 * Lazily create a default implementation
687 */
688 protected TypeConverter createTypeConverter() {
689 return new DefaultTypeConverter(getInjector());
690 }
691
692 /**
693 * Lazily create a default implementation
694 */
695 protected Injector createInjector() {
696 FactoryFinder finder = new FactoryFinder();
697 try {
698 return (Injector) finder.newInstance("Injector");
699 } catch (NoFactoryAvailableException e) {
700 // lets use the default
701 return new ReflectionInjector();
702 } catch (IllegalAccessException e) {
703 throw new RuntimeCamelException(e);
704 } catch (InstantiationException e) {
705 throw new RuntimeCamelException(e);
706 } catch (IOException e) {
707 throw new RuntimeCamelException(e);
708 } catch (ClassNotFoundException e) {
709 throw new RuntimeCamelException(e);
710 }
711 }
712
713 /**
714 * Lazily create a default implementation
715 */
716 protected ComponentResolver createComponentResolver() {
717 return new DefaultComponentResolver();
718 }
719
720 /**
721 * Lazily create a default implementation
722 */
723 protected Registry createRegistry() {
724 return new JndiRegistry();
725 }
726
727 /**
728 * A pluggable strategy to allow an endpoint to be created without requiring
729 * a component to be its factory, such as for looking up the URI inside some
730 * {@link Registry}
731 *
732 * @param uri the uri for the endpoint to be created
733 * @return the newly created endpoint or null if it could not be resolved
734 */
735 protected Endpoint createEndpoint(String uri) {
736 Object value = getRegistry().lookup(uri);
737 if (value instanceof Endpoint) {
738 return (Endpoint) value;
739 } else if (value instanceof Processor) {
740 return new ProcessorEndpoint(uri, this, (Processor) value);
741 } else if (value != null) {
742 return convertBeanToEndpoint(uri, value);
743 }
744 return null;
745 }
746
747 /**
748 * Attempt to convert the bean from a {@link Registry} to an endpoint using
749 * some kind of transformation or wrapper
750 *
751 * @param uri the uri for the endpoint (and name in the registry)
752 * @param bean the bean to be converted to an endpoint, which will be not null
753 * @return a new endpoint
754 */
755 protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
756 throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
757 + " could not be converted to an Endpoint");
758 }
759
760 /**
761 * Should we start newly added routes?
762 */
763 protected boolean shouldStartRoutes() {
764 return isStarted() && !isStarting();
765 }
766
767 public void setDataFormats(Map<String, DataFormatType> dataFormats) {
768 this.dataFormats = dataFormats;
769 }
770
771 public Map<String, DataFormatType> getDataFormats() {
772 return dataFormats;
773 }
774 }