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