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    }