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