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    }