001    /**
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.camel.impl;
019    
020    import org.apache.camel.CamelContext;
021    import org.apache.camel.Component;
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.Exchange;
024    import org.apache.camel.ResolveEndpointFailedException;
025    import org.apache.camel.Route;
026    import org.apache.camel.RuntimeCamelException;
027    import org.apache.camel.Service;
028    import org.apache.camel.TypeConverter;
029    import org.apache.camel.builder.RouteBuilder;
030    import org.apache.camel.impl.converter.DefaultTypeConverter;
031    import org.apache.camel.spi.ComponentResolver;
032    import org.apache.camel.spi.ExchangeConverter;
033    import org.apache.camel.spi.Injector;
034    import org.apache.camel.util.FactoryFinder;
035    import org.apache.camel.util.NoFactoryAvailableException;
036    import org.apache.camel.util.ObjectHelper;
037    import static org.apache.camel.util.ServiceHelper.startServices;
038    import static org.apache.camel.util.ServiceHelper.stopServices;
039    
040    import java.io.IOException;
041    import java.util.ArrayList;
042    import java.util.Collection;
043    import java.util.HashMap;
044    import java.util.List;
045    import java.util.Map;
046    import java.util.concurrent.Callable;
047    
048    /**
049     * Represents the context used to configure routes and the policies to use.
050     *
051     * @version $Revision: 520517 $
052     * @org.apache.xbean.XBean element="container" rootElement="true"
053     */
054    public class DefaultCamelContext extends ServiceSupport implements CamelContext, Service {
055        private Map<String, Endpoint> endpoints = new HashMap<String, Endpoint>();
056        private Map<String, Component> components = new HashMap<String, Component>();
057        private List<Route> routes;
058        private List<Service> servicesToClose = new ArrayList<Service>();
059        private TypeConverter typeConverter;
060        private ExchangeConverter exchangeConverter;
061        private Injector injector;
062        private ComponentResolver componentResolver;
063        private boolean autoCreateComponents = true;
064    
065        /**
066         * Adds a component to the container.
067         */
068        public void addComponent(String componentName, final Component component) {
069            if (component == null) {
070                throw new IllegalArgumentException("Component cannot be null");
071            }
072            synchronized (components) {
073                if (components.containsKey(componentName)) {
074                    throw new IllegalArgumentException("Component previously added: " + componentName);
075                }
076                component.setCamelContext(this);
077                components.put(componentName, component);
078            }
079        }
080    
081        public Component getComponent(String name) {
082            // synchronize the look up and auto create so that 2 threads can't
083            // concurrently auto create the same component.
084            synchronized (components) {
085                Component component = components.get(name);
086                if (component == null && autoCreateComponents) {
087                    try {
088                        component = getComponentResolver().resolveComponent(name, this);
089                        if (component != null) {
090                            addComponent(name, component);
091                            if (isStarted()) {
092                                // If the component is looked up after the context is started,
093                                // lets start it up.
094                                startServices(component);
095                            }
096                        }
097                    }
098                    catch (Exception e) {
099                        throw new RuntimeCamelException("Could not auto create component: " + name, e);
100                    }
101                }
102                return component;
103            }
104        }
105    
106        public <T extends Component> T getComponent(String name, Class<T> componentType) {
107            Component component = getComponent(name);
108            if (componentType.isInstance(component)) {
109                return componentType.cast(component);
110            }
111            else {
112                throw new IllegalArgumentException("The component is not of type: " + componentType + " but is: " + component);
113            }
114        }
115    
116        /**
117         * Removes a previously added component.
118         *
119         * @param componentName
120         * @return the previously added component or null if it had not been previously added.
121         */
122        public Component removeComponent(String componentName) {
123            synchronized (components) {
124                return components.remove(componentName);
125            }
126        }
127    
128        /**
129         * Gets the a previously added component by name or lazily creates the component
130         * using the factory Callback.
131         *
132         * @param componentName
133         * @param factory       used to create a new component instance if the component was not previously added.
134         * @return
135         */
136        public Component getOrCreateComponent(String componentName, Callable<Component> factory) {
137            synchronized (components) {
138                Component component = components.get(componentName);
139                if (component == null) {
140                    try {
141                        component = factory.call();
142                        if (component == null) {
143                            throw new RuntimeCamelException("Factory failed to create the " + componentName + " component, it returned null.");
144                        }
145                        components.put(componentName, component);
146                        component.setCamelContext(this);
147                    }
148                    catch (Exception e) {
149                        throw new RuntimeCamelException("Factory failed to create the " + componentName + " component", e);
150                    }
151                }
152                return component;
153            }
154        }
155    
156        // Endpoint Management Methods
157        //-----------------------------------------------------------------------
158    
159        public Collection<Endpoint> getSingletonEndpoints() {
160            synchronized (endpoints) {
161                return new ArrayList<Endpoint>(endpoints.values());
162            }
163        }
164    
165        public Endpoint addSingletonEndpoint(String uri, Endpoint endpoint) throws Exception {
166            Endpoint oldEndpoint;
167            synchronized (endpoints) {
168                startServices(endpoint);
169                oldEndpoint = endpoints.remove(uri);
170                endpoints.put(uri, endpoint);
171                stopServices(oldEndpoint);
172            }
173            return oldEndpoint;
174        }
175    
176        public Endpoint removeSingletonEndpoint(String uri) throws Exception {
177            Endpoint oldEndpoint;
178            synchronized (endpoints) {
179                oldEndpoint = endpoints.remove(uri);
180                stopServices(oldEndpoint);
181            }
182            return oldEndpoint;
183        }
184    
185        /**
186         * Resolves the given URI to an endpoint
187         */
188        public Endpoint getEndpoint(String uri) {
189            Endpoint answer;
190            synchronized (endpoints) {
191                answer = endpoints.get(uri);
192                if (answer == null) {
193                    try {
194    
195                        // Use the URI prefix to find the component.
196                        String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
197                        if (splitURI[1] == null) {
198                            throw new IllegalArgumentException("Invalid URI, it did not contain a scheme: " + uri);
199                        }
200                        String scheme = splitURI[0];
201                        Component component = getComponent(scheme);
202    
203                        // Ask the component to resolve the endpoint.
204                        if (component != null) {
205    
206                            // Have the component create the endpoint if it can.
207                            answer = component.createEndpoint(uri);
208    
209                            // If it's a singleton then auto register it.
210                            if (answer != null && answer.isSingleton()) {
211                                if (answer != null) {
212                                    startServices(answer);
213                                    endpoints.put(uri, answer);
214                                }
215                            }
216                        }
217                    }
218                    catch (Exception e) {
219                        throw new ResolveEndpointFailedException(uri, e);
220                    }
221                }
222            }
223            return answer;
224        }
225    
226        public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
227            Endpoint endpoint = getEndpoint(name);
228            if (endpointType.isInstance(endpoint)) {
229                return endpointType.cast(endpoint);
230            }
231            else {
232                throw new IllegalArgumentException("The endpoint is not of type: " + endpointType + " but is: " + endpoint);
233            }
234        }
235    
236        // Route Management Methods
237        //-----------------------------------------------------------------------
238        public List<Route> getRoutes() {
239            return routes;
240        }
241    
242        public void setRoutes(List<Route> routes) {
243            this.routes = routes;
244        }
245    
246        public void addRoutes(Collection<Route> routes) throws Exception {
247            if (this.routes == null) {
248                this.routes = new ArrayList<Route>(routes);
249            }
250            else {
251                this.routes.addAll(routes);
252            }
253            if (isStarted()) {
254                startRoutes(routes);
255            }
256        }
257    
258        public void addRoutes(RouteBuilder builder) throws Exception {
259            // lets now add the routes from the builder
260            builder.setContext(this);
261            addRoutes(builder.getRouteList());
262        }
263    
264        // Properties
265        //-----------------------------------------------------------------------
266        public ExchangeConverter getExchangeConverter() {
267            if (exchangeConverter == null) {
268                exchangeConverter = createExchangeConverter();
269            }
270            return exchangeConverter;
271        }
272    
273        public void setExchangeConverter(ExchangeConverter exchangeConverter) {
274            this.exchangeConverter = exchangeConverter;
275        }
276    
277        public TypeConverter getTypeConverter() {
278            if (typeConverter == null) {
279                typeConverter = createTypeConverter();
280            }
281            return typeConverter;
282        }
283    
284        public void setTypeConverter(TypeConverter typeConverter) {
285            this.typeConverter = typeConverter;
286        }
287    
288        public Injector getInjector() {
289            if (injector == null) {
290                injector = createInjector();
291            }
292            return injector;
293        }
294    
295        public void setInjector(Injector injector) {
296            this.injector = injector;
297        }
298    
299        public ComponentResolver getComponentResolver() {
300            if (componentResolver == null) {
301                componentResolver = createComponentResolver();
302            }
303            return componentResolver;
304        }
305    
306        public void setComponentResolver(ComponentResolver componentResolver) {
307            this.componentResolver = componentResolver;
308        }
309    
310        // Implementation methods
311        //-----------------------------------------------------------------------
312    
313        protected void doStart() throws Exception {
314            if (components != null) {
315                for (Component component : components.values()) {
316                    startServices(component);
317                }
318            }
319            startRoutes(routes);
320        }
321    
322        protected void doStop() throws Exception {
323            stopServices(servicesToClose);
324            if (components != null) {
325                for (Component component : components.values()) {
326                    stopServices(component);
327                }
328            }
329        }
330    
331        protected void startRoutes(Collection<Route> routeList) throws Exception {
332            if (routeList != null) {
333                for (Route<Exchange> route : routeList) {
334                    List<Service> services = route.getServicesForRoute();
335                    servicesToClose.addAll(services);
336                    startServices(services);
337                }
338            }
339        }
340    
341        /**
342         * Lazily create a default implementation
343         */
344        protected ExchangeConverter createExchangeConverter() {
345            return new DefaultExchangeConverter();
346        }
347    
348        /**
349         * Lazily create a default implementation
350         */
351        protected TypeConverter createTypeConverter() {
352            return new DefaultTypeConverter();
353        }
354    
355        /**
356         * Lazily create a default implementation
357         */
358        protected Injector createInjector() {
359            FactoryFinder finder = new FactoryFinder();
360            try {
361                return (Injector) finder.newInstance("Injector");
362            }
363            catch (NoFactoryAvailableException e) {
364                // lets use the default
365                return new ReflectionInjector();
366            }
367            catch (IllegalAccessException e) {
368                throw new RuntimeCamelException(e);
369            }
370            catch (InstantiationException e) {
371                throw new RuntimeCamelException(e);
372            }
373            catch (IOException e) {
374                throw new RuntimeCamelException(e);
375            }
376            catch (ClassNotFoundException e) {
377                throw new RuntimeCamelException(e);
378            }
379        }
380    
381        /**
382         * Lazily create a default implementation
383         */
384        protected ComponentResolver createComponentResolver() {
385            return new DefaultComponentResolver();
386        }
387    
388        public boolean isAutoCreateComponents() {
389            return autoCreateComponents;
390        }
391    
392        public void setAutoCreateComponents(boolean autoCreateComponents) {
393            this.autoCreateComponents = autoCreateComponents;
394        }
395    }