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