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    
020    import java.io.IOException;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.HashMap;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.concurrent.Callable;
027    
028    import javax.naming.Context;
029    
030    import org.apache.camel.CamelContext;
031    import org.apache.camel.Component;
032    import org.apache.camel.Endpoint;
033    import org.apache.camel.Exchange;
034    import org.apache.camel.Processor;
035    import org.apache.camel.ProducerTemplate;
036    import org.apache.camel.ResolveEndpointFailedException;
037    import org.apache.camel.Route;
038    import org.apache.camel.Routes;
039    import org.apache.camel.RuntimeCamelException;
040    import org.apache.camel.Service;
041    import org.apache.camel.TypeConverter;
042    import org.apache.camel.impl.converter.DefaultTypeConverter;
043    import org.apache.camel.model.RouteType;
044    import org.apache.camel.spi.ComponentResolver;
045    import org.apache.camel.spi.ExchangeConverter;
046    import org.apache.camel.spi.Injector;
047    import org.apache.camel.spi.InterceptStrategy;
048    import org.apache.camel.spi.Language;
049    import org.apache.camel.spi.LanguageResolver;
050    import org.apache.camel.spi.LifecycleStrategy;
051    import org.apache.camel.spi.Registry;
052    import org.apache.camel.util.FactoryFinder;
053    import org.apache.camel.util.NoFactoryAvailableException;
054    import org.apache.camel.util.ObjectHelper;
055    import org.apache.camel.util.ReflectionInjector;
056    import org.apache.commons.logging.Log;
057    import org.apache.commons.logging.LogFactory;
058    
059    import static org.apache.camel.util.ServiceHelper.startServices;
060    import static org.apache.camel.util.ServiceHelper.stopServices;
061    /**
062     * Represents the context used to configure routes and the policies to use.
063     *
064     * @version $Revision: 42244 $
065     */
066    public class DefaultCamelContext extends ServiceSupport implements CamelContext, Service {
067        private static final transient Log LOG = LogFactory.getLog(DefaultCamelContext.class);
068        private static final String NAME_PREFIX = "camel-";
069        private static int nameSuffix;
070    
071        private String name;
072        private final Map<String, Endpoint> endpoints = new HashMap<String, Endpoint>();
073        private final Map<String, Component> components = new HashMap<String, Component>();
074        private List<Route> routes;
075        private List<Service> servicesToClose = new ArrayList<Service>();
076        private TypeConverter typeConverter;
077        private ExchangeConverter exchangeConverter;
078        private Injector injector;
079        private ComponentResolver componentResolver;
080        private boolean autoCreateComponents = true;
081        private LanguageResolver languageResolver = new DefaultLanguageResolver();
082        private Registry registry;
083        private LifecycleStrategy lifecycleStrategy = new DefaultLifecycleStrategy();
084        private List<RouteType> routeDefinitions = new ArrayList<RouteType>();
085        private List<InterceptStrategy> interceptStrategies = new ArrayList<InterceptStrategy>();
086    
087        public DefaultCamelContext() {
088            name = NAME_PREFIX + ++nameSuffix;
089        }
090    
091        /**
092         * Creates the {@link CamelContext} using the given JNDI context as the
093         * registry
094         *
095         * @param jndiContext
096         */
097        public DefaultCamelContext(Context jndiContext) {
098            this(new JndiRegistry(jndiContext));
099        }
100    
101        /**
102         * Creates the {@link CamelContext} using the given registry
103         */
104        public DefaultCamelContext(Registry registry) {
105            this();
106            this.registry = registry;
107        }
108    
109        public String getName() {
110            return name;
111        }
112    
113        /**
114         * Sets the name of the this context.
115         */
116        public void setName(String name) {
117            this.name = name;
118        }
119    
120        public void addComponent(String componentName, final Component component) {
121            if (component == null) {
122                throw new IllegalArgumentException("Component cannot be null");
123            }
124            synchronized (components) {
125                if (components.containsKey(componentName)) {
126                    throw new IllegalArgumentException("Component previously added: " + componentName);
127                }
128                component.setCamelContext(this);
129                components.put(componentName, component);
130            }
131        }
132    
133        public Component getComponent(String name) {
134            // synchronize the look up and auto create so that 2 threads can't
135            // concurrently auto create the same component.
136            synchronized (components) {
137                Component component = components.get(name);
138                if (component == null && autoCreateComponents) {
139                    try {
140                        component = getComponentResolver().resolveComponent(name, this);
141                        if (component != null) {
142                            addComponent(name, component);
143                            if (isStarted()) {
144                                // If the component is looked up after the context
145                                // is started,
146                                // lets start it up.
147                                startServices(component);
148                            }
149                        }
150                    } catch (Exception e) {
151                        throw new RuntimeCamelException("Could not auto create component: " + name, e);
152                    }
153                }
154                return component;
155            }
156        }
157    
158        public <T extends Component> T getComponent(String name, Class<T> componentType) {
159            Component component = getComponent(name);
160            if (componentType.isInstance(component)) {
161                return componentType.cast(component);
162            } else {
163                throw new IllegalArgumentException("The component is not of type: " + componentType + " but is: "
164                                                   + component);
165            }
166        }
167    
168        public Component removeComponent(String componentName) {
169            synchronized (components) {
170                return components.remove(componentName);
171            }
172        }
173    
174        public Component getOrCreateComponent(String componentName, Callable<Component> factory) {
175            synchronized (components) {
176                Component component = components.get(componentName);
177                if (component == null) {
178                    try {
179                        component = factory.call();
180                        if (component == null) {
181                            throw new RuntimeCamelException("Factory failed to create the " + componentName
182                                                            + " component, it returned null.");
183                        }
184                        components.put(componentName, component);
185                        component.setCamelContext(this);
186                    } catch (Exception e) {
187                        throw new RuntimeCamelException("Factory failed to create the " + componentName
188                                                        + " component", e);
189                    }
190                }
191                return component;
192            }
193        }
194    
195        // Endpoint Management Methods
196        // -----------------------------------------------------------------------
197    
198        public Collection<Endpoint> getSingletonEndpoints() {
199            synchronized (endpoints) {
200                return new ArrayList<Endpoint>(endpoints.values());
201            }
202        }
203    
204        public Endpoint addSingletonEndpoint(String uri, Endpoint endpoint) throws Exception {
205            Endpoint oldEndpoint;
206            synchronized (endpoints) {
207                startServices(endpoint);
208                oldEndpoint = endpoints.remove(uri);
209                endpoints.put(uri, endpoint);
210                stopServices(oldEndpoint);
211            }
212            return oldEndpoint;
213        }
214    
215        public Endpoint removeSingletonEndpoint(String uri) throws Exception {
216            Endpoint oldEndpoint;
217            synchronized (endpoints) {
218                oldEndpoint = endpoints.remove(uri);
219                stopServices(oldEndpoint);
220            }
221            return oldEndpoint;
222        }
223    
224        public Endpoint getEndpoint(String uri) {
225            Endpoint answer;
226            synchronized (endpoints) {
227                answer = endpoints.get(uri);
228                if (answer == null) {
229                    try {
230    
231                        // Use the URI prefix to find the component.
232                        String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
233                        if (splitURI[1] != null) {
234                            String scheme = splitURI[0];
235                            Component component = getComponent(scheme);
236    
237                            // Ask the component to resolve the endpoint.
238                            if (component != null) {
239                                // Have the component create the endpoint if it can.
240                                answer = component.createEndpoint(uri);
241    
242                                if (answer != null && LOG.isDebugEnabled()) {
243                                    LOG.debug(uri + " converted to endpoint: " + answer + " by component: " + component);
244                                }
245                            }
246                        }
247                        if (answer == null) {
248                            answer = createEndpoint(uri);
249                        }
250    
251                        // If it's a singleton then auto register it.
252                        if (answer != null) {
253                            addService(answer);
254    
255                            if (answer.isSingleton()) {
256                                endpoints.put(uri, answer);
257    
258                                // TODO we should support non-singletons in the lifecycle
259                                lifecycleStrategy.onEndpointAdd(answer);
260                            }
261                        }
262                    } catch (Exception e) {
263                        LOG.debug("Failed to resolve endpoint " + uri + ". Reason: " + e, e);
264                        throw new ResolveEndpointFailedException(uri, e);
265                    }
266                }
267            }
268            return answer;
269        }
270    
271    
272        public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
273            Endpoint endpoint = getEndpoint(name);
274            if (endpointType.isInstance(endpoint)) {
275                return endpointType.cast(endpoint);
276            } else {
277                throw new IllegalArgumentException("The endpoint is not of type: " + endpointType + " but is: "
278                                                   + endpoint);
279            }
280        }
281    
282        // Route Management Methods
283        // -----------------------------------------------------------------------
284        public List<Route> getRoutes() {
285            return routes;
286        }
287    
288        public void setRoutes(List<Route> routes) {
289            this.routes = routes;
290        }
291    
292        public void addRoutes(Collection<Route> routes) throws Exception {
293            if (this.routes == null) {
294                this.routes = new ArrayList<Route>(routes);
295            } else {
296                this.routes.addAll(routes);
297            }
298            lifecycleStrategy.onRoutesAdd(routes);
299            if (shouldStartRoutes()) {
300                startRoutes(routes);
301            }
302        }
303    
304        public void addRoutes(Routes builder) throws Exception {
305            // lets now add the routes from the builder
306            builder.setContext(this);
307            List<Route> routeList = builder.getRouteList();
308            LOG.debug("Adding routes from: " + builder + " routes: " + routeList);
309            addRoutes(routeList);
310        }
311    
312        public void addRouteDefinitions(Collection<RouteType> routeDefinitions) throws Exception {
313            this.routeDefinitions.addAll(routeDefinitions);
314            if (shouldStartRoutes()) {
315                startRouteDefinitions(routeDefinitions);
316            }
317    
318        }
319    
320        /**
321         * Adds a service, starting it so that it will be stopped with this context
322         */
323        public void addService(Object object) throws Exception {
324            if (object instanceof Service) {
325                Service service = (Service) object;
326                service.start();
327                servicesToClose.add(service);
328            }
329        }
330    
331        // Helper methods
332        // -----------------------------------------------------------------------
333    
334        public Language resolveLanguage(String language) {
335            return getLanguageResolver().resolveLanguage(language, this);
336        }
337    
338        // Properties
339        // -----------------------------------------------------------------------
340        public ExchangeConverter getExchangeConverter() {
341            if (exchangeConverter == null) {
342                exchangeConverter = createExchangeConverter();
343            }
344            return exchangeConverter;
345        }
346    
347        public void setExchangeConverter(ExchangeConverter exchangeConverter) {
348            this.exchangeConverter = exchangeConverter;
349        }
350    
351        public TypeConverter getTypeConverter() {
352            if (typeConverter == null) {
353                typeConverter = createTypeConverter();
354            }
355            return typeConverter;
356        }
357    
358        public void setTypeConverter(TypeConverter typeConverter) {
359            this.typeConverter = typeConverter;
360        }
361    
362        public Injector getInjector() {
363            if (injector == null) {
364                injector = createInjector();
365            }
366            return injector;
367        }
368    
369        public void setInjector(Injector injector) {
370            this.injector = injector;
371        }
372    
373        public ComponentResolver getComponentResolver() {
374            if (componentResolver == null) {
375                componentResolver = createComponentResolver();
376            }
377            return componentResolver;
378        }
379    
380        public void setComponentResolver(ComponentResolver componentResolver) {
381            this.componentResolver = componentResolver;
382        }
383    
384        public LanguageResolver getLanguageResolver() {
385            return languageResolver;
386        }
387    
388        public void setLanguageResolver(LanguageResolver languageResolver) {
389            this.languageResolver = languageResolver;
390        }
391    
392        public boolean isAutoCreateComponents() {
393            return autoCreateComponents;
394        }
395    
396        public void setAutoCreateComponents(boolean autoCreateComponents) {
397            this.autoCreateComponents = autoCreateComponents;
398        }
399    
400        public Registry getRegistry() {
401            if (registry == null) {
402                registry = createRegistry();
403            }
404            return registry;
405        }
406    
407        public void setRegistry(Registry registry) {
408            this.registry = registry;
409        }
410    
411        public LifecycleStrategy getLifecycleStrategy() {
412            return lifecycleStrategy;
413        }
414    
415        public void setLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
416            this.lifecycleStrategy = lifecycleStrategy;
417        }
418    
419        public List<RouteType> getRouteDefinitions() {
420            return routeDefinitions;
421        }
422    
423        public List<InterceptStrategy> getInterceptStrategies() {
424            return interceptStrategies;
425        }
426    
427        public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
428            this.interceptStrategies = interceptStrategies;
429        }
430    
431        public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
432            getInterceptStrategies().add(interceptStrategy);
433        }
434    
435        // Implementation methods
436        // -----------------------------------------------------------------------
437    
438        protected void doStart() throws Exception {
439            forceLazyInitialization();
440            if (components != null) {
441                for (Component component : components.values()) {
442                    startServices(component);
443                }
444            }
445            startRouteDefinitions(routeDefinitions);
446            startRoutes(routes);
447        }
448    
449        protected void startRouteDefinitions(Collection<RouteType> list) throws Exception {
450            if (list != null) {
451                Collection<Route> routes = new ArrayList<Route>();
452                for (RouteType route : list) {
453                    route.addRoutes(this, routes);
454                }
455                addRoutes(routes);
456            }
457        }
458    
459        protected void doStop() throws Exception {
460            stopServices(servicesToClose);
461            if (components != null) {
462                for (Component component : components.values()) {
463                    stopServices(component);
464                }
465            }
466        }
467    
468        protected void startRoutes(Collection<Route> routeList) throws Exception {
469            if (routeList != null) {
470                for (Route<Exchange> route : routeList) {
471                    List<Service> services = route.getServicesForRoute();
472                    for (Service service : services) {
473                        addService(service);
474                    }
475                }
476            }
477        }
478    
479        /**
480         * Lets force some lazy initialization to occur upfront before we start any
481         * components and create routes
482         */
483        protected void forceLazyInitialization() {
484            getExchangeConverter();
485            getInjector();
486            getLanguageResolver();
487            getTypeConverter();
488        }
489    
490        /**
491         * Lazily create a default implementation
492         */
493        protected ExchangeConverter createExchangeConverter() {
494            return new DefaultExchangeConverter();
495        }
496    
497        /**
498         * Lazily create a default implementation
499         */
500        protected TypeConverter createTypeConverter() {
501            return new DefaultTypeConverter(getInjector());
502        }
503    
504        /**
505         * Lazily create a default implementation
506         */
507        protected Injector createInjector() {
508            FactoryFinder finder = new FactoryFinder();
509            try {
510                return (Injector)finder.newInstance("Injector");
511            } catch (NoFactoryAvailableException e) {
512                // lets use the default
513                return new ReflectionInjector();
514            } catch (IllegalAccessException e) {
515                throw new RuntimeCamelException(e);
516            } catch (InstantiationException e) {
517                throw new RuntimeCamelException(e);
518            } catch (IOException e) {
519                throw new RuntimeCamelException(e);
520            } catch (ClassNotFoundException e) {
521                throw new RuntimeCamelException(e);
522            }
523        }
524    
525        /**
526         * Lazily create a default implementation
527         */
528        protected ComponentResolver createComponentResolver() {
529            return new DefaultComponentResolver();
530        }
531    
532        /**
533         * Lazily create a default implementation
534         */
535        protected Registry createRegistry() {
536            return new JndiRegistry();
537        }
538    
539        /**
540         * A pluggable strategy to allow an endpoint to be created without requiring
541         * a component to be its factory, such as for looking up the URI inside some
542         * {@link Registry}
543         *
544         * @param uri the uri for the endpoint to be created
545         * @return the newly created endpoint or null if it could not be resolved
546         */
547        protected Endpoint createEndpoint(String uri) {
548            Object value = getRegistry().lookup(uri);
549            if (value instanceof Endpoint) {
550                return (Endpoint)value;
551            } else if (value instanceof Processor) {
552                return new ProcessorEndpoint(uri, this, (Processor)value);
553            } else if (value != null) {
554                return convertBeanToEndpoint(uri, value);
555            }
556            return null;
557        }
558    
559        /**
560         * Attempt to convert the bean from a {@link Registry} to an endpoint using
561         * some kind of transformation or wrapper
562         *
563         * @param uri the uri for the endpoint (and name in the registry)
564         * @param bean the bean to be converted to an endpoint, which will be not null
565         * @return a new endpoint
566         */
567        protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
568            throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
569                                               + " could not be converted to an Endpoint");
570        }
571    
572        /**
573         * Should we start newly added routes?
574         */
575        protected boolean shouldStartRoutes() {
576            return isStarted() && !isStarting();
577        }
578    
579        public <E extends Exchange> ProducerTemplate<E> createProducerTemplate() {
580            return new DefaultProducerTemplate<E>(this);
581        }
582    
583    }