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: 677554 $
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    
098            if (Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED)) {
099                LOG.info("JMX is disabled. Using DefaultLifecycleStrategy.");
100                lifecycleStrategy = new DefaultLifecycleStrategy();
101            } else {
102                try {
103                    LOG.info("JMX enabled. Using InstrumentationLifecycleStrategy.");
104                    lifecycleStrategy = new InstrumentationLifecycleStrategy();
105                } catch (NoClassDefFoundError e) {
106                    // if we can't instantiate the JMX enabled strategy then fallback to default
107                    // could be because of missing .jars on the classpath
108                    LOG.warn("Could not find needed classes for JMX lifecycle strategy."
109                        + " Are you missing spring-context.jar by any chance? NoClassDefFoundError: " + e.getMessage());
110                } catch (Exception e) {
111                    LOG.warn("Could not create JMX lifecycle strategy, caused by: " + e.getMessage());
112                }
113                // if not created then fallback to default
114                if (lifecycleStrategy == null) {
115                    LOG.warn("Not possible to use JMX lifecycle strategy. Using DefaultLifecycleStrategy instead.");
116                    lifecycleStrategy = new DefaultLifecycleStrategy();
117                }
118            }
119        }
120    
121        /**
122         * Creates the {@link CamelContext} using the given JNDI context as the
123         * registry
124         *
125         * @param jndiContext
126         */
127        public DefaultCamelContext(Context jndiContext) {
128            this(new JndiRegistry(jndiContext));
129        }
130    
131        /**
132         * Creates the {@link CamelContext} using the given registry
133         */
134        public DefaultCamelContext(Registry registry) {
135            this();
136            this.registry = registry;
137        }
138    
139        public String getName() {
140            return name;
141        }
142    
143        /**
144         * Sets the name of the this context.
145         */
146        public void setName(String name) {
147            this.name = name;
148        }
149    
150        public void addComponent(String componentName, final Component component) {
151            if (component == null) {
152                throw new IllegalArgumentException("Component cannot be null");
153            }
154            synchronized (components) {
155                if (components.containsKey(componentName)) {
156                    throw new IllegalArgumentException("Component previously added: " + componentName);
157                }
158                component.setCamelContext(this);
159                components.put(componentName, component);
160            }
161        }
162    
163        public Component getComponent(String name) {
164            // synchronize the look up and auto create so that 2 threads can't
165            // concurrently auto create the same component.
166            synchronized (components) {
167                Component component = components.get(name);
168                if (component == null && autoCreateComponents) {
169                    try {
170                        component = getComponentResolver().resolveComponent(name, this);
171                        if (component != null) {
172                            addComponent(name, component);
173                            if (isStarted()) {
174                                // If the component is looked up after the context
175                                // is started,
176                                // lets start it up.
177                                startServices(component);
178                            }
179                        }
180                    } catch (Exception e) {
181                        throw new RuntimeCamelException("Could not auto create component: " + name, e);
182                    }
183                }
184                return component;
185            }
186        }
187    
188        public <T extends Component> T getComponent(String name, Class<T> componentType) {
189            Component component = getComponent(name);
190            if (componentType.isInstance(component)) {
191                return componentType.cast(component);
192            } else {
193                throw new IllegalArgumentException("The component is not of type: " + componentType + " but is: "
194                        + component);
195            }
196        }
197    
198        public Component removeComponent(String componentName) {
199            synchronized (components) {
200                return components.remove(componentName);
201            }
202        }
203    
204        public Component getOrCreateComponent(String componentName, Callable<Component> factory) {
205            synchronized (components) {
206                Component component = components.get(componentName);
207                if (component == null) {
208                    try {
209                        component = factory.call();
210                        if (component == null) {
211                            throw new RuntimeCamelException("Factory failed to create the " + componentName
212                                    + " component, it returned null.");
213                        }
214                        components.put(componentName, component);
215                        component.setCamelContext(this);
216                    } catch (Exception e) {
217                        throw new RuntimeCamelException("Factory failed to create the " + componentName
218                                + " component", e);
219                    }
220                }
221                return component;
222            }
223        }
224    
225        // Endpoint Management Methods
226        // -----------------------------------------------------------------------
227    
228        public Collection<Endpoint> getSingletonEndpoints() {
229            synchronized (endpoints) {
230                return new ArrayList<Endpoint>(endpoints.values());
231            }
232        }
233    
234        public Endpoint addSingletonEndpoint(String uri, Endpoint endpoint) throws Exception {
235            Endpoint oldEndpoint;
236            synchronized (endpoints) {
237                startServices(endpoint);
238                oldEndpoint = endpoints.remove(uri);
239                endpoints.put(uri, endpoint);
240                stopServices(oldEndpoint);
241            }
242            return oldEndpoint;
243        }
244    
245        public Endpoint removeSingletonEndpoint(String uri) throws Exception {
246            Endpoint oldEndpoint;
247            synchronized (endpoints) {
248                oldEndpoint = endpoints.remove(uri);
249                stopServices(oldEndpoint);
250            }
251            return oldEndpoint;
252        }
253    
254        public Endpoint getEndpoint(String uri) {
255            Endpoint answer;
256            synchronized (endpoints) {
257                answer = endpoints.get(uri);
258                if (answer == null) {
259                    try {
260    
261                        // Use the URI prefix to find the component.
262                        String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
263                        if (splitURI[1] != null) {
264                            String scheme = splitURI[0];
265                            Component component = getComponent(scheme);
266    
267                            // Ask the component to resolve the endpoint.
268                            if (component != null) {
269                                // Have the component create the endpoint if it can.
270                                answer = component.createEndpoint(uri);
271    
272                                if (answer != null && LOG.isDebugEnabled()) {
273                                    LOG.debug(uri + " converted to endpoint: " + answer + " by component: " + component);
274                                }
275                            }
276                        }
277                        if (answer == null) {
278                            answer = createEndpoint(uri);
279                        }
280    
281                        // If it's a singleton then auto register it.
282                        if (answer != null) {
283                            addService(answer);
284    
285                            if (answer.isSingleton()) {
286                                endpoints.put(uri, answer);
287    
288                                // TODO we should support non-singletons in the lifecycle
289                                lifecycleStrategy.onEndpointAdd(answer);
290                            }
291                        }
292                    } catch (Exception e) {
293                        LOG.debug("Failed to resolve endpoint " + uri + ". Reason: " + e, e);
294                        throw new ResolveEndpointFailedException(uri, e);
295                    }
296                }
297            }
298            return answer;
299        }
300    
301    
302        public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
303            Endpoint endpoint = getEndpoint(name);
304            if (endpointType.isInstance(endpoint)) {
305                return endpointType.cast(endpoint);
306            } else {
307                throw new IllegalArgumentException("The endpoint is not of type: " + endpointType + " but is: "
308                        + endpoint);
309            }
310        }
311    
312        // Route Management Methods
313        // -----------------------------------------------------------------------
314        public List<Route> getRoutes() {
315            if (routes == null) {
316                routes = new ArrayList<Route>();
317            }
318            return routes;
319        }
320    
321        public void setRoutes(List<Route> routes) {
322            this.routes = routes;
323        }
324    
325        public void addRoutes(Collection<Route> routes) throws Exception {
326            if (this.routes == null) {
327                this.routes = new ArrayList<Route>(routes);
328            } else {
329                this.routes.addAll(routes);
330            }
331            lifecycleStrategy.onRoutesAdd(routes);
332            if (shouldStartRoutes()) {
333                startRoutes(routes);
334            }
335        }
336    
337        public void addRoutes(Routes builder) throws Exception {
338            // lets now add the routes from the builder
339            builder.setContext(this);
340            List<Route> routeList = builder.getRouteList();
341            LOG.debug("Adding routes from: " + builder + " routes: " + routeList);
342            addRoutes(routeList);
343        }
344    
345        public void addRouteDefinitions(Collection<RouteType> routeDefinitions) throws Exception {
346            this.routeDefinitions.addAll(routeDefinitions);
347            if (shouldStartRoutes()) {
348                startRouteDefinitions(routeDefinitions);
349            }
350    
351        }
352    
353        /**
354         * Adds a service, starting it so that it will be stopped with this context
355         */
356        public void addService(Object object) throws Exception {
357            if (object instanceof Service) {
358                Service service = (Service) object;
359                service.start();
360                servicesToClose.add(service);
361            }
362        }
363    
364        // Helper methods
365        // -----------------------------------------------------------------------
366    
367        public Language resolveLanguage(String language) {
368            return getLanguageResolver().resolveLanguage(language, this);
369        }
370    
371        // Properties
372        // -----------------------------------------------------------------------
373        public ExchangeConverter getExchangeConverter() {
374            if (exchangeConverter == null) {
375                exchangeConverter = createExchangeConverter();
376            }
377            return exchangeConverter;
378        }
379    
380        public void setExchangeConverter(ExchangeConverter exchangeConverter) {
381            this.exchangeConverter = exchangeConverter;
382        }
383    
384        public TypeConverter getTypeConverter() {
385            if (typeConverter == null) {
386                typeConverter = createTypeConverter();
387            }
388            return typeConverter;
389        }
390    
391        public void setTypeConverter(TypeConverter typeConverter) {
392            this.typeConverter = typeConverter;
393        }
394    
395        public Injector getInjector() {
396            if (injector == null) {
397                injector = createInjector();
398            }
399            return injector;
400        }
401    
402        public void setInjector(Injector injector) {
403            this.injector = injector;
404        }
405    
406        public ComponentResolver getComponentResolver() {
407            if (componentResolver == null) {
408                componentResolver = createComponentResolver();
409            }
410            return componentResolver;
411        }
412    
413        public void setComponentResolver(ComponentResolver componentResolver) {
414            this.componentResolver = componentResolver;
415        }
416    
417        public LanguageResolver getLanguageResolver() {
418            return languageResolver;
419        }
420    
421        public void setLanguageResolver(LanguageResolver languageResolver) {
422            this.languageResolver = languageResolver;
423        }
424    
425        public boolean isAutoCreateComponents() {
426            return autoCreateComponents;
427        }
428    
429        public void setAutoCreateComponents(boolean autoCreateComponents) {
430            this.autoCreateComponents = autoCreateComponents;
431        }
432    
433        public Registry getRegistry() {
434            if (registry == null) {
435                registry = createRegistry();
436            }
437            return registry;
438        }
439    
440        public void setRegistry(Registry registry) {
441            this.registry = registry;
442        }
443    
444        public LifecycleStrategy getLifecycleStrategy() {
445            return lifecycleStrategy;
446        }
447    
448        public void setLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
449            this.lifecycleStrategy = lifecycleStrategy;
450        }
451    
452        public List<RouteType> getRouteDefinitions() {
453            return routeDefinitions;
454        }
455    
456        public List<InterceptStrategy> getInterceptStrategies() {
457            return interceptStrategies;
458        }
459    
460        public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
461            this.interceptStrategies = interceptStrategies;
462        }
463    
464        public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
465            getInterceptStrategies().add(interceptStrategy);
466        }
467    
468        /**
469         * Returns true if tracing has been enabled or disabled via the {@link #setTrace(Boolean)} method
470         * or it has not been specified then default to the <b>camel.trace</b> system property
471         */
472        public boolean getTrace() {
473            final Boolean value = getTracing();
474            if (value != null) {
475                return value;
476            } else {
477                return SystemHelper.isSystemProperty("canel.trace");
478            }
479        }
480    
481        public Boolean getTracing() {
482            return trace;
483        }
484    
485        public void setTrace(Boolean trace) {
486            this.trace = trace;
487        }
488    
489        public <E extends Exchange> ProducerTemplate<E> createProducerTemplate() {
490            return new DefaultProducerTemplate<E>(this);
491        }
492    
493        public ErrorHandlerBuilder getErrorHandlerBuilder() {
494            return errorHandlerBuilder;
495        }
496    
497        /**
498         * Sets the default error handler builder which is inherited by the routes
499         */
500        public void setErrorHandlerBuilder(ErrorHandlerBuilder errorHandlerBuilder) {
501            this.errorHandlerBuilder = errorHandlerBuilder;
502        }
503    
504        // Implementation methods
505        // -----------------------------------------------------------------------
506    
507        protected void doStart() throws Exception {
508            if (getTrace()) {
509                // lets check if we already have already been configured and if not add the default
510                boolean found = false;
511                final List<InterceptStrategy> list = getInterceptStrategies();
512                for (InterceptStrategy strategy : list) {
513                    if (strategy instanceof Tracer) {
514                        found = true;
515                    }
516                }
517                if (!found) {
518                    addInterceptStrategy(new Tracer());
519                }
520            }
521            lifecycleStrategy.onContextStart(this);
522    
523            forceLazyInitialization();
524            if (components != null) {
525                for (Component component : components.values()) {
526                    startServices(component);
527                }
528            }
529            startRouteDefinitions(routeDefinitions);
530            startRoutes(routes);
531        }
532    
533        protected void startRouteDefinitions(Collection<RouteType> list) throws Exception {
534            if (list != null) {
535                Collection<Route> routes = new ArrayList<Route>();
536                for (RouteType route : list) {
537                    route.addRoutes(this, routes);
538                }
539                addRoutes(routes);
540            }
541        }
542    
543        protected void doStop() throws Exception {
544            stopServices(servicesToClose);
545            if (components != null) {
546                for (Component component : components.values()) {
547                    stopServices(component);
548                }
549            }
550        }
551    
552        protected void startRoutes(Collection<Route> routeList) throws Exception {
553            if (routeList != null) {
554                for (Route<Exchange> route : routeList) {
555                    List<Service> services = route.getServicesForRoute();
556                    for (Service service : services) {
557                        addService(service);
558                    }
559                }
560            }
561        }
562    
563        /**
564         * Lets force some lazy initialization to occur upfront before we start any
565         * components and create routes
566         */
567        protected void forceLazyInitialization() {
568            getExchangeConverter();
569            getInjector();
570            getLanguageResolver();
571            getTypeConverter();
572        }
573    
574        /**
575         * Lazily create a default implementation
576         */
577        protected ExchangeConverter createExchangeConverter() {
578            return new DefaultExchangeConverter();
579        }
580    
581        /**
582         * Lazily create a default implementation
583         */
584        protected TypeConverter createTypeConverter() {
585            return new DefaultTypeConverter(getInjector());
586        }
587    
588        /**
589         * Lazily create a default implementation
590         */
591        protected Injector createInjector() {
592            FactoryFinder finder = new FactoryFinder();
593            try {
594                return (Injector) finder.newInstance("Injector");
595            } catch (NoFactoryAvailableException e) {
596                // lets use the default
597                return new ReflectionInjector();
598            } catch (IllegalAccessException e) {
599                throw new RuntimeCamelException(e);
600            } catch (InstantiationException e) {
601                throw new RuntimeCamelException(e);
602            } catch (IOException e) {
603                throw new RuntimeCamelException(e);
604            } catch (ClassNotFoundException e) {
605                throw new RuntimeCamelException(e);
606            }
607        }
608    
609        /**
610         * Lazily create a default implementation
611         */
612        protected ComponentResolver createComponentResolver() {
613            return new DefaultComponentResolver();
614        }
615    
616        /**
617         * Lazily create a default implementation
618         */
619        protected Registry createRegistry() {
620            return new JndiRegistry();
621        }
622    
623        /**
624         * A pluggable strategy to allow an endpoint to be created without requiring
625         * a component to be its factory, such as for looking up the URI inside some
626         * {@link Registry}
627         *
628         * @param uri the uri for the endpoint to be created
629         * @return the newly created endpoint or null if it could not be resolved
630         */
631        protected Endpoint createEndpoint(String uri) {
632            Object value = getRegistry().lookup(uri);
633            if (value instanceof Endpoint) {
634                return (Endpoint) value;
635            } else if (value instanceof Processor) {
636                return new ProcessorEndpoint(uri, this, (Processor) value);
637            } else if (value != null) {
638                return convertBeanToEndpoint(uri, value);
639            }
640            return null;
641        }
642    
643        /**
644         * Attempt to convert the bean from a {@link Registry} to an endpoint using
645         * some kind of transformation or wrapper
646         *
647         * @param uri  the uri for the endpoint (and name in the registry)
648         * @param bean the bean to be converted to an endpoint, which will be not null
649         * @return a new endpoint
650         */
651        protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
652            throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
653                    + " could not be converted to an Endpoint");
654        }
655    
656        /**
657         * Should we start newly added routes?
658         */
659        protected boolean shouldStartRoutes() {
660            return isStarted() && !isStarting();
661        }
662    
663    
664    }