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