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 */
017package org.apache.camel.impl;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.net.URI;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.Date;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.LinkedHashMap;
030import java.util.LinkedHashSet;
031import java.util.List;
032import java.util.Map;
033import java.util.Properties;
034import java.util.Set;
035import java.util.TreeMap;
036import java.util.concurrent.Callable;
037import java.util.concurrent.CopyOnWriteArrayList;
038import java.util.concurrent.ScheduledExecutorService;
039import java.util.concurrent.TimeUnit;
040import java.util.concurrent.atomic.AtomicInteger;
041
042import javax.naming.Context;
043import javax.xml.bind.JAXBContext;
044import javax.xml.bind.Unmarshaller;
045
046import org.apache.camel.CamelContext;
047import org.apache.camel.CamelContextAware;
048import org.apache.camel.Component;
049import org.apache.camel.Consumer;
050import org.apache.camel.ConsumerTemplate;
051import org.apache.camel.Endpoint;
052import org.apache.camel.ErrorHandlerFactory;
053import org.apache.camel.FailedToStartRouteException;
054import org.apache.camel.IsSingleton;
055import org.apache.camel.MultipleConsumersSupport;
056import org.apache.camel.NamedNode;
057import org.apache.camel.NoFactoryAvailableException;
058import org.apache.camel.NoSuchEndpointException;
059import org.apache.camel.PollingConsumer;
060import org.apache.camel.Processor;
061import org.apache.camel.Producer;
062import org.apache.camel.ProducerTemplate;
063import org.apache.camel.ResolveEndpointFailedException;
064import org.apache.camel.Route;
065import org.apache.camel.RoutesBuilder;
066import org.apache.camel.RuntimeCamelException;
067import org.apache.camel.Service;
068import org.apache.camel.ServiceStatus;
069import org.apache.camel.ShutdownRoute;
070import org.apache.camel.ShutdownRunningTask;
071import org.apache.camel.StartupListener;
072import org.apache.camel.StatefulService;
073import org.apache.camel.SuspendableService;
074import org.apache.camel.TypeConverter;
075import org.apache.camel.VetoCamelContextStartException;
076import org.apache.camel.builder.ErrorHandlerBuilder;
077import org.apache.camel.builder.ErrorHandlerBuilderSupport;
078import org.apache.camel.component.properties.PropertiesComponent;
079import org.apache.camel.impl.converter.BaseTypeConverterRegistry;
080import org.apache.camel.impl.converter.DefaultTypeConverter;
081import org.apache.camel.impl.converter.LazyLoadingTypeConverter;
082import org.apache.camel.management.DefaultManagementMBeanAssembler;
083import org.apache.camel.management.DefaultManagementStrategy;
084import org.apache.camel.management.JmxSystemPropertyKeys;
085import org.apache.camel.management.ManagementStrategyFactory;
086import org.apache.camel.model.DataFormatDefinition;
087import org.apache.camel.model.FromDefinition;
088import org.apache.camel.model.ModelCamelContext;
089import org.apache.camel.model.ProcessorDefinition;
090import org.apache.camel.model.ProcessorDefinitionHelper;
091import org.apache.camel.model.RouteDefinition;
092import org.apache.camel.model.RouteDefinitionHelper;
093import org.apache.camel.model.RoutesDefinition;
094import org.apache.camel.model.rest.RestDefinition;
095import org.apache.camel.processor.interceptor.BacklogDebugger;
096import org.apache.camel.processor.interceptor.BacklogTracer;
097import org.apache.camel.processor.interceptor.Debug;
098import org.apache.camel.processor.interceptor.Delayer;
099import org.apache.camel.processor.interceptor.HandleFault;
100import org.apache.camel.processor.interceptor.StreamCaching;
101import org.apache.camel.processor.interceptor.Tracer;
102import org.apache.camel.spi.AsyncProcessorAwaitManager;
103import org.apache.camel.spi.CamelContextNameStrategy;
104import org.apache.camel.spi.ClassResolver;
105import org.apache.camel.spi.ComponentResolver;
106import org.apache.camel.spi.Container;
107import org.apache.camel.spi.DataFormat;
108import org.apache.camel.spi.DataFormatResolver;
109import org.apache.camel.spi.Debugger;
110import org.apache.camel.spi.EndpointRegistry;
111import org.apache.camel.spi.EndpointStrategy;
112import org.apache.camel.spi.EventNotifier;
113import org.apache.camel.spi.ExecutorServiceManager;
114import org.apache.camel.spi.FactoryFinder;
115import org.apache.camel.spi.FactoryFinderResolver;
116import org.apache.camel.spi.InflightRepository;
117import org.apache.camel.spi.Injector;
118import org.apache.camel.spi.InterceptStrategy;
119import org.apache.camel.spi.Language;
120import org.apache.camel.spi.LanguageResolver;
121import org.apache.camel.spi.LifecycleStrategy;
122import org.apache.camel.spi.ManagementMBeanAssembler;
123import org.apache.camel.spi.ManagementNameStrategy;
124import org.apache.camel.spi.ManagementStrategy;
125import org.apache.camel.spi.ModelJAXBContextFactory;
126import org.apache.camel.spi.NodeIdFactory;
127import org.apache.camel.spi.PackageScanClassResolver;
128import org.apache.camel.spi.ProcessorFactory;
129import org.apache.camel.spi.Registry;
130import org.apache.camel.spi.RestConfiguration;
131import org.apache.camel.spi.RestRegistry;
132import org.apache.camel.spi.RouteContext;
133import org.apache.camel.spi.RoutePolicyFactory;
134import org.apache.camel.spi.RouteStartupOrder;
135import org.apache.camel.spi.RuntimeEndpointRegistry;
136import org.apache.camel.spi.ServicePool;
137import org.apache.camel.spi.ShutdownStrategy;
138import org.apache.camel.spi.StreamCachingStrategy;
139import org.apache.camel.spi.TypeConverterRegistry;
140import org.apache.camel.spi.UnitOfWorkFactory;
141import org.apache.camel.spi.UuidGenerator;
142import org.apache.camel.support.ServiceSupport;
143import org.apache.camel.util.CamelContextHelper;
144import org.apache.camel.util.CollectionStringBuffer;
145import org.apache.camel.util.EndpointHelper;
146import org.apache.camel.util.EventHelper;
147import org.apache.camel.util.IOHelper;
148import org.apache.camel.util.IntrospectionSupport;
149import org.apache.camel.util.JsonSchemaHelper;
150import org.apache.camel.util.LoadPropertiesException;
151import org.apache.camel.util.ObjectHelper;
152import org.apache.camel.util.ServiceHelper;
153import org.apache.camel.util.StopWatch;
154import org.apache.camel.util.StringHelper;
155import org.apache.camel.util.StringQuoteHelper;
156import org.apache.camel.util.TimeUtils;
157import org.apache.camel.util.URISupport;
158import org.slf4j.Logger;
159import org.slf4j.LoggerFactory;
160
161/**
162 * Represents the context used to configure routes and the policies to use.
163 *
164 * @version
165 */
166@SuppressWarnings("deprecation")
167public class DefaultCamelContext extends ServiceSupport implements ModelCamelContext, SuspendableService {
168    private final Logger log = LoggerFactory.getLogger(getClass());
169    private JAXBContext jaxbContext;
170    private CamelContextNameStrategy nameStrategy = new DefaultCamelContextNameStrategy();
171    private ManagementNameStrategy managementNameStrategy = new DefaultManagementNameStrategy(this);
172    private String managementName;
173    private ClassLoader applicationContextClassLoader;
174    private EndpointRegistry<EndpointKey> endpoints;
175    private final AtomicInteger endpointKeyCounter = new AtomicInteger();
176    private final List<EndpointStrategy> endpointStrategies = new ArrayList<EndpointStrategy>();
177    private final Map<String, Component> components = new HashMap<String, Component>();
178    private final Set<Route> routes = new LinkedHashSet<Route>();
179    private final List<Service> servicesToClose = new CopyOnWriteArrayList<Service>();
180    private final Set<StartupListener> startupListeners = new LinkedHashSet<StartupListener>();
181    private TypeConverter typeConverter;
182    private TypeConverterRegistry typeConverterRegistry;
183    private Injector injector;
184    private ComponentResolver componentResolver;
185    private boolean autoCreateComponents = true;
186    private LanguageResolver languageResolver = new DefaultLanguageResolver();
187    private final Map<String, Language> languages = new HashMap<String, Language>();
188    private Registry registry;
189    private List<LifecycleStrategy> lifecycleStrategies = new CopyOnWriteArrayList<LifecycleStrategy>();
190    private ManagementStrategy managementStrategy;
191    private ManagementMBeanAssembler managementMBeanAssembler;
192    private final List<RouteDefinition> routeDefinitions = new ArrayList<RouteDefinition>();
193    private final List<RestDefinition> restDefinitions = new ArrayList<RestDefinition>();
194    private RestConfiguration restConfiguration = new RestConfiguration();
195    private RestRegistry restRegistry = new DefaultRestRegistry();
196    private List<InterceptStrategy> interceptStrategies = new ArrayList<InterceptStrategy>();
197    private List<RoutePolicyFactory> routePolicyFactories = new ArrayList<RoutePolicyFactory>();
198
199    // special flags to control the first startup which can are special
200    private volatile boolean firstStartDone;
201    private volatile boolean doNotStartRoutesOnFirstStart;
202    private final ThreadLocal<Boolean> isStartingRoutes = new ThreadLocal<Boolean>();
203    private final ThreadLocal<Boolean> isSetupRoutes = new ThreadLocal<Boolean>();
204    private Boolean autoStartup = Boolean.TRUE;
205    private Boolean trace = Boolean.FALSE;
206    private Boolean messageHistory = Boolean.TRUE;
207    private Boolean streamCache = Boolean.FALSE;
208    private Boolean handleFault = Boolean.FALSE;
209    private Boolean disableJMX = Boolean.FALSE;
210    private Boolean lazyLoadTypeConverters = Boolean.FALSE;
211    private Boolean typeConverterStatisticsEnabled = Boolean.FALSE;
212    private Boolean useMDCLogging = Boolean.FALSE;
213    private Boolean useBreadcrumb = Boolean.TRUE;
214    private Boolean allowUseOriginalMessage = Boolean.TRUE;
215    private Long delay;
216    private ErrorHandlerFactory errorHandlerBuilder;
217    private final Object errorHandlerExecutorServiceLock = new Object();
218    private ScheduledExecutorService errorHandlerExecutorService;
219    private Map<String, DataFormatDefinition> dataFormats = new HashMap<String, DataFormatDefinition>();
220    private DataFormatResolver dataFormatResolver = new DefaultDataFormatResolver();
221    private Map<String, String> properties = new HashMap<String, String>();
222    private FactoryFinderResolver factoryFinderResolver = new DefaultFactoryFinderResolver();
223    private FactoryFinder defaultFactoryFinder;
224    private PropertiesComponent propertiesComponent;
225    private StreamCachingStrategy streamCachingStrategy;
226    private final Map<String, FactoryFinder> factories = new HashMap<String, FactoryFinder>();
227    private final Map<String, RouteService> routeServices = new LinkedHashMap<String, RouteService>();
228    private final Map<String, RouteService> suspendedRouteServices = new LinkedHashMap<String, RouteService>();
229    private ClassResolver classResolver = new DefaultClassResolver(this);
230    private PackageScanClassResolver packageScanClassResolver;
231    // we use a capacity of 100 per endpoint, so for the same endpoint we have at most 100 producers in the pool
232    // so if we have 6 endpoints in the pool, we can have 6 x 100 producers in total
233    private ServicePool<Endpoint, Producer> producerServicePool = new SharedProducerServicePool(100);
234    private ServicePool<Endpoint, PollingConsumer> pollingConsumerServicePool = new SharedPollingConsumerServicePool(100);
235    private NodeIdFactory nodeIdFactory = new DefaultNodeIdFactory();
236    private ProcessorFactory processorFactory;
237    private InterceptStrategy defaultTracer;
238    private InterceptStrategy defaultBacklogTracer;
239    private InterceptStrategy defaultBacklogDebugger;
240    private InflightRepository inflightRepository = new DefaultInflightRepository();
241    private AsyncProcessorAwaitManager asyncProcessorAwaitManager = new DefaultAsyncProcessorAwaitManager();
242    private RuntimeEndpointRegistry runtimeEndpointRegistry = new DefaultRuntimeEndpointRegistry();
243    private final List<RouteStartupOrder> routeStartupOrder = new ArrayList<RouteStartupOrder>();
244    // start auto assigning route ids using numbering 1000 and upwards
245    private int defaultRouteStartupOrder = 1000;
246    private ShutdownStrategy shutdownStrategy = new DefaultShutdownStrategy(this);
247    private ShutdownRoute shutdownRoute = ShutdownRoute.Default;
248    private ShutdownRunningTask shutdownRunningTask = ShutdownRunningTask.CompleteCurrentTaskOnly;
249    private ExecutorServiceManager executorServiceManager;
250    private Debugger debugger;
251    private UuidGenerator uuidGenerator = createDefaultUuidGenerator();
252    private UnitOfWorkFactory unitOfWorkFactory = new DefaultUnitOfWorkFactory();
253    private final StopWatch stopWatch = new StopWatch(false);
254    private Date startDate;
255    private ModelJAXBContextFactory modelJAXBContextFactory;
256
257    /**
258     * Creates the {@link CamelContext} using {@link JndiRegistry} as registry,
259     * but will silently fallback and use {@link SimpleRegistry} if JNDI cannot be used.
260     * <p/>
261     * Use one of the other constructors to force use an explicit registry / JNDI.
262     */
263    public DefaultCamelContext() {
264        this.executorServiceManager = new DefaultExecutorServiceManager(this);
265
266        // create endpoint registry at first since end users may access endpoints before CamelContext is started
267        this.endpoints = new DefaultEndpointRegistry(this);
268
269        // use WebSphere specific resolver if running on WebSphere
270        if (WebSpherePackageScanClassResolver.isWebSphereClassLoader(this.getClass().getClassLoader())) {
271            log.info("Using WebSphere specific PackageScanClassResolver");
272            packageScanClassResolver = new WebSpherePackageScanClassResolver("META-INF/services/org/apache/camel/TypeConverter");
273        } else {
274            packageScanClassResolver = new DefaultPackageScanClassResolver();
275        }
276
277        // setup management strategy first since end users may use it to add event notifiers
278        // using the management strategy before the CamelContext has been started
279        this.managementStrategy = createManagementStrategy();
280        this.managementMBeanAssembler = createManagementMBeanAssembler();
281
282        // Call all registered trackers with this context
283        // Note, this may use a partially constructed object
284        CamelContextTrackerRegistry.INSTANCE.contextCreated(this);
285
286        // [TODO] Remove in 3.0
287        Container.Instance.manage(this);
288    }
289
290    /**
291     * Creates the {@link CamelContext} using the given JNDI context as the registry
292     *
293     * @param jndiContext the JNDI context
294     */
295    public DefaultCamelContext(Context jndiContext) {
296        this();
297        setJndiContext(jndiContext);
298    }
299
300    /**
301     * Creates the {@link CamelContext} using the given registry
302     *
303     * @param registry the registry
304     */
305    public DefaultCamelContext(Registry registry) {
306        this();
307        setRegistry(registry);
308    }
309
310    public <T extends CamelContext> T adapt(Class<T> type) {
311        return type.cast(this);
312    }
313
314    public String getName() {
315        return getNameStrategy().getName();
316    }
317
318    /**
319     * Sets the name of the this context.
320     *
321     * @param name the name
322     */
323    public void setName(String name) {
324        // use an explicit name strategy since an explicit name was provided to be used
325        this.nameStrategy = new ExplicitCamelContextNameStrategy(name);
326    }
327
328    public CamelContextNameStrategy getNameStrategy() {
329        return nameStrategy;
330    }
331
332    public void setNameStrategy(CamelContextNameStrategy nameStrategy) {
333        this.nameStrategy = nameStrategy;
334    }
335
336    public ManagementNameStrategy getManagementNameStrategy() {
337        return managementNameStrategy;
338    }
339
340    public void setManagementNameStrategy(ManagementNameStrategy managementNameStrategy) {
341        this.managementNameStrategy = managementNameStrategy;
342    }
343
344    public String getManagementName() {
345        return managementName;
346    }
347
348    public void setManagementName(String managementName) {
349        this.managementName = managementName;
350    }
351
352    public Component hasComponent(String componentName) {
353        return components.get(componentName);
354    }
355
356    public void addComponent(String componentName, final Component component) {
357        ObjectHelper.notNull(component, "component");
358        synchronized (components) {
359            if (components.containsKey(componentName)) {
360                throw new IllegalArgumentException("Cannot add component as its already previously added: " + componentName);
361            }
362            component.setCamelContext(this);
363            components.put(componentName, component);
364            for (LifecycleStrategy strategy : lifecycleStrategies) {
365                strategy.onComponentAdd(componentName, component);
366            }
367
368            // keep reference to properties component up to date
369            if (component instanceof PropertiesComponent && "properties".equals(componentName)) {
370                propertiesComponent = (PropertiesComponent) component;
371            }
372        }
373    }
374
375    public Component getComponent(String name) {
376        return getComponent(name, autoCreateComponents);
377    }
378
379    public Component getComponent(String name, boolean autoCreateComponents) {
380        // synchronize the look up and auto create so that 2 threads can't
381        // concurrently auto create the same component.
382        synchronized (components) {
383            Component component = components.get(name);
384            if (component == null && autoCreateComponents) {
385                try {
386                    if (log.isDebugEnabled()) {
387                        log.debug("Using ComponentResolver: {} to resolve component with name: {}", getComponentResolver(), name);
388                    }
389                    component = getComponentResolver().resolveComponent(name, this);
390                    if (component != null) {
391                        addComponent(name, component);
392                        if (isStarted() || isStarting()) {
393                            // If the component is looked up after the context is started, lets start it up.
394                            if (component instanceof Service) {
395                                startService((Service)component);
396                            }
397                        }
398                    }
399                } catch (Exception e) {
400                    throw new RuntimeCamelException("Cannot auto create component: " + name, e);
401                }
402            }
403            log.trace("getComponent({}) -> {}", name, component);
404            return component;
405        }
406    }
407
408    public <T extends Component> T getComponent(String name, Class<T> componentType) {
409        Component component = getComponent(name);
410        if (componentType.isInstance(component)) {
411            return componentType.cast(component);
412        } else {
413            String message;
414            if (component == null) {
415                message = "Did not find component given by the name: " + name;
416            } else {
417                message = "Found component of type: " + component.getClass() + " instead of expected: " + componentType;
418            }
419            throw new IllegalArgumentException(message);
420        }
421    }
422
423    public Component removeComponent(String componentName) {
424        synchronized (components) {
425            Component oldComponent = components.remove(componentName);
426            if (oldComponent != null) {
427                try {
428                    stopServices(oldComponent);
429                } catch (Exception e) {
430                    log.warn("Error stopping component " + oldComponent + ". This exception will be ignored.", e);
431                }
432                for (LifecycleStrategy strategy : lifecycleStrategies) {
433                    strategy.onComponentRemove(componentName, oldComponent);
434                }
435            }
436            // keep reference to properties component up to date
437            if (oldComponent != null && "properties".equals(componentName)) {
438                propertiesComponent = null;
439            }
440            return oldComponent;
441        }
442    }
443
444    // Endpoint Management Methods
445    // -----------------------------------------------------------------------
446
447    public EndpointRegistry getEndpointRegistry() {
448        return endpoints;
449    }
450
451    public Collection<Endpoint> getEndpoints() {
452        return new ArrayList<Endpoint>(endpoints.values());
453    }
454
455    public Map<String, Endpoint> getEndpointMap() {
456        Map<String, Endpoint> answer = new TreeMap<String, Endpoint>();
457        for (Map.Entry<EndpointKey, Endpoint> entry : endpoints.entrySet()) {
458            answer.put(entry.getKey().get(), entry.getValue());
459        }
460        return answer;
461    }
462
463    public Endpoint hasEndpoint(String uri) {
464        return endpoints.get(getEndpointKey(uri));
465    }
466
467    public Endpoint addEndpoint(String uri, Endpoint endpoint) throws Exception {
468        Endpoint oldEndpoint;
469
470        startService(endpoint);
471        oldEndpoint = endpoints.remove(getEndpointKey(uri));
472        for (LifecycleStrategy strategy : lifecycleStrategies) {
473            strategy.onEndpointAdd(endpoint);
474        }
475        addEndpointToRegistry(uri, endpoint);
476        if (oldEndpoint != null) {
477            stopServices(oldEndpoint);
478        }
479
480        return oldEndpoint;
481    }
482
483    public void removeEndpoint(Endpoint endpoint) throws Exception {
484        removeEndpoints(endpoint.getEndpointUri());
485    }
486
487    public Collection<Endpoint> removeEndpoints(String uri) throws Exception {
488        Collection<Endpoint> answer = new ArrayList<Endpoint>();
489        Endpoint oldEndpoint = endpoints.remove(getEndpointKey(uri));
490        if (oldEndpoint != null) {
491            answer.add(oldEndpoint);
492            stopServices(oldEndpoint);
493        } else {
494            for (Map.Entry<EndpointKey, Endpoint> entry : endpoints.entrySet()) {
495                oldEndpoint = entry.getValue();
496                if (EndpointHelper.matchEndpoint(this, oldEndpoint.getEndpointUri(), uri)) {
497                    try {
498                        stopServices(oldEndpoint);
499                    } catch (Exception e) {
500                        log.warn("Error stopping endpoint " + oldEndpoint + ". This exception will be ignored.", e);
501                    }
502                    answer.add(oldEndpoint);
503                    endpoints.remove(entry.getKey());
504                }
505            }
506        }
507
508        // notify lifecycle its being removed
509        for (Endpoint endpoint : answer) {
510            for (LifecycleStrategy strategy : lifecycleStrategies) {
511                strategy.onEndpointRemove(endpoint);
512            }
513        }
514
515        return answer;
516    }
517
518    public Endpoint getEndpoint(String uri) {
519        ObjectHelper.notEmpty(uri, "uri");
520
521        log.trace("Getting endpoint with uri: {}", uri);
522
523        // in case path has property placeholders then try to let property component resolve those
524        try {
525            uri = resolvePropertyPlaceholders(uri);
526        } catch (Exception e) {
527            throw new ResolveEndpointFailedException(uri, e);
528        }
529
530        final String rawUri = uri;
531
532        // normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order
533        uri = normalizeEndpointUri(uri);
534
535        log.trace("Getting endpoint with raw uri: {}, normalized uri: {}", rawUri, uri);
536
537        Endpoint answer;
538        String scheme = null;
539        EndpointKey key = getEndpointKey(uri);
540        answer = endpoints.get(key);
541        if (answer == null) {
542            try {
543                // Use the URI prefix to find the component.
544                String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
545                if (splitURI[1] != null) {
546                    scheme = splitURI[0];
547                    log.trace("Endpoint uri: {} is from component with name: {}", uri, scheme);
548                    Component component = getComponent(scheme);
549
550                    // Ask the component to resolve the endpoint.
551                    if (component != null) {
552                        log.trace("Creating endpoint from uri: {} using component: {}", uri, component);
553
554                        // Have the component create the endpoint if it can.
555                        if (component.useRawUri()) {
556                            answer = component.createEndpoint(rawUri);
557                        } else {
558                            answer = component.createEndpoint(uri);
559                        }
560
561                        if (answer != null && log.isDebugEnabled()) {
562                            log.debug("{} converted to endpoint: {} by component: {}", new Object[]{URISupport.sanitizeUri(uri), answer, component});
563                        }
564                    }
565                }
566
567                if (answer == null) {
568                    // no component then try in registry and elsewhere
569                    answer = createEndpoint(uri);
570                    log.trace("No component to create endpoint from uri: {} fallback lookup in registry -> {}", uri, answer);
571                }
572
573                if (answer != null) {
574                    addService(answer);
575                    answer = addEndpointToRegistry(uri, answer);
576                }
577            } catch (Exception e) {
578                throw new ResolveEndpointFailedException(uri, e);
579            }
580        }
581
582        // unknown scheme
583        if (answer == null && scheme != null) {
584            throw new ResolveEndpointFailedException(uri, "No component found with scheme: " + scheme);
585        }
586
587        return answer;
588    }
589
590    public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
591        Endpoint endpoint = getEndpoint(name);
592        if (endpoint == null) {
593            throw new NoSuchEndpointException(name);
594        }
595        if (endpoint instanceof InterceptSendToEndpoint) {
596            endpoint = ((InterceptSendToEndpoint) endpoint).getDelegate();
597        }
598        if (endpointType.isInstance(endpoint)) {
599            return endpointType.cast(endpoint);
600        } else {
601            throw new IllegalArgumentException("The endpoint is not of type: " + endpointType
602                + " but is: " + endpoint.getClass().getCanonicalName());
603        }
604    }
605
606    public void addRegisterEndpointCallback(EndpointStrategy strategy) {
607        if (!endpointStrategies.contains(strategy)) {
608            // let it be invoked for already registered endpoints so it can catch-up.
609            endpointStrategies.add(strategy);
610            for (Endpoint endpoint : getEndpoints()) {
611                Endpoint newEndpoint = strategy.registerEndpoint(endpoint.getEndpointUri(), endpoint);
612                if (newEndpoint != null) {
613                    // put will replace existing endpoint with the new endpoint
614                    endpoints.put(getEndpointKey(endpoint.getEndpointUri()), newEndpoint);
615                }
616            }
617        }
618    }
619
620    /**
621     * Strategy to add the given endpoint to the internal endpoint registry
622     *
623     * @param uri      uri of the endpoint
624     * @param endpoint the endpoint to add
625     * @return the added endpoint
626     */
627    protected Endpoint addEndpointToRegistry(String uri, Endpoint endpoint) {
628        ObjectHelper.notEmpty(uri, "uri");
629        ObjectHelper.notNull(endpoint, "endpoint");
630
631        // if there is endpoint strategies, then use the endpoints they return
632        // as this allows to intercept endpoints etc.
633        for (EndpointStrategy strategy : endpointStrategies) {
634            endpoint = strategy.registerEndpoint(uri, endpoint);
635        }
636        endpoints.put(getEndpointKey(uri, endpoint), endpoint);
637        return endpoint;
638    }
639
640    /**
641     * Normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order.
642     *
643     * @param uri the uri
644     * @return normalized uri
645     * @throws ResolveEndpointFailedException if uri cannot be normalized
646     */
647    protected static String normalizeEndpointUri(String uri) {
648        try {
649            uri = URISupport.normalizeUri(uri);
650        } catch (Exception e) {
651            throw new ResolveEndpointFailedException(uri, e);
652        }
653        return uri;
654    }
655
656    /**
657     * Gets the endpoint key to use for lookup or whe adding endpoints to the {@link DefaultEndpointRegistry}
658     *
659     * @param uri the endpoint uri
660     * @return the key
661     */
662    protected EndpointKey getEndpointKey(String uri) {
663        return new EndpointKey(uri);
664    }
665
666    /**
667     * Gets the endpoint key to use for lookup or whe adding endpoints to the {@link DefaultEndpointRegistry}
668     *
669     * @param uri      the endpoint uri
670     * @param endpoint the endpoint
671     * @return the key
672     */
673    protected EndpointKey getEndpointKey(String uri, Endpoint endpoint) {
674        if (endpoint != null && !endpoint.isSingleton()) {
675            int counter = endpointKeyCounter.incrementAndGet();
676            return new EndpointKey(uri + ":" + counter);
677        } else {
678            return new EndpointKey(uri);
679        }
680    }
681
682    // Route Management Methods
683    // -----------------------------------------------------------------------
684
685    public List<RouteStartupOrder> getRouteStartupOrder() {
686        return routeStartupOrder;
687    }
688
689    public List<Route> getRoutes() {
690        // lets return a copy of the collection as objects are removed later when services are stopped
691        if (routes.isEmpty()) {
692            return Collections.emptyList();
693        } else {
694            synchronized (routes) {
695                return new ArrayList<Route>(routes);
696            }
697        }
698    }
699
700    public Route getRoute(String id) {
701        for (Route route : getRoutes()) {
702            if (route.getId().equals(id)) {
703                return route;
704            }
705        }
706        return null;
707    }
708
709    @Deprecated
710    public void setRoutes(List<Route> routes) {
711        throw new UnsupportedOperationException("Overriding existing routes is not supported yet, use addRouteCollection instead");
712    }
713
714    void removeRouteCollection(Collection<Route> routes) {
715        synchronized (routes) {
716            this.routes.removeAll(routes);
717        }
718    }
719
720    void addRouteCollection(Collection<Route> routes) throws Exception {
721        synchronized (routes) {
722            this.routes.addAll(routes);
723        }
724    }
725
726    public void addRoutes(final RoutesBuilder builder) throws Exception {
727        log.debug("Adding routes from builder: {}", builder);
728        doWithDefinedClassLoader(new Callable<Void>() {
729            @Override
730            public Void call() throws Exception {
731                builder.addRoutesToCamelContext(DefaultCamelContext.this);
732                return null;
733            }
734        });
735    }
736
737    public synchronized RoutesDefinition loadRoutesDefinition(InputStream is) throws Exception {
738        // load routes using JAXB
739        if (jaxbContext == null) {
740            // must use classloader from CamelContext to have JAXB working
741            jaxbContext = getModelJAXBContextFactory().newJAXBContext();
742        }
743
744        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
745        Object result = unmarshaller.unmarshal(is);
746
747        if (result == null) {
748            throw new IOException("Cannot unmarshal to routes using JAXB from input stream: " + is);
749        }
750
751        // can either be routes or a single route
752        RoutesDefinition answer;
753        if (result instanceof RouteDefinition) {
754            RouteDefinition route = (RouteDefinition) result;
755            answer = new RoutesDefinition();
756            answer.getRoutes().add(route);
757        } else if (result instanceof RoutesDefinition) {
758            answer = (RoutesDefinition) result;
759        } else {
760            throw new IllegalArgumentException("Unmarshalled object is an unsupported type: " + ObjectHelper.className(result) + " -> " + result);
761        }
762
763        return answer;
764    }
765
766    public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
767        if (routeDefinitions == null || routeDefinitions.isEmpty()) {
768            return;
769        }
770        for (RouteDefinition routeDefinition : routeDefinitions) {
771            removeRouteDefinition(routeDefinition);
772        }
773        this.routeDefinitions.addAll(routeDefinitions);
774        if (shouldStartRoutes()) {
775            startRouteDefinitions(routeDefinitions);
776        }
777    }
778
779    public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception {
780        addRouteDefinitions(Arrays.asList(routeDefinition));
781    }
782
783    /**
784     * Removes the route definition with the given key.
785     *
786     * @return true if one or more routes was removed
787     */
788    protected boolean removeRouteDefinition(String key) {
789        boolean answer = false;
790        Iterator<RouteDefinition> iter = routeDefinitions.iterator();
791        while (iter.hasNext()) {
792            RouteDefinition route = iter.next();
793            if (route.idOrCreate(nodeIdFactory).equals(key)) {
794                iter.remove();
795                answer = true;
796            }
797        }
798        return answer;
799    }
800
801    public synchronized void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
802        for (RouteDefinition routeDefinition : routeDefinitions) {
803            removeRouteDefinition(routeDefinition);
804        }
805    }
806
807    public synchronized void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception {
808        String id = routeDefinition.getId();
809        if (id != null) {
810            // remove existing route
811            stopRoute(id);
812            removeRoute(id);
813        }
814        this.routeDefinitions.remove(routeDefinition);
815    }
816
817    public ServiceStatus getRouteStatus(String key) {
818        RouteService routeService = routeServices.get(key);
819        if (routeService != null) {
820            return routeService.getStatus();
821        }
822        return null;
823    }
824
825    public void startRoute(RouteDefinition route) throws Exception {
826        // assign ids to the routes and validate that the id's is all unique
827        RouteDefinitionHelper.forceAssignIds(this, routeDefinitions);
828        String duplicate = RouteDefinitionHelper.validateUniqueIds(route, routeDefinitions);
829        if (duplicate != null) {
830            throw new FailedToStartRouteException(route.getId(), "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes.");
831        }
832
833        // indicate we are staring the route using this thread so
834        // we are able to query this if needed
835        isStartingRoutes.set(true);
836        try {
837            // must ensure route is prepared, before we can start it
838            route.prepare(this);
839
840            List<Route> routes = new ArrayList<Route>();
841            List<RouteContext> routeContexts = route.addRoutes(this, routes);
842            RouteService routeService = new RouteService(this, route, routeContexts, routes);
843            startRouteService(routeService, true);
844        } finally {
845            // we are done staring routes
846            isStartingRoutes.remove();
847        }
848    }
849
850    public boolean isStartingRoutes() {
851        Boolean answer = isStartingRoutes.get();
852        return answer != null && answer;
853    }
854
855    public boolean isSetupRoutes() {
856        Boolean answer = isSetupRoutes.get();
857        return answer != null && answer;
858    }
859
860    public void stopRoute(RouteDefinition route) throws Exception {
861        stopRoute(route.idOrCreate(nodeIdFactory));
862    }
863
864    public void startAllRoutes() throws Exception {
865        doStartOrResumeRoutes(routeServices, true, true, false, false);
866    }
867
868    public synchronized void startRoute(String routeId) throws Exception {
869        RouteService routeService = routeServices.get(routeId);
870        if (routeService != null) {
871            startRouteService(routeService, false);
872        }
873    }
874
875    public synchronized void resumeRoute(String routeId) throws Exception {
876        if (!routeSupportsSuspension(routeId)) {
877            // start route if suspension is not supported
878            startRoute(routeId);
879            return;
880        }
881
882        RouteService routeService = routeServices.get(routeId);
883        if (routeService != null) {
884            resumeRouteService(routeService);
885        }
886    }
887
888    public synchronized boolean stopRoute(String routeId, long timeout, TimeUnit timeUnit, boolean abortAfterTimeout) throws Exception {
889        RouteService routeService = routeServices.get(routeId);
890        if (routeService != null) {
891            RouteStartupOrder route = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
892
893            boolean completed = getShutdownStrategy().shutdown(this, route, timeout, timeUnit, abortAfterTimeout);
894            if (completed) {
895                // must stop route service as well
896                stopRouteService(routeService, false);
897            } else {
898                // shutdown was aborted, make sure route is re-started properly
899                startRouteService(routeService, false);
900            }
901            return completed;
902        }
903        return false;
904    }
905
906    public synchronized void stopRoute(String routeId) throws Exception {
907        RouteService routeService = routeServices.get(routeId);
908        if (routeService != null) {
909            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
910            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
911            routes.add(order);
912
913            getShutdownStrategy().shutdown(this, routes);
914            // must stop route service as well
915            stopRouteService(routeService, false);
916        }
917    }
918
919    public synchronized void stopRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
920        RouteService routeService = routeServices.get(routeId);
921        if (routeService != null) {
922            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
923            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
924            routes.add(order);
925
926            getShutdownStrategy().shutdown(this, routes, timeout, timeUnit);
927            // must stop route service as well
928            stopRouteService(routeService, false);
929        }
930    }
931
932    public synchronized void shutdownRoute(String routeId) throws Exception {
933        RouteService routeService = routeServices.get(routeId);
934        if (routeService != null) {
935            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
936            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
937            routes.add(order);
938
939            getShutdownStrategy().shutdown(this, routes);
940            // must stop route service as well (and remove the routes from management)
941            stopRouteService(routeService, true);
942        }
943    }
944
945    public synchronized void shutdownRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
946        RouteService routeService = routeServices.get(routeId);
947        if (routeService != null) {
948            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
949            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
950            routes.add(order);
951
952            getShutdownStrategy().shutdown(this, routes, timeout, timeUnit);
953            // must stop route service as well (and remove the routes from management)
954            stopRouteService(routeService, true);
955        }
956    }
957
958    public synchronized boolean removeRoute(String routeId) throws Exception {
959        // remove the route from ErrorHandlerBuilder if possible
960        if (getErrorHandlerBuilder() instanceof ErrorHandlerBuilderSupport) {
961            ErrorHandlerBuilderSupport builder = (ErrorHandlerBuilderSupport)getErrorHandlerBuilder();
962            builder.removeOnExceptionList(routeId);
963        }
964
965        // gather a map of all the endpoints in use by the routes, so we can known if a given endpoints is in use
966        // by one or more routes, when we remove the route
967        Map<String, Set<Endpoint>> endpointsInUse = new HashMap<String, Set<Endpoint>>();
968        for (Map.Entry<String, RouteService> entry : routeServices.entrySet()) {
969            endpointsInUse.put(entry.getKey(), entry.getValue().gatherEndpoints());
970        }
971
972        RouteService routeService = routeServices.get(routeId);
973        if (routeService != null) {
974            if (getRouteStatus(routeId).isStopped()) {
975                routeService.setRemovingRoutes(true);
976                shutdownRouteService(routeService);
977                removeRouteDefinition(routeId);
978                routeServices.remove(routeId);
979                // remove route from startup order as well, as it was removed
980                Iterator<RouteStartupOrder> it = routeStartupOrder.iterator();
981                while (it.hasNext()) {
982                    RouteStartupOrder order = it.next();
983                    if (order.getRoute().getId().equals(routeId)) {
984                        it.remove();
985                    }
986                }
987
988                // from the route which we have removed, then remove all its private endpoints
989                // (eg the endpoints which are not in use by other routes)
990                Set<Endpoint> toRemove = new LinkedHashSet<Endpoint>();
991                for (Endpoint endpoint : endpointsInUse.get(routeId)) {
992                    // how many times is the endpoint in use
993                    int count = 0;
994                    for (Set<Endpoint> endpoints : endpointsInUse.values()) {
995                        if (endpoints.contains(endpoint)) {
996                            count++;
997                        }
998                    }
999                    // notice we will count ourselves so if there is only 1 then its safe to remove
1000                    if (count <= 1) {
1001                        toRemove.add(endpoint);
1002                    }
1003                }
1004                for (Endpoint endpoint : toRemove) {
1005                    log.debug("Removing: {} which was only in use by route: {}", endpoint, routeId);
1006                    removeEndpoint(endpoint);
1007                }
1008                return true;
1009            } else {
1010                return false;
1011            }
1012        }
1013        return false;
1014    }
1015
1016    public synchronized void suspendRoute(String routeId) throws Exception {
1017        if (!routeSupportsSuspension(routeId)) {
1018            // stop if we suspend is not supported
1019            stopRoute(routeId);
1020            return;
1021        }
1022
1023        RouteService routeService = routeServices.get(routeId);
1024        if (routeService != null) {
1025            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
1026            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1027            routes.add(order);
1028
1029            getShutdownStrategy().suspend(this, routes);
1030            // must suspend route service as well
1031            suspendRouteService(routeService);
1032        }
1033    }
1034
1035    public synchronized void suspendRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
1036        if (!routeSupportsSuspension(routeId)) {
1037            stopRoute(routeId, timeout, timeUnit);
1038            return;
1039        }
1040
1041        RouteService routeService = routeServices.get(routeId);
1042        if (routeService != null) {
1043            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
1044            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1045            routes.add(order);
1046
1047            getShutdownStrategy().suspend(this, routes, timeout, timeUnit);
1048            // must suspend route service as well
1049            suspendRouteService(routeService);
1050        }
1051    }
1052
1053    public void addService(Object object) throws Exception {
1054        addService(object, true);
1055    }
1056
1057    public void addService(Object object, boolean closeOnShutdown) throws Exception {
1058        doAddService(object, closeOnShutdown);
1059    }
1060
1061    private void doAddService(Object object, boolean closeOnShutdown) throws Exception {
1062        // inject CamelContext
1063        if (object instanceof CamelContextAware) {
1064            CamelContextAware aware = (CamelContextAware) object;
1065            aware.setCamelContext(this);
1066        }
1067
1068        if (object instanceof Service) {
1069            Service service = (Service) object;
1070
1071            for (LifecycleStrategy strategy : lifecycleStrategies) {
1072                if (service instanceof Endpoint) {
1073                    // use specialized endpoint add
1074                    strategy.onEndpointAdd((Endpoint) service);
1075                } else {
1076                    strategy.onServiceAdd(this, service, null);
1077                }
1078            }
1079
1080            // only add to services to close if its a singleton
1081            // otherwise we could for example end up with a lot of prototype scope endpoints
1082            boolean singleton = true; // assume singleton by default
1083            if (service instanceof IsSingleton) {
1084                singleton = ((IsSingleton) service).isSingleton();
1085            }
1086            // do not add endpoints as they have their own list
1087            if (singleton && !(service instanceof Endpoint)) {
1088                // only add to list of services to close if its not already there
1089                if (closeOnShutdown && !hasService(service)) {
1090                    servicesToClose.add(service);
1091                }
1092            }
1093        }
1094
1095        // and then ensure service is started (as stated in the javadoc)
1096        if (object instanceof Service) {
1097            startService((Service)object);
1098        } else if (object instanceof Collection<?>) {
1099            startServices((Collection<?>)object);
1100        }
1101    }
1102
1103    public boolean removeService(Object object) throws Exception {
1104        if (object instanceof Endpoint) {
1105            removeEndpoint((Endpoint) object);
1106            return true;
1107        }
1108        if (object instanceof Service) {
1109            Service service = (Service) object;
1110            for (LifecycleStrategy strategy : lifecycleStrategies) {
1111                strategy.onServiceRemove(this, service, null);
1112            }
1113            return servicesToClose.remove(service);
1114        }
1115        return false;
1116    }
1117
1118    public boolean hasService(Object object) {
1119        if (object instanceof Service) {
1120            Service service = (Service) object;
1121            return servicesToClose.contains(service);
1122        }
1123        return false;
1124    }
1125
1126    @Override
1127    public <T> T hasService(Class<T> type) {
1128        for (Service service : servicesToClose) {
1129            if (type.isInstance(service)) {
1130                return type.cast(service);
1131            }
1132        }
1133        return null;
1134    }
1135
1136    public void addStartupListener(StartupListener listener) throws Exception {
1137        // either add to listener so we can invoke then later when CamelContext has been started
1138        // or invoke the callback right now
1139        if (isStarted()) {
1140            listener.onCamelContextStarted(this, true);
1141        } else {
1142            startupListeners.add(listener);
1143        }
1144    }
1145
1146    public String resolveComponentDefaultName(String javaType) {
1147        // special for some components
1148        // TODO: ActiveMQ 5.11 will include this out of the box, so not needed when its released
1149        if ("org.apache.activemq.camel.component.ActiveMQComponent".equals(javaType)) {
1150            return "jms";
1151        }
1152
1153        // try to find the component by its java type from the in-use components
1154        if (javaType != null) {
1155            // find all the components which will include the default component name
1156            try {
1157                Map<String, Properties> all = CamelContextHelper.findComponents(this);
1158                for (Map.Entry<String, Properties> entry : all.entrySet()) {
1159                    String fqn = (String) entry.getValue().get("class");
1160                    if (javaType.equals(fqn)) {
1161                        // is there component docs for that name?
1162                        String name = entry.getKey();
1163                        String json = getComponentParameterJsonSchema(name);
1164                        if (json != null) {
1165                            return name;
1166                        }
1167                    }
1168                }
1169            } catch (Exception e) {
1170                // ignore
1171                return null;
1172            }
1173        }
1174
1175        // could not find a component with that name
1176        return null;
1177    }
1178
1179    public Map<String, Properties> findComponents() throws LoadPropertiesException, IOException {
1180        return CamelContextHelper.findComponents(this);
1181    }
1182
1183    public Map<String, Properties> findEips() throws LoadPropertiesException, IOException {
1184        return CamelContextHelper.findEips(this);
1185    }
1186
1187    public String getComponentDocumentation(String componentName) throws IOException {
1188        // use the component factory finder to find the package name of the component class, which is the location
1189        // where the documentation exists as well
1190        FactoryFinder finder = getFactoryFinder(DefaultComponentResolver.RESOURCE_PATH);
1191        try {
1192            Class<?> clazz = finder.findClass(componentName);
1193            if (clazz == null) {
1194                // fallback and find existing component
1195                Component existing = hasComponent(componentName);
1196                if (existing != null) {
1197                    clazz = existing.getClass();
1198                } else {
1199                    return null;
1200                }
1201            }
1202
1203            String packageName = clazz.getPackage().getName();
1204            packageName = packageName.replace('.', '/');
1205            String path = packageName + "/" + componentName + ".html";
1206
1207            ClassResolver resolver = getClassResolver();
1208            InputStream inputStream = resolver.loadResourceAsStream(path);
1209            log.debug("Loading component documentation for: {} using class resolver: {} -> {}", new Object[]{componentName, resolver, inputStream});
1210            if (inputStream != null) {
1211                try {
1212                    return IOHelper.loadText(inputStream);
1213                } finally {
1214                    IOHelper.close(inputStream);
1215                }
1216            }
1217            // special for ActiveMQ as it is really just JMS
1218            if ("ActiveMQComponent".equals(clazz.getSimpleName())) {
1219                return getComponentDocumentation("jms");
1220            } else {
1221                return null;
1222            }
1223        } catch (ClassNotFoundException e) {
1224            return null;
1225        }
1226    }
1227
1228    public String getComponentParameterJsonSchema(String componentName) throws IOException {
1229        // use the component factory finder to find the package name of the component class, which is the location
1230        // where the documentation exists as well
1231        FactoryFinder finder = getFactoryFinder(DefaultComponentResolver.RESOURCE_PATH);
1232        try {
1233            Class<?> clazz = finder.findClass(componentName);
1234            if (clazz == null) {
1235                // fallback and find existing component
1236                Component existing = hasComponent(componentName);
1237                if (existing != null) {
1238                    clazz = existing.getClass();
1239                } else {
1240                    return null;
1241                }
1242            }
1243
1244            String packageName = clazz.getPackage().getName();
1245            packageName = packageName.replace('.', '/');
1246            String path = packageName + "/" + componentName + ".json";
1247
1248            ClassResolver resolver = getClassResolver();
1249            InputStream inputStream = resolver.loadResourceAsStream(path);
1250            log.debug("Loading component JSON Schema for: {} using class resolver: {} -> {}", new Object[]{componentName, resolver, inputStream});
1251            if (inputStream != null) {
1252                try {
1253                    return IOHelper.loadText(inputStream);
1254                } finally {
1255                    IOHelper.close(inputStream);
1256                }
1257            }
1258            // special for ActiveMQ as it is really just JMS
1259            if ("ActiveMQComponent".equals(clazz.getSimpleName())) {
1260                return getComponentParameterJsonSchema("jms");
1261            } else {
1262                return null;
1263            }
1264        } catch (ClassNotFoundException e) {
1265            return null;
1266        }
1267    }
1268
1269    public String getDataFormatParameterJsonSchema(String dataFormatName) throws IOException {
1270        // use the dataformat factory finder to find the package name of the dataformat class, which is the location
1271        // where the documentation exists as well
1272        FactoryFinder finder = getFactoryFinder(DefaultDataFormatResolver.DATAFORMAT_RESOURCE_PATH);
1273        try {
1274            Class<?> clazz = finder.findClass(dataFormatName);
1275            if (clazz == null) {
1276                return null;
1277            }
1278
1279            String packageName = clazz.getPackage().getName();
1280            packageName = packageName.replace('.', '/');
1281            String path = packageName + "/" + dataFormatName + ".json";
1282
1283            ClassResolver resolver = getClassResolver();
1284            InputStream inputStream = resolver.loadResourceAsStream(path);
1285            log.debug("Loading dataformat JSON Schema for: {} using class resolver: {} -> {}", new Object[]{dataFormatName, resolver, inputStream});
1286            if (inputStream != null) {
1287                try {
1288                    return IOHelper.loadText(inputStream);
1289                } finally {
1290                    IOHelper.close(inputStream);
1291                }
1292            }
1293            return null;
1294
1295        } catch (ClassNotFoundException e) {
1296            return null;
1297        }
1298    }
1299
1300    public String getLanguageParameterJsonSchema(String languageName) throws IOException {
1301        // use the language factory finder to find the package name of the language class, which is the location
1302        // where the documentation exists as well
1303        FactoryFinder finder = getFactoryFinder(DefaultLanguageResolver.LANGUAGE_RESOURCE_PATH);
1304        try {
1305            Class<?> clazz = finder.findClass(languageName);
1306            if (clazz == null) {
1307                return null;
1308            }
1309
1310            String packageName = clazz.getPackage().getName();
1311            packageName = packageName.replace('.', '/');
1312            String path = packageName + "/" + languageName + ".json";
1313
1314            ClassResolver resolver = getClassResolver();
1315            InputStream inputStream = resolver.loadResourceAsStream(path);
1316            log.debug("Loading language JSON Schema for: {} using class resolver: {} -> {}", new Object[]{languageName, resolver, inputStream});
1317            if (inputStream != null) {
1318                try {
1319                    return IOHelper.loadText(inputStream);
1320                } finally {
1321                    IOHelper.close(inputStream);
1322                }
1323            }
1324            return null;
1325
1326        } catch (ClassNotFoundException e) {
1327            return null;
1328        }
1329    }
1330
1331    public String getEipParameterJsonSchema(String eipName) throws IOException {
1332        // the eip json schema may be in some of the sub-packages so look until we find it
1333        String[] subPackages = new String[]{"", "/config", "/dataformat", "/language", "/loadbalancer", "/rest"};
1334        for (String sub : subPackages) {
1335            String path = CamelContextHelper.MODEL_DOCUMENTATION_PREFIX + sub + "/" + eipName + ".json";
1336            ClassResolver resolver = getClassResolver();
1337            InputStream inputStream = resolver.loadResourceAsStream(path);
1338            if (inputStream != null) {
1339                log.debug("Loading eip JSON Schema for: {} using class resolver: {} -> {}", new Object[]{eipName, resolver, inputStream});
1340                try {
1341                    return IOHelper.loadText(inputStream);
1342                } finally {
1343                    IOHelper.close(inputStream);
1344                }
1345            }
1346        }
1347        return null;
1348    }
1349
1350    public String explainEipJson(String nameOrId, boolean includeAllOptions) {
1351        try {
1352            // try to find the id within all known routes and their eips
1353            String eipName = nameOrId;
1354            NamedNode target = null;
1355            for (RouteDefinition route : getRouteDefinitions()) {
1356                if (route.getId().equals(nameOrId)) {
1357                    target = route;
1358                    break;
1359                }
1360                for (FromDefinition from : route.getInputs()) {
1361                    if (nameOrId.equals(from.getId())) {
1362                        target = route;
1363                        break;
1364                    }
1365                }
1366                Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
1367                while (it.hasNext()) {
1368                    ProcessorDefinition def = it.next();
1369                    if (nameOrId.equals(def.getId())) {
1370                        target = def;
1371                        break;
1372                    }
1373                }
1374                if (target != null) {
1375                    break;
1376                }
1377            }
1378
1379            if (target != null) {
1380                eipName = target.getShortName();
1381            }
1382
1383            String json = getEipParameterJsonSchema(eipName);
1384            if (json == null) {
1385                return null;
1386            }
1387
1388            // overlay with runtime parameters that id uses at runtime
1389            if (target != null) {
1390                List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
1391
1392                // selected rows to use for answer
1393                Map<String, String[]> selected = new LinkedHashMap<String, String[]>();
1394
1395                // extract options from the node
1396                Map<String, Object> options = new LinkedHashMap<String, Object>();
1397                IntrospectionSupport.getProperties(target, options, "", false);
1398                // remove outputs which we do not want to include
1399                options.remove("outputs");
1400
1401                // include other rows
1402                for (Map<String, String> row : rows) {
1403                    String name = row.get("name");
1404                    String kind = row.get("kind");
1405                    String label = row.get("label");
1406                    String required = row.get("required");
1407                    String value = row.get("value");
1408                    String defaultValue = row.get("defaultValue");
1409                    String type = row.get("type");
1410                    String javaType = row.get("javaType");
1411                    String deprecated = row.get("deprecated");
1412                    String description = row.get("description");
1413
1414                    // find the configured option
1415                    Object o = options.get(name);
1416                    if (o != null) {
1417                        value = o.toString();
1418                    }
1419
1420                    value = URISupport.sanitizePath(value);
1421
1422                    if (includeAllOptions || o != null) {
1423                        // add as selected row
1424                        if (!selected.containsKey(name)) {
1425                            selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1426                        }
1427                    }
1428                }
1429
1430                json = ObjectHelper.before(json, "  \"properties\": {");
1431
1432                StringBuilder buffer = new StringBuilder("  \"properties\": {");
1433
1434                boolean first = true;
1435                for (String[] row : selected.values()) {
1436                    if (first) {
1437                        first = false;
1438                    } else {
1439                        buffer.append(",");
1440                    }
1441                    buffer.append("\n    ");
1442
1443                    String name = row[0];
1444                    String kind = row[1];
1445                    String label = row[2];
1446                    String required = row[3];
1447                    String type = row[4];
1448                    String javaType = row[5];
1449                    String deprecated = row[6];
1450                    String value = row[7];
1451                    String defaultValue = row[8];
1452                    String description = row[9];
1453
1454                    // add json of the option
1455                    buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1456                    CollectionStringBuffer csb = new CollectionStringBuffer();
1457                    if (kind != null) {
1458                        csb.append("\"kind\": \"" + kind + "\"");
1459                    }
1460                    if (label != null) {
1461                        csb.append("\"label\": \"" + label + "\"");
1462                    }
1463                    if (required != null) {
1464                        csb.append("\"required\": \"" + required + "\"");
1465                    }
1466                    if (type != null) {
1467                        csb.append("\"type\": \"" + type + "\"");
1468                    }
1469                    if (javaType != null) {
1470                        csb.append("\"javaType\": \"" + javaType + "\"");
1471                    }
1472                    if (deprecated != null) {
1473                        csb.append("\"deprecated\": \"" + deprecated + "\"");
1474                    }
1475                    if (value != null) {
1476                        csb.append("\"value\": \"" + value + "\"");
1477                    }
1478                    if (defaultValue != null) {
1479                        csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1480                    }
1481                    if (description != null) {
1482                        csb.append("\"description\": \"" + description + "\"");
1483                    }
1484                    if (!csb.isEmpty()) {
1485                        buffer.append(csb.toString());
1486                    }
1487                    buffer.append(" }");
1488                }
1489
1490                buffer.append("\n  }\n}\n");
1491
1492                // insert the original first part of the json into the start of the buffer
1493                buffer.insert(0, json);
1494                return buffer.toString();
1495            }
1496
1497            return json;
1498        } catch (Exception e) {
1499            // ignore and return empty response
1500            return null;
1501        }
1502    }
1503
1504    public String explainComponentJson(String componentName, boolean includeAllOptions) {
1505        try {
1506            String json = getComponentParameterJsonSchema(componentName);
1507            if (json == null) {
1508                return null;
1509            }
1510
1511            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("componentProperties", json, true);
1512
1513            // selected rows to use for answer
1514            Map<String, String[]> selected = new LinkedHashMap<String, String[]>();
1515
1516            // insert values from component
1517            Component component = getComponent(componentName);
1518            Map<String, Object> options = new HashMap<String, Object>();
1519            IntrospectionSupport.getProperties(component, options, null);
1520
1521            for (Map.Entry<String, Object> entry : options.entrySet()) {
1522                String name = entry.getKey();
1523
1524                // skip unwanted options which is default inherited from DefaultComponent
1525                if ("camelContext".equals(name) || "endpointClass".equals(name)) {
1526                    continue;
1527                }
1528
1529                String value = "";
1530                if (entry.getValue() != null) {
1531                    value = entry.getValue().toString();
1532                }
1533                value = URISupport.sanitizePath(value);
1534
1535                // find type and description from the json schema
1536                String type = null;
1537                String kind = null;
1538                String label = null;
1539                String required = null;
1540                String javaType = null;
1541                String deprecated = null;
1542                String defaultValue = null;
1543                String description = null;
1544                for (Map<String, String> row : rows) {
1545                    if (name.equals(row.get("name"))) {
1546                        type = row.get("type");
1547                        kind = row.get("kind");
1548                        label = row.get("label");
1549                        required = row.get("required");
1550                        javaType = row.get("javaType");
1551                        deprecated = row.get("deprecated");
1552                        defaultValue = row.get("defaultValue");
1553                        description = row.get("description");
1554                        break;
1555                    }
1556                }
1557
1558                // add as selected row
1559                selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1560            }
1561
1562            // include other rows
1563            for (Map<String, String> row : rows) {
1564                String name = row.get("name");
1565                String kind = row.get("kind");
1566                String label = row.get("label");
1567                String required = row.get("required");
1568                String value = row.get("value");
1569                String defaultValue = row.get("defaultValue");
1570                String type = row.get("type");
1571                String javaType = row.get("javaType");
1572                String deprecated = row.get("deprecated");
1573                value = URISupport.sanitizePath(value);
1574                String description = row.get("description");
1575
1576                // always include path options
1577                if (includeAllOptions) {
1578                    // add as selected row
1579                    if (!selected.containsKey(name)) {
1580                        selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1581                    }
1582                }
1583            }
1584
1585            json = ObjectHelper.before(json, "  \"componentProperties\": {");
1586
1587            StringBuilder buffer = new StringBuilder("  \"componentProperties\": {");
1588
1589            boolean first = true;
1590            for (String[] row : selected.values()) {
1591                if (first) {
1592                    first = false;
1593                } else {
1594                    buffer.append(",");
1595                }
1596                buffer.append("\n    ");
1597
1598                String name = row[0];
1599                String kind = row[1];
1600                String label = row[2];
1601                String required = row[3];
1602                String type = row[4];
1603                String javaType = row[5];
1604                String deprecated = row[6];
1605                String value = row[7];
1606                String defaultValue = row[8];
1607                String description = row[9];
1608
1609                // add json of the option
1610                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1611                CollectionStringBuffer csb = new CollectionStringBuffer();
1612                if (kind != null) {
1613                    csb.append("\"kind\": \"" + kind + "\"");
1614                }
1615                if (label != null) {
1616                    csb.append("\"label\": \"" + label + "\"");
1617                }
1618                if (required != null) {
1619                    csb.append("\"required\": \"" + required + "\"");
1620                }
1621                if (type != null) {
1622                    csb.append("\"type\": \"" + type + "\"");
1623                }
1624                if (javaType != null) {
1625                    csb.append("\"javaType\": \"" + javaType + "\"");
1626                }
1627                if (deprecated != null) {
1628                    csb.append("\"deprecated\": \"" + deprecated + "\"");
1629                }
1630                if (value != null) {
1631                    csb.append("\"value\": \"" + value + "\"");
1632                }
1633                if (defaultValue != null) {
1634                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1635                }
1636                if (description != null) {
1637                    csb.append("\"description\": \"" + description + "\"");
1638                }
1639                if (!csb.isEmpty()) {
1640                    buffer.append(csb.toString());
1641                }
1642                buffer.append(" }");
1643            }
1644
1645            buffer.append("\n  }\n}\n");
1646
1647            // insert the original first part of the json into the start of the buffer
1648            buffer.insert(0, json);
1649            return buffer.toString();
1650
1651        } catch (Exception e) {
1652            // ignore and return empty response
1653            return null;
1654        }
1655    }
1656
1657    public String explainEndpointJson(String uri, boolean includeAllOptions) {
1658        try {
1659            URI u = new URI(uri);
1660
1661            String json = getComponentParameterJsonSchema(u.getScheme());
1662            if (json == null) {
1663                return null;
1664            }
1665
1666            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
1667
1668            // selected rows to use for answer
1669            Map<String, String[]> selected = new LinkedHashMap<String, String[]>();
1670
1671            // insert values from uri
1672            Map<String, Object> options = URISupport.parseParameters(u);
1673
1674            // extract consumer. prefix options
1675            Map<String, Object> consumerOptions = IntrospectionSupport.extractProperties(options, "consumer.");
1676            // and add back again without the consumer. prefix as that json schema omits that
1677            options.putAll(consumerOptions);
1678
1679            for (Map.Entry<String, Object> entry : options.entrySet()) {
1680                String name = entry.getKey();
1681                String value = "";
1682                if (entry.getValue() != null) {
1683                    value = entry.getValue().toString();
1684                }
1685                value = URISupport.sanitizePath(value);
1686
1687                // find type and description from the json schema
1688                String type = null;
1689                String kind = null;
1690                String label = null;
1691                String required = null;
1692                String javaType = null;
1693                String deprecated = null;
1694                String defaultValue = null;
1695                String description = null;
1696                for (Map<String, String> row : rows) {
1697                    if (name.equals(row.get("name"))) {
1698                        type = row.get("type");
1699                        kind = row.get("kind");
1700                        label = row.get("label");
1701                        required = row.get("required");
1702                        javaType = row.get("javaType");
1703                        deprecated = row.get("deprecated");
1704                        defaultValue = row.get("defaultValue");
1705                        description = row.get("description");
1706                        break;
1707                    }
1708                }
1709
1710                // add as selected row
1711                selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1712            }
1713
1714            // include other rows
1715            for (Map<String, String> row : rows) {
1716                String name = row.get("name");
1717                String kind = row.get("kind");
1718                String label = row.get("label");
1719                String required = row.get("required");
1720                String value = row.get("value");
1721                String defaultValue = row.get("defaultValue");
1722                String type = row.get("type");
1723                String javaType = row.get("javaType");
1724                String deprecated = row.get("deprecated");
1725                value = URISupport.sanitizePath(value);
1726                String description = row.get("description");
1727
1728                if ("path".equals(kind)) {
1729                    // if its the path option then we need to grab the actual value from the uri, which is the remainder path
1730                    value = URISupport.extractRemainderPath(u, false);
1731                    value = URISupport.sanitizePath(value);
1732                }
1733
1734                // always include path options
1735                if (includeAllOptions || "path".equals(kind)) {
1736                    // add as selected row
1737                    if (!selected.containsKey(name)) {
1738                        selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1739                    }
1740                }
1741            }
1742
1743            json = ObjectHelper.before(json, "  \"properties\": {");
1744
1745            StringBuilder buffer = new StringBuilder("  \"properties\": {");
1746
1747            boolean first = true;
1748            for (String[] row : selected.values()) {
1749                if (first) {
1750                    first = false;
1751                } else {
1752                    buffer.append(",");
1753                }
1754                buffer.append("\n    ");
1755
1756                String name = row[0];
1757                String kind = row[1];
1758                String label = row[2];
1759                String required = row[3];
1760                String type = row[4];
1761                String javaType = row[5];
1762                String deprecated = row[6];
1763                String value = row[7];
1764                String defaultValue = row[8];
1765                String description = row[9];
1766
1767                // add json of the option
1768                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1769                CollectionStringBuffer csb = new CollectionStringBuffer();
1770                if (kind != null) {
1771                    csb.append("\"kind\": \"" + kind + "\"");
1772                }
1773                if (label != null) {
1774                    csb.append("\"label\": \"" + label + "\"");
1775                }
1776                if (required != null) {
1777                    csb.append("\"required\": \"" + required + "\"");
1778                }
1779                if (type != null) {
1780                    csb.append("\"type\": \"" + type + "\"");
1781                }
1782                if (javaType != null) {
1783                    csb.append("\"javaType\": \"" + javaType + "\"");
1784                }
1785                if (deprecated != null) {
1786                    csb.append("\"deprecated\": \"" + deprecated + "\"");
1787                }
1788                if (value != null) {
1789                    csb.append("\"value\": \"" + value + "\"");
1790                }
1791                if (defaultValue != null) {
1792                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1793                }
1794                if (description != null) {
1795                    csb.append("\"description\": \"" + description + "\"");
1796                }
1797                if (!csb.isEmpty()) {
1798                    buffer.append(csb.toString());
1799                }
1800                buffer.append(" }");
1801            }
1802
1803            buffer.append("\n  }\n}\n");
1804
1805            // insert the original first part of the json into the start of the buffer
1806            buffer.insert(0, json);
1807            return buffer.toString();
1808
1809        } catch (Exception e) {
1810            // ignore and return empty response
1811            return null;
1812        }
1813    }
1814
1815    public String createRouteStaticEndpointJson(String routeId) {
1816        // lets include dynamic as well as we want as much data as possible
1817        return createRouteStaticEndpointJson(routeId, true);
1818    }
1819
1820    public String createRouteStaticEndpointJson(String routeId, boolean includeDynamic) {
1821        List<RouteDefinition> routes = new ArrayList<RouteDefinition>();
1822        if (routeId != null) {
1823            RouteDefinition route = getRouteDefinition(routeId);
1824            if (route == null) {
1825                throw new IllegalArgumentException("Route with id " + routeId + " does not exist");
1826            }
1827            routes.add(route);
1828        } else {
1829            routes.addAll(getRouteDefinitions());
1830        }
1831
1832        StringBuilder buffer = new StringBuilder("{\n  \"routes\": {");
1833        boolean firstRoute = true;
1834        for (RouteDefinition route : routes) {
1835            if (!firstRoute) {
1836                buffer.append("\n    },");
1837            } else {
1838                firstRoute = false;
1839            }
1840
1841            String id = route.getId();
1842            buffer.append("\n    \"").append(id).append("\": {");
1843            buffer.append("\n      \"inputs\": [");
1844            // for inputs we do not need to check dynamic as we have the data from the route definition
1845            Set<String> inputs = RouteDefinitionHelper.gatherAllStaticEndpointUris(this, route, true, false);
1846            boolean first = true;
1847            for (String input : inputs) {
1848                if (!first) {
1849                    buffer.append(",");
1850                } else {
1851                    first = false;
1852                }
1853                buffer.append("\n        ");
1854                buffer.append(StringHelper.toJson("uri", input, true));
1855            }
1856            buffer.append("\n      ]");
1857
1858            buffer.append(",");
1859            buffer.append("\n      \"outputs\": [");
1860            Set<String> outputs = RouteDefinitionHelper.gatherAllEndpointUris(this, route, false, true, includeDynamic);
1861            first = true;
1862            for (String output : outputs) {
1863                if (!first) {
1864                    buffer.append(",");
1865                } else {
1866                    first = false;
1867                }
1868                buffer.append("\n        ");
1869                buffer.append(StringHelper.toJson("uri", output, true));
1870            }
1871            buffer.append("\n      ]");
1872        }
1873        if (!firstRoute) {
1874            buffer.append("\n    }");
1875        }
1876        buffer.append("\n  }\n}\n");
1877
1878        return buffer.toString();
1879    }
1880
1881    // Helper methods
1882    // -----------------------------------------------------------------------
1883
1884    public Language resolveLanguage(String language) {
1885        Language answer;
1886        synchronized (languages) {
1887            answer = languages.get(language);
1888
1889            // check if the language is singleton, if so return the shared instance
1890            if (answer instanceof IsSingleton) {
1891                boolean singleton = ((IsSingleton) answer).isSingleton();
1892                if (singleton) {
1893                    return answer;
1894                }
1895            }
1896
1897            // language not known or not singleton, then use resolver
1898            answer = getLanguageResolver().resolveLanguage(language, this);
1899
1900            // inject CamelContext if aware
1901            if (answer != null) {
1902                if (answer instanceof CamelContextAware) {
1903                    ((CamelContextAware) answer).setCamelContext(this);
1904                }
1905                if (answer instanceof Service) {
1906                    try {
1907                        startService((Service) answer);
1908                    } catch (Exception e) {
1909                        throw ObjectHelper.wrapRuntimeCamelException(e);
1910                    }
1911                }
1912
1913                languages.put(language, answer);
1914            }
1915        }
1916
1917        return answer;
1918    }
1919
1920    public String getPropertyPrefixToken() {
1921        PropertiesComponent pc = getPropertiesComponent();
1922
1923        if (pc != null) {
1924            return pc.getPrefixToken();
1925        } else {
1926            return null;
1927        }
1928    }
1929
1930    public String getPropertySuffixToken() {
1931        PropertiesComponent pc = getPropertiesComponent();
1932
1933        if (pc != null) {
1934            return pc.getSuffixToken();
1935        } else {
1936            return null;
1937        }
1938    }
1939
1940    public String resolvePropertyPlaceholders(String text) throws Exception {
1941        // While it is more efficient to only do the lookup if we are sure we need the component,
1942        // with custom tokens, we cannot know if the URI contains a property or not without having
1943        // the component.  We also lose fail-fast behavior for the missing component with this change.
1944        PropertiesComponent pc = getPropertiesComponent();
1945
1946        // Do not parse uris that are designated for the properties component as it will handle that itself
1947        if (text != null && !text.startsWith("properties:")) {
1948            // No component, assume default tokens.
1949            if (pc == null && text.contains(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
1950                // lookup existing properties component, or force create a new default component
1951                pc = (PropertiesComponent) CamelContextHelper.lookupPropertiesComponent(this, true);
1952            }
1953
1954            if (pc != null && text.contains(pc.getPrefixToken())) {
1955                // the parser will throw exception if property key was not found
1956                String answer = pc.parseUri(text);
1957                log.debug("Resolved text: {} -> {}", text, answer);
1958                return answer;
1959            }
1960        }
1961
1962        // return original text as is
1963        return text;
1964    }
1965
1966    // Properties
1967    // -----------------------------------------------------------------------
1968
1969    public TypeConverter getTypeConverter() {
1970        if (typeConverter == null) {
1971            synchronized (this) {
1972                // we can synchronize on this as there is only one instance
1973                // of the camel context (its the container)
1974                typeConverter = createTypeConverter();
1975                try {
1976                    // must add service eager
1977                    addService(typeConverter);
1978                } catch (Exception e) {
1979                    throw ObjectHelper.wrapRuntimeCamelException(e);
1980                }
1981            }
1982        }
1983        return typeConverter;
1984    }
1985
1986    public void setTypeConverter(TypeConverter typeConverter) {
1987        this.typeConverter = typeConverter;
1988        try {
1989            // must add service eager
1990            addService(typeConverter);
1991        } catch (Exception e) {
1992            throw ObjectHelper.wrapRuntimeCamelException(e);
1993        }
1994    }
1995
1996    public TypeConverterRegistry getTypeConverterRegistry() {
1997        if (typeConverterRegistry == null) {
1998            // init type converter as its lazy
1999            if (typeConverter == null) {
2000                getTypeConverter();
2001            }
2002            if (typeConverter instanceof TypeConverterRegistry) {
2003                typeConverterRegistry = (TypeConverterRegistry) typeConverter;
2004            }
2005        }
2006        return typeConverterRegistry;
2007    }
2008
2009    public void setTypeConverterRegistry(TypeConverterRegistry typeConverterRegistry) {
2010        this.typeConverterRegistry = typeConverterRegistry;
2011    }
2012
2013    public Injector getInjector() {
2014        if (injector == null) {
2015            injector = createInjector();
2016        }
2017        return injector;
2018    }
2019
2020    public void setInjector(Injector injector) {
2021        this.injector = injector;
2022    }
2023
2024    public ManagementMBeanAssembler getManagementMBeanAssembler() {
2025        return managementMBeanAssembler;
2026    }
2027
2028    public void setManagementMBeanAssembler(ManagementMBeanAssembler managementMBeanAssembler) {
2029        this.managementMBeanAssembler = managementMBeanAssembler;
2030    }
2031
2032    public ComponentResolver getComponentResolver() {
2033        if (componentResolver == null) {
2034            componentResolver = createComponentResolver();
2035        }
2036        return componentResolver;
2037    }
2038
2039    public void setComponentResolver(ComponentResolver componentResolver) {
2040        this.componentResolver = componentResolver;
2041    }
2042
2043    public LanguageResolver getLanguageResolver() {
2044        if (languageResolver == null) {
2045            languageResolver = new DefaultLanguageResolver();
2046        }
2047        return languageResolver;
2048    }
2049
2050    public void setLanguageResolver(LanguageResolver languageResolver) {
2051        this.languageResolver = languageResolver;
2052    }
2053
2054    public boolean isAutoCreateComponents() {
2055        return autoCreateComponents;
2056    }
2057
2058    public void setAutoCreateComponents(boolean autoCreateComponents) {
2059        this.autoCreateComponents = autoCreateComponents;
2060    }
2061
2062    public Registry getRegistry() {
2063        if (registry == null) {
2064            registry = createRegistry();
2065            setRegistry(registry);
2066        }
2067        return registry;
2068    }
2069
2070    public <T> T getRegistry(Class<T> type) {
2071        Registry reg = getRegistry();
2072
2073        // unwrap the property placeholder delegate
2074        if (reg instanceof PropertyPlaceholderDelegateRegistry) {
2075            reg = ((PropertyPlaceholderDelegateRegistry) reg).getRegistry();
2076        }
2077
2078        if (type.isAssignableFrom(reg.getClass())) {
2079            return type.cast(reg);
2080        } else if (reg instanceof CompositeRegistry) {
2081            List<Registry> list = ((CompositeRegistry) reg).getRegistryList();
2082            for (Registry r : list) {
2083                if (type.isAssignableFrom(r.getClass())) {
2084                    return type.cast(r);
2085                }
2086            }
2087        }
2088        return null;
2089    }
2090
2091    /**
2092     * Sets the registry to the given JNDI context
2093     *
2094     * @param jndiContext is the JNDI context to use as the registry
2095     * @see #setRegistry(org.apache.camel.spi.Registry)
2096     */
2097    public void setJndiContext(Context jndiContext) {
2098        setRegistry(new JndiRegistry(jndiContext));
2099    }
2100
2101    public void setRegistry(Registry registry) {
2102        // wrap the registry so we always do property placeholder lookups
2103        if (!(registry instanceof PropertyPlaceholderDelegateRegistry)) {
2104            registry = new PropertyPlaceholderDelegateRegistry(this, registry);
2105        }
2106        this.registry = registry;
2107    }
2108
2109    public List<LifecycleStrategy> getLifecycleStrategies() {
2110        return lifecycleStrategies;
2111    }
2112
2113    public void setLifecycleStrategies(List<LifecycleStrategy> lifecycleStrategies) {
2114        this.lifecycleStrategies = lifecycleStrategies;
2115    }
2116
2117    public void addLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
2118        this.lifecycleStrategies.add(lifecycleStrategy);
2119    }
2120
2121    public void setupRoutes(boolean done) {
2122        if (done) {
2123            isSetupRoutes.remove();
2124        } else {
2125            isSetupRoutes.set(true);
2126        }
2127    }
2128
2129    public synchronized List<RouteDefinition> getRouteDefinitions() {
2130        return routeDefinitions;
2131    }
2132
2133    public synchronized RouteDefinition getRouteDefinition(String id) {
2134        for (RouteDefinition route : routeDefinitions) {
2135            if (route.idOrCreate(nodeIdFactory).equals(id)) {
2136                return route;
2137            }
2138        }
2139        return null;
2140    }
2141
2142    public synchronized List<RestDefinition> getRestDefinitions() {
2143        return restDefinitions;
2144    }
2145
2146    public void addRestDefinitions(Collection<RestDefinition> restDefinitions) throws Exception {
2147        if (restDefinitions == null || restDefinitions.isEmpty()) {
2148            return;
2149        }
2150
2151        this.restDefinitions.addAll(restDefinitions);
2152    }
2153
2154    public RestConfiguration getRestConfiguration() {
2155        return restConfiguration;
2156    }
2157
2158    public void setRestConfiguration(RestConfiguration restConfiguration) {
2159        this.restConfiguration = restConfiguration;
2160    }
2161
2162    public List<InterceptStrategy> getInterceptStrategies() {
2163        return interceptStrategies;
2164    }
2165
2166    public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
2167        this.interceptStrategies = interceptStrategies;
2168    }
2169
2170    public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
2171        getInterceptStrategies().add(interceptStrategy);
2172
2173        // for backwards compatible or if user add them here instead of the setXXX methods
2174
2175        if (interceptStrategy instanceof Tracer) {
2176            setTracing(true);
2177        } else if (interceptStrategy instanceof HandleFault) {
2178            setHandleFault(true);
2179        } else if (interceptStrategy instanceof StreamCaching) {
2180            setStreamCaching(true);
2181        } else if (interceptStrategy instanceof Delayer) {
2182            setDelayer(((Delayer)interceptStrategy).getDelay());
2183        }
2184    }
2185
2186    public List<RoutePolicyFactory> getRoutePolicyFactories() {
2187        return routePolicyFactories;
2188    }
2189
2190    public void setRoutePolicyFactories(List<RoutePolicyFactory> routePolicyFactories) {
2191        this.routePolicyFactories = routePolicyFactories;
2192    }
2193
2194    public void addRoutePolicyFactory(RoutePolicyFactory routePolicyFactory) {
2195        getRoutePolicyFactories().add(routePolicyFactory);
2196    }
2197
2198    public void setStreamCaching(Boolean cache) {
2199        this.streamCache = cache;
2200    }
2201
2202    public Boolean isStreamCaching() {
2203        return streamCache;
2204    }
2205
2206    public void setTracing(Boolean tracing) {
2207        this.trace = tracing;
2208    }
2209
2210    public Boolean isTracing() {
2211        return trace;
2212    }
2213
2214    public Boolean isMessageHistory() {
2215        return messageHistory;
2216    }
2217
2218    public void setMessageHistory(Boolean messageHistory) {
2219        this.messageHistory = messageHistory;
2220    }
2221
2222    public Boolean isHandleFault() {
2223        return handleFault;
2224    }
2225
2226    public void setHandleFault(Boolean handleFault) {
2227        this.handleFault = handleFault;
2228    }
2229
2230    public Long getDelayer() {
2231        return delay;
2232    }
2233
2234    public void setDelayer(Long delay) {
2235        this.delay = delay;
2236    }
2237
2238    public ProducerTemplate createProducerTemplate() {
2239        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2240        return createProducerTemplate(size);
2241    }
2242
2243    public ProducerTemplate createProducerTemplate(int maximumCacheSize) {
2244        DefaultProducerTemplate answer = new DefaultProducerTemplate(this);
2245        answer.setMaximumCacheSize(maximumCacheSize);
2246        // start it so its ready to use
2247        try {
2248            startService(answer);
2249        } catch (Exception e) {
2250            throw ObjectHelper.wrapRuntimeCamelException(e);
2251        }
2252        return answer;
2253    }
2254
2255    public ConsumerTemplate createConsumerTemplate() {
2256        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2257        return createConsumerTemplate(size);
2258    }
2259
2260    public ConsumerTemplate createConsumerTemplate(int maximumCacheSize) {
2261        DefaultConsumerTemplate answer = new DefaultConsumerTemplate(this);
2262        answer.setMaximumCacheSize(maximumCacheSize);
2263        // start it so its ready to use
2264        try {
2265            startService(answer);
2266        } catch (Exception e) {
2267            throw ObjectHelper.wrapRuntimeCamelException(e);
2268        }
2269        return answer;
2270    }
2271
2272    public ErrorHandlerBuilder getErrorHandlerBuilder() {
2273        return (ErrorHandlerBuilder)errorHandlerBuilder;
2274    }
2275
2276    public void setErrorHandlerBuilder(ErrorHandlerFactory errorHandlerBuilder) {
2277        this.errorHandlerBuilder = errorHandlerBuilder;
2278    }
2279
2280    public ScheduledExecutorService getErrorHandlerExecutorService() {
2281        synchronized (errorHandlerExecutorServiceLock) {
2282            if (errorHandlerExecutorService == null) {
2283                // setup default thread pool for error handler
2284                errorHandlerExecutorService = getExecutorServiceManager().newDefaultScheduledThreadPool("ErrorHandlerRedeliveryThreadPool", "ErrorHandlerRedeliveryTask");
2285            }
2286        }
2287        return errorHandlerExecutorService;
2288    }
2289
2290    public void setProducerServicePool(ServicePool<Endpoint, Producer> producerServicePool) {
2291        this.producerServicePool = producerServicePool;
2292    }
2293
2294    public ServicePool<Endpoint, Producer> getProducerServicePool() {
2295        return producerServicePool;
2296    }
2297
2298    public ServicePool<Endpoint, PollingConsumer> getPollingConsumerServicePool() {
2299        return pollingConsumerServicePool;
2300    }
2301
2302    public void setPollingConsumerServicePool(ServicePool<Endpoint, PollingConsumer> pollingConsumerServicePool) {
2303        this.pollingConsumerServicePool = pollingConsumerServicePool;
2304    }
2305
2306    public UnitOfWorkFactory getUnitOfWorkFactory() {
2307        return unitOfWorkFactory;
2308    }
2309
2310    public void setUnitOfWorkFactory(UnitOfWorkFactory unitOfWorkFactory) {
2311        this.unitOfWorkFactory = unitOfWorkFactory;
2312    }
2313
2314    public RuntimeEndpointRegistry getRuntimeEndpointRegistry() {
2315        return runtimeEndpointRegistry;
2316    }
2317
2318    public void setRuntimeEndpointRegistry(RuntimeEndpointRegistry runtimeEndpointRegistry) {
2319        this.runtimeEndpointRegistry = runtimeEndpointRegistry;
2320    }
2321
2322    public String getUptime() {
2323        // compute and log uptime
2324        if (startDate == null) {
2325            return "not started";
2326        }
2327        long delta = new Date().getTime() - startDate.getTime();
2328        return TimeUtils.printDuration(delta);
2329    }
2330
2331    @Override
2332    protected void doSuspend() throws Exception {
2333        EventHelper.notifyCamelContextSuspending(this);
2334
2335        log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is suspending");
2336        StopWatch watch = new StopWatch();
2337
2338        // update list of started routes to be suspended
2339        // because we only want to suspend started routes
2340        // (so when we resume we only resume the routes which actually was suspended)
2341        for (Map.Entry<String, RouteService> entry : getRouteServices().entrySet()) {
2342            if (entry.getValue().getStatus().isStarted()) {
2343                suspendedRouteServices.put(entry.getKey(), entry.getValue());
2344            }
2345        }
2346
2347        // assemble list of startup ordering so routes can be shutdown accordingly
2348        List<RouteStartupOrder> orders = new ArrayList<RouteStartupOrder>();
2349        for (Map.Entry<String, RouteService> entry : suspendedRouteServices.entrySet()) {
2350            Route route = entry.getValue().getRoutes().iterator().next();
2351            Integer order = entry.getValue().getRouteDefinition().getStartupOrder();
2352            if (order == null) {
2353                order = defaultRouteStartupOrder++;
2354            }
2355            orders.add(new DefaultRouteStartupOrder(order, route, entry.getValue()));
2356        }
2357
2358        // suspend routes using the shutdown strategy so it can shutdown in correct order
2359        // routes which doesn't support suspension will be stopped instead
2360        getShutdownStrategy().suspend(this, orders);
2361
2362        // mark the route services as suspended or stopped
2363        for (RouteService service : suspendedRouteServices.values()) {
2364            if (routeSupportsSuspension(service.getId())) {
2365                service.suspend();
2366            } else {
2367                service.stop();
2368            }
2369        }
2370
2371        watch.stop();
2372        if (log.isInfoEnabled()) {
2373            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is suspended in " + TimeUtils.printDuration(watch.taken()));
2374        }
2375
2376        EventHelper.notifyCamelContextSuspended(this);
2377    }
2378
2379    @Override
2380    protected void doResume() throws Exception {
2381        try {
2382            EventHelper.notifyCamelContextResuming(this);
2383
2384            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is resuming");
2385            StopWatch watch = new StopWatch();
2386
2387            // start the suspended routes (do not check for route clashes, and indicate)
2388            doStartOrResumeRoutes(suspendedRouteServices, false, true, true, false);
2389
2390            // mark the route services as resumed (will be marked as started) as well
2391            for (RouteService service : suspendedRouteServices.values()) {
2392                if (routeSupportsSuspension(service.getId())) {
2393                    service.resume();
2394                } else {
2395                    service.start();
2396                }
2397            }
2398
2399            watch.stop();
2400            if (log.isInfoEnabled()) {
2401                log.info("Resumed " + suspendedRouteServices.size() + " routes");
2402                log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") resumed in " + TimeUtils.printDuration(watch.taken()));
2403            }
2404
2405            // and clear the list as they have been resumed
2406            suspendedRouteServices.clear();
2407
2408            EventHelper.notifyCamelContextResumed(this);
2409        } catch (Exception e) {
2410            EventHelper.notifyCamelContextResumeFailed(this, e);
2411            throw e;
2412        }
2413    }
2414
2415    public void start() throws Exception {
2416        startDate = new Date();
2417        stopWatch.restart();
2418        log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is starting");
2419
2420        doNotStartRoutesOnFirstStart = !firstStartDone && !isAutoStartup();
2421
2422        // if the context was configured with auto startup = false, and we are already started,
2423        // then we may need to start the routes on the 2nd start call
2424        if (firstStartDone && !isAutoStartup() && isStarted()) {
2425            // invoke this logic to warm up the routes and if possible also start the routes
2426            doStartOrResumeRoutes(routeServices, true, true, false, true);
2427        }
2428
2429        // super will invoke doStart which will prepare internal services and start routes etc.
2430        try {
2431            firstStartDone = true;
2432            super.start();
2433        } catch (VetoCamelContextStartException e) {
2434            if (e.isRethrowException()) {
2435                throw e;
2436            } else {
2437                log.info("CamelContext ({}) vetoed to not start due {}", getName(), e.getMessage());
2438                // swallow exception and change state of this camel context to stopped
2439                stop();
2440                return;
2441            }
2442        }
2443
2444        stopWatch.stop();
2445        if (log.isInfoEnabled()) {
2446            // count how many routes are actually started
2447            int started = 0;
2448            for (Route route : getRoutes()) {
2449                if (getRouteStatus(route.getId()).isStarted()) {
2450                    started++;
2451                }
2452            }
2453            log.info("Total " + getRoutes().size() + " routes, of which " + started + " is started.");
2454            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") started in " + TimeUtils.printDuration(stopWatch.taken()));
2455        }
2456        EventHelper.notifyCamelContextStarted(this);
2457    }
2458
2459    // Implementation methods
2460    // -----------------------------------------------------------------------
2461
2462    protected synchronized void doStart() throws Exception {
2463        doWithDefinedClassLoader(new Callable<Void>() {
2464            @Override
2465            public Void call() throws Exception {
2466                try {
2467                    doStartCamel();
2468                    return null;
2469                } catch (Exception e) {
2470                    // fire event that we failed to start
2471                    EventHelper.notifyCamelContextStartupFailed(DefaultCamelContext.this, e);
2472                    // rethrow cause
2473                    throw e;
2474                }
2475            }
2476        });
2477    }
2478
2479    private <T> T doWithDefinedClassLoader(Callable<T> callable) throws Exception {
2480        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
2481        try {
2482            // Using the ApplicationClassLoader as the default for TCCL
2483            if (applicationContextClassLoader != null) {
2484                Thread.currentThread().setContextClassLoader(applicationContextClassLoader);
2485            }
2486            return callable.call();
2487        } finally {
2488            Thread.currentThread().setContextClassLoader(tccl);
2489        }
2490    }
2491
2492    private void doStartCamel() throws Exception {
2493
2494        if (classResolver instanceof CamelContextAware) {
2495            ((CamelContextAware) classResolver).setCamelContext(this);
2496        }
2497
2498        if (log.isDebugEnabled()) {
2499            log.debug("Using ClassResolver={}, PackageScanClassResolver={}, ApplicationContextClassLoader={}",
2500                    new Object[]{getClassResolver(), getPackageScanClassResolver(), getApplicationContextClassLoader()});
2501        }
2502
2503        if (isStreamCaching()) {
2504            log.info("StreamCaching is enabled on CamelContext: {}", getName());
2505        }
2506
2507        if (isTracing()) {
2508            // tracing is added in the DefaultChannel so we can enable it on the fly
2509            log.info("Tracing is enabled on CamelContext: {}", getName());
2510        }
2511
2512        if (isUseMDCLogging()) {
2513            // log if MDC has been enabled
2514            log.info("MDC logging is enabled on CamelContext: {}", getName());
2515        }
2516
2517        if (isHandleFault()) {
2518            // only add a new handle fault if not already configured
2519            if (HandleFault.getHandleFault(this) == null) {
2520                log.info("HandleFault is enabled on CamelContext: {}", getName());
2521                addInterceptStrategy(new HandleFault());
2522            }
2523        }
2524
2525        if (getDelayer() != null && getDelayer() > 0) {
2526            log.info("Delayer is enabled with: {} ms. on CamelContext: {}", getDelayer(), getName());
2527        }
2528
2529        // register debugger
2530        if (getDebugger() != null) {
2531            log.info("Debugger: {} is enabled on CamelContext: {}", getDebugger(), getName());
2532            // register this camel context on the debugger
2533            getDebugger().setCamelContext(this);
2534            startService(getDebugger());
2535            addInterceptStrategy(new Debug(getDebugger()));
2536        }
2537
2538        // start management strategy before lifecycles are started
2539        ManagementStrategy managementStrategy = getManagementStrategy();
2540        // inject CamelContext if aware
2541        if (managementStrategy instanceof CamelContextAware) {
2542            ((CamelContextAware) managementStrategy).setCamelContext(this);
2543        }
2544        ServiceHelper.startService(managementStrategy);
2545
2546        // start lifecycle strategies
2547        ServiceHelper.startServices(lifecycleStrategies);
2548        Iterator<LifecycleStrategy> it = lifecycleStrategies.iterator();
2549        while (it.hasNext()) {
2550            LifecycleStrategy strategy = it.next();
2551            try {
2552                strategy.onContextStart(this);
2553            } catch (VetoCamelContextStartException e) {
2554                // okay we should not start Camel since it was vetoed
2555                log.warn("Lifecycle strategy vetoed starting CamelContext ({}) due {}", getName(), e.getMessage());
2556                throw e;
2557            } catch (Exception e) {
2558                log.warn("Lifecycle strategy " + strategy + " failed starting CamelContext ({}) due {}", getName(), e.getMessage());
2559                throw e;
2560            }
2561        }
2562
2563        // start notifiers as services
2564        for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
2565            if (notifier instanceof Service) {
2566                Service service = (Service) notifier;
2567                for (LifecycleStrategy strategy : lifecycleStrategies) {
2568                    strategy.onServiceAdd(this, service, null);
2569                }
2570            }
2571            if (notifier instanceof Service) {
2572                startService((Service)notifier);
2573            }
2574        }
2575
2576        // must let some bootstrap service be started before we can notify the starting event
2577        EventHelper.notifyCamelContextStarting(this);
2578
2579        forceLazyInitialization();
2580
2581        // re-create endpoint registry as the cache size limit may be set after the constructor of this instance was called.
2582        // and we needed to create endpoints up-front as it may be accessed before this context is started
2583        endpoints = new DefaultEndpointRegistry(this, endpoints);
2584        addService(endpoints);
2585        // special for executorServiceManager as want to stop it manually
2586        doAddService(executorServiceManager, false);
2587        addService(producerServicePool);
2588        addService(inflightRepository);
2589        addService(asyncProcessorAwaitManager);
2590        addService(shutdownStrategy);
2591        addService(packageScanClassResolver);
2592        addService(restRegistry);
2593
2594        if (runtimeEndpointRegistry != null) {
2595            if (runtimeEndpointRegistry instanceof EventNotifier) {
2596                getManagementStrategy().addEventNotifier((EventNotifier) runtimeEndpointRegistry);
2597            }
2598            addService(runtimeEndpointRegistry);
2599        }
2600
2601        // eager lookup any configured properties component to avoid subsequent lookup attempts which may impact performance
2602        // due we use properties component for property placeholder resolution at runtime
2603        Component existing = CamelContextHelper.lookupPropertiesComponent(this, false);
2604        if (existing != null) {
2605            // store reference to the existing properties component
2606            if (existing instanceof PropertiesComponent) {
2607                propertiesComponent = (PropertiesComponent) existing;
2608            } else {
2609                // properties component must be expected type
2610                throw new IllegalArgumentException("Found properties component of type: " + existing.getClass() + " instead of expected: " + PropertiesComponent.class);
2611            }
2612        }
2613
2614        // start components
2615        startServices(components.values());
2616
2617        // start the route definitions before the routes is started
2618        startRouteDefinitions(routeDefinitions);
2619
2620        // is there any stream caching enabled then log an info about this and its limit of spooling to disk, so people is aware of this
2621        boolean streamCachingInUse = isStreamCaching();
2622        if (!streamCachingInUse) {
2623            for (RouteDefinition route : routeDefinitions) {
2624                Boolean routeCache = CamelContextHelper.parseBoolean(this, route.getStreamCache());
2625                if (routeCache != null && routeCache) {
2626                    streamCachingInUse = true;
2627                    break;
2628                }
2629            }
2630        }
2631
2632        if (isAllowUseOriginalMessage()) {
2633            log.info("AllowUseOriginalMessage is enabled. If access to the original message is not needed,"
2634                    + " then its recommended to turn this option off as it may improve performance.");
2635        }
2636
2637        if (streamCachingInUse) {
2638            // stream caching is in use so enable the strategy
2639            getStreamCachingStrategy().setEnabled(true);
2640            addService(getStreamCachingStrategy());
2641        } else {
2642            // log if stream caching is not in use as this can help people to enable it if they use streams
2643            log.info("StreamCaching is not in use. If using streams then its recommended to enable stream caching."
2644                    + " See more details at http://camel.apache.org/stream-caching.html");
2645        }
2646
2647        // start routes
2648        if (doNotStartRoutesOnFirstStart) {
2649            log.debug("Skip starting of routes as CamelContext has been configured with autoStartup=false");
2650        }
2651
2652        // invoke this logic to warmup the routes and if possible also start the routes
2653        doStartOrResumeRoutes(routeServices, true, !doNotStartRoutesOnFirstStart, false, true);
2654
2655        // starting will continue in the start method
2656    }
2657
2658    protected synchronized void doStop() throws Exception {
2659        stopWatch.restart();
2660        log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is shutting down");
2661        EventHelper.notifyCamelContextStopping(this);
2662
2663        // stop route inputs in the same order as they was started so we stop the very first inputs first
2664        try {
2665            // force shutting down routes as they may otherwise cause shutdown to hang
2666            shutdownStrategy.shutdownForced(this, getRouteStartupOrder());
2667        } catch (Throwable e) {
2668            log.warn("Error occurred while shutting down routes. This exception will be ignored.", e);
2669        }
2670        getRouteStartupOrder().clear();
2671
2672        // shutdown await manager to trigger interrupt of blocked threads to attempt to free these threads graceful
2673        shutdownServices(asyncProcessorAwaitManager);
2674
2675        shutdownServices(routeServices.values());
2676        // do not clear route services or startup listeners as we can start Camel again and get the route back as before
2677
2678        // but clear any suspend routes
2679        suspendedRouteServices.clear();
2680
2681        // stop consumers from the services to close first, such as POJO consumer (eg @Consumer)
2682        // which we need to stop after the routes, as a POJO consumer is essentially a route also
2683        for (Service service : servicesToClose) {
2684            if (service instanceof Consumer) {
2685                shutdownServices(service);
2686            }
2687        }
2688
2689        // the stop order is important
2690
2691        // shutdown default error handler thread pool
2692        if (errorHandlerExecutorService != null) {
2693            // force shutting down the thread pool
2694            getExecutorServiceManager().shutdownNow(errorHandlerExecutorService);
2695            errorHandlerExecutorService = null;
2696        }
2697
2698        // shutdown debugger
2699        ServiceHelper.stopAndShutdownService(getDebugger());
2700
2701        shutdownServices(endpoints.values());
2702        endpoints.clear();
2703
2704        shutdownServices(components.values());
2705        components.clear();
2706
2707        shutdownServices(languages.values());
2708        languages.clear();
2709
2710        try {
2711            for (LifecycleStrategy strategy : lifecycleStrategies) {
2712                strategy.onContextStop(this);
2713            }
2714        } catch (Throwable e) {
2715            log.warn("Error occurred while stopping lifecycle strategies. This exception will be ignored.", e);
2716        }
2717
2718        // shutdown services as late as possible
2719        shutdownServices(servicesToClose);
2720        servicesToClose.clear();
2721
2722        // must notify that we are stopped before stopping the management strategy
2723        EventHelper.notifyCamelContextStopped(this);
2724
2725        // stop the notifier service
2726        for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
2727            shutdownServices(notifier);
2728        }
2729
2730        // shutdown executor service and management as the last one
2731        shutdownServices(executorServiceManager);
2732        shutdownServices(managementStrategy);
2733        shutdownServices(managementMBeanAssembler);
2734        shutdownServices(lifecycleStrategies);
2735        // do not clear lifecycleStrategies as we can start Camel again and get the route back as before
2736
2737        // stop the lazy created so they can be re-created on restart
2738        forceStopLazyInitialization();
2739
2740        // stop to clear introspection cache
2741        IntrospectionSupport.stop();
2742
2743        stopWatch.stop();
2744        if (log.isInfoEnabled()) {
2745            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") uptime {}", getUptime());
2746            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is shutdown in " + TimeUtils.printDuration(stopWatch.taken()));
2747        }
2748
2749        // and clear start date
2750        startDate = null;
2751
2752        // [TODO] Remove in 3.0
2753        Container.Instance.unmanage(this);
2754    }
2755
2756    /**
2757     * Starts or resumes the routes
2758     *
2759     * @param routeServices  the routes to start (will only start a route if its not already started)
2760     * @param checkClash     whether to check for startup ordering clash
2761     * @param startConsumer  whether the route consumer should be started. Can be used to warmup the route without starting the consumer.
2762     * @param resumeConsumer whether the route consumer should be resumed.
2763     * @param addingRoutes   whether we are adding new routes
2764     * @throws Exception is thrown if error starting routes
2765     */
2766    protected void doStartOrResumeRoutes(Map<String, RouteService> routeServices, boolean checkClash,
2767                                         boolean startConsumer, boolean resumeConsumer, boolean addingRoutes) throws Exception {
2768        isStartingRoutes.set(true);
2769        try {
2770            // filter out already started routes
2771            Map<String, RouteService> filtered = new LinkedHashMap<String, RouteService>();
2772            for (Map.Entry<String, RouteService> entry : routeServices.entrySet()) {
2773                boolean startable = false;
2774
2775                Consumer consumer = entry.getValue().getRoutes().iterator().next().getConsumer();
2776                if (consumer instanceof SuspendableService) {
2777                    // consumer could be suspended, which is not reflected in the RouteService status
2778                    startable = ((SuspendableService) consumer).isSuspended();
2779                }
2780
2781                if (!startable && consumer instanceof StatefulService) {
2782                    // consumer could be stopped, which is not reflected in the RouteService status
2783                    startable = ((StatefulService) consumer).getStatus().isStartable();
2784                } else if (!startable) {
2785                    // no consumer so use state from route service
2786                    startable = entry.getValue().getStatus().isStartable();
2787                }
2788
2789                if (startable) {
2790                    filtered.put(entry.getKey(), entry.getValue());
2791                }
2792            }
2793
2794            if (!filtered.isEmpty()) {
2795                // the context is now considered started (i.e. isStarted() == true))
2796                // starting routes is done after, not during context startup
2797                safelyStartRouteServices(checkClash, startConsumer, resumeConsumer, addingRoutes, filtered.values());
2798            }
2799
2800            // we are finished starting routes, so remove flag before we emit the startup listeners below
2801            isStartingRoutes.remove();
2802
2803            // now notify any startup aware listeners as all the routes etc has been started,
2804            // allowing the listeners to do custom work after routes has been started
2805            for (StartupListener startup : startupListeners) {
2806                startup.onCamelContextStarted(this, isStarted());
2807            }
2808        } finally {
2809            isStartingRoutes.remove();
2810        }
2811    }
2812
2813    protected boolean routeSupportsSuspension(String routeId) {
2814        RouteService routeService = routeServices.get(routeId);
2815        if (routeService != null) {
2816            return routeService.getRoutes().iterator().next().supportsSuspension();
2817        }
2818        return false;
2819    }
2820
2821    private void shutdownServices(Object service) {
2822        // do not rethrow exception as we want to keep shutting down in case of problems
2823
2824        // allow us to do custom work before delegating to service helper
2825        try {
2826            if (service instanceof Service) {
2827                ServiceHelper.stopAndShutdownService(service);
2828            } else if (service instanceof Collection) {
2829                ServiceHelper.stopAndShutdownServices((Collection<?>)service);
2830            }
2831        } catch (Throwable e) {
2832            log.warn("Error occurred while shutting down service: " + service + ". This exception will be ignored.", e);
2833            // fire event
2834            EventHelper.notifyServiceStopFailure(this, service, e);
2835        }
2836    }
2837
2838    private void shutdownServices(Collection<?> services) {
2839        // reverse stopping by default
2840        shutdownServices(services, true);
2841    }
2842
2843    private void shutdownServices(Collection<?> services, boolean reverse) {
2844        Collection<?> list = services;
2845        if (reverse) {
2846            List<Object> reverseList = new ArrayList<Object>(services);
2847            Collections.reverse(reverseList);
2848            list = reverseList;
2849        }
2850
2851        for (Object service : list) {
2852            shutdownServices(service);
2853        }
2854    }
2855
2856    private void startService(Service service) throws Exception {
2857        // and register startup aware so they can be notified when
2858        // camel context has been started
2859        if (service instanceof StartupListener) {
2860            StartupListener listener = (StartupListener) service;
2861            addStartupListener(listener);
2862        }
2863
2864        if (service instanceof CamelContextAware) {
2865            CamelContextAware aware = (CamelContextAware) service;
2866            aware.setCamelContext(this);
2867        }
2868
2869        service.start();
2870    }
2871
2872    private void startServices(Collection<?> services) throws Exception {
2873        for (Object element : services) {
2874            if (element instanceof Service) {
2875                startService((Service)element);
2876            }
2877        }
2878    }
2879
2880    private void stopServices(Object service) throws Exception {
2881        // allow us to do custom work before delegating to service helper
2882        try {
2883            ServiceHelper.stopService(service);
2884        } catch (Exception e) {
2885            // fire event
2886            EventHelper.notifyServiceStopFailure(this, service, e);
2887            // rethrow to signal error with stopping
2888            throw e;
2889        }
2890    }
2891
2892    protected void startRouteDefinitions(Collection<RouteDefinition> list) throws Exception {
2893        if (list != null) {
2894            for (RouteDefinition route : list) {
2895                startRoute(route);
2896            }
2897        }
2898    }
2899
2900    /**
2901     * Starts the given route service
2902     */
2903    protected synchronized void startRouteService(RouteService routeService, boolean addingRoutes) throws Exception {
2904        // we may already be starting routes so remember this, so we can unset accordingly in finally block
2905        boolean alreadyStartingRoutes = isStartingRoutes();
2906        if (!alreadyStartingRoutes) {
2907            isStartingRoutes.set(true);
2908        }
2909
2910        try {
2911            // the route service could have been suspended, and if so then resume it instead
2912            if (routeService.getStatus().isSuspended()) {
2913                resumeRouteService(routeService);
2914            } else {
2915                // start the route service
2916                routeServices.put(routeService.getId(), routeService);
2917                if (shouldStartRoutes()) {
2918                    // this method will log the routes being started
2919                    safelyStartRouteServices(true, true, true, false, addingRoutes, routeService);
2920                    // start route services if it was configured to auto startup and we are not adding routes
2921                    boolean autoStartup = routeService.getRouteDefinition().isAutoStartup(this) && this.isAutoStartup();
2922                    if (!addingRoutes || autoStartup) {
2923                        // start the route since auto start is enabled or we are starting a route (not adding new routes)
2924                        routeService.start();
2925                    }
2926                }
2927            }
2928        } finally {
2929            if (!alreadyStartingRoutes) {
2930                isStartingRoutes.remove();
2931            }
2932        }
2933    }
2934
2935    /**
2936     * Resumes the given route service
2937     */
2938    protected synchronized void resumeRouteService(RouteService routeService) throws Exception {
2939        // the route service could have been stopped, and if so then start it instead
2940        if (!routeService.getStatus().isSuspended()) {
2941            startRouteService(routeService, false);
2942        } else {
2943            // resume the route service
2944            if (shouldStartRoutes()) {
2945                // this method will log the routes being started
2946                safelyStartRouteServices(true, false, true, true, false, routeService);
2947                // must resume route service as well
2948                routeService.resume();
2949            }
2950        }
2951    }
2952
2953    protected synchronized void stopRouteService(RouteService routeService, boolean removingRoutes) throws Exception {
2954        routeService.setRemovingRoutes(removingRoutes);
2955        stopRouteService(routeService);
2956    }
2957
2958    protected void logRouteState(Route route, String state) {
2959        if (log.isInfoEnabled()) {
2960            if (route.getConsumer() != null) {
2961                log.info("Route: {} is {}, was consuming from: {}", new Object[]{route.getId(), state, route.getConsumer().getEndpoint()});
2962            } else {
2963                log.info("Route: {} is {}.", route.getId(), state);
2964            }
2965        }
2966    }
2967
2968    protected synchronized void stopRouteService(RouteService routeService) throws Exception {
2969        routeService.stop();
2970        for (Route route : routeService.getRoutes()) {
2971            logRouteState(route, "stopped");
2972        }
2973    }
2974
2975    protected synchronized void shutdownRouteService(RouteService routeService) throws Exception {
2976        routeService.shutdown();
2977        for (Route route : routeService.getRoutes()) {
2978            logRouteState(route, "shutdown and removed");
2979        }
2980    }
2981
2982    protected synchronized void suspendRouteService(RouteService routeService) throws Exception {
2983        routeService.setRemovingRoutes(false);
2984        routeService.suspend();
2985        for (Route route : routeService.getRoutes()) {
2986            logRouteState(route, "suspended");
2987        }
2988    }
2989
2990    /**
2991     * Starts the routes services in a proper manner which ensures the routes will be started in correct order,
2992     * check for clash and that the routes will also be shutdown in correct order as well.
2993     * <p/>
2994     * This method <b>must</b> be used to start routes in a safe manner.
2995     *
2996     * @param checkClash     whether to check for startup order clash
2997     * @param startConsumer  whether the route consumer should be started. Can be used to warmup the route without starting the consumer.
2998     * @param resumeConsumer whether the route consumer should be resumed.
2999     * @param addingRoutes   whether we are adding new routes
3000     * @param routeServices  the routes
3001     * @throws Exception is thrown if error starting the routes
3002     */
3003    protected synchronized void safelyStartRouteServices(boolean checkClash, boolean startConsumer, boolean resumeConsumer,
3004                                                         boolean addingRoutes, Collection<RouteService> routeServices) throws Exception {
3005        // list of inputs to start when all the routes have been prepared for starting
3006        // we use a tree map so the routes will be ordered according to startup order defined on the route
3007        Map<Integer, DefaultRouteStartupOrder> inputs = new TreeMap<Integer, DefaultRouteStartupOrder>();
3008
3009        // figure out the order in which the routes should be started
3010        for (RouteService routeService : routeServices) {
3011            DefaultRouteStartupOrder order = doPrepareRouteToBeStarted(routeService);
3012            // check for clash before we add it as input
3013            if (checkClash) {
3014                doCheckStartupOrderClash(order, inputs);
3015            }
3016            inputs.put(order.getStartupOrder(), order);
3017        }
3018
3019        // warm up routes before we start them
3020        doWarmUpRoutes(inputs, startConsumer);
3021
3022        if (startConsumer) {
3023            if (resumeConsumer) {
3024                // and now resume the routes
3025                doResumeRouteConsumers(inputs, addingRoutes);
3026            } else {
3027                // and now start the routes
3028                // and check for clash with multiple consumers of the same endpoints which is not allowed
3029                doStartRouteConsumers(inputs, addingRoutes);
3030            }
3031        }
3032
3033        // inputs no longer needed
3034        inputs.clear();
3035    }
3036
3037    /**
3038     * @see #safelyStartRouteServices(boolean,boolean,boolean,boolean,java.util.Collection)
3039     */
3040    protected synchronized void safelyStartRouteServices(boolean forceAutoStart, boolean checkClash, boolean startConsumer,
3041                                                         boolean resumeConsumer, boolean addingRoutes, RouteService... routeServices) throws Exception {
3042        safelyStartRouteServices(checkClash, startConsumer, resumeConsumer, addingRoutes, Arrays.asList(routeServices));
3043    }
3044
3045    private DefaultRouteStartupOrder doPrepareRouteToBeStarted(RouteService routeService) {
3046        // add the inputs from this route service to the list to start afterwards
3047        // should be ordered according to the startup number
3048        Integer startupOrder = routeService.getRouteDefinition().getStartupOrder();
3049        if (startupOrder == null) {
3050            // auto assign a default startup order
3051            startupOrder = defaultRouteStartupOrder++;
3052        }
3053
3054        // create holder object that contains information about this route to be started
3055        Route route = routeService.getRoutes().iterator().next();
3056        return new DefaultRouteStartupOrder(startupOrder, route, routeService);
3057    }
3058
3059    private boolean doCheckStartupOrderClash(DefaultRouteStartupOrder answer, Map<Integer, DefaultRouteStartupOrder> inputs) throws FailedToStartRouteException {
3060        // check for clash by startupOrder id
3061        DefaultRouteStartupOrder other = inputs.get(answer.getStartupOrder());
3062        if (other != null && answer != other) {
3063            String otherId = other.getRoute().getId();
3064            throw new FailedToStartRouteException(answer.getRoute().getId(), "startupOrder clash. Route " + otherId + " already has startupOrder "
3065                + answer.getStartupOrder() + " configured which this route have as well. Please correct startupOrder to be unique among all your routes.");
3066        }
3067        // check in existing already started as well
3068        for (RouteStartupOrder order : routeStartupOrder) {
3069            String otherId = order.getRoute().getId();
3070            if (answer.getRoute().getId().equals(otherId)) {
3071                // its the same route id so skip clash check as its the same route (can happen when using suspend/resume)
3072            } else if (answer.getStartupOrder() == order.getStartupOrder()) {
3073                throw new FailedToStartRouteException(answer.getRoute().getId(), "startupOrder clash. Route " + otherId + " already has startupOrder "
3074                    + answer.getStartupOrder() + " configured which this route have as well. Please correct startupOrder to be unique among all your routes.");
3075            }
3076        }
3077        return true;
3078    }
3079
3080    private void doWarmUpRoutes(Map<Integer, DefaultRouteStartupOrder> inputs, boolean autoStartup) throws Exception {
3081        // now prepare the routes by starting its services before we start the input
3082        for (Map.Entry<Integer, DefaultRouteStartupOrder> entry : inputs.entrySet()) {
3083            // defer starting inputs till later as we want to prepare the routes by starting
3084            // all their processors and child services etc.
3085            // then later we open the floods to Camel by starting the inputs
3086            // what this does is to ensure Camel is more robust on starting routes as all routes
3087            // will then be prepared in time before we start inputs which will consume messages to be routed
3088            RouteService routeService = entry.getValue().getRouteService();
3089            log.debug("Warming up route id: {} having autoStartup={}", routeService.getId(), autoStartup);
3090            routeService.warmUp();
3091        }
3092    }
3093
3094    private void doResumeRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean addingRoutes) throws Exception {
3095        doStartOrResumeRouteConsumers(inputs, true, addingRoutes);
3096    }
3097
3098    private void doStartRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean addingRoutes) throws Exception {
3099        doStartOrResumeRouteConsumers(inputs, false, addingRoutes);
3100    }
3101
3102    private void doStartOrResumeRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean resumeOnly, boolean addingRoute) throws Exception {
3103        List<Endpoint> routeInputs = new ArrayList<Endpoint>();
3104
3105        for (Map.Entry<Integer, DefaultRouteStartupOrder> entry : inputs.entrySet()) {
3106            Integer order = entry.getKey();
3107            Route route = entry.getValue().getRoute();
3108            RouteService routeService = entry.getValue().getRouteService();
3109
3110            // if we are starting camel, then skip routes which are configured to not be auto started
3111            boolean autoStartup = routeService.getRouteDefinition().isAutoStartup(this) && this.isAutoStartup();
3112            if (addingRoute && !autoStartup) {
3113                log.info("Skipping starting of route " + routeService.getId() + " as its configured with autoStartup=false");
3114                continue;
3115            }
3116
3117            // start the service
3118            for (Consumer consumer : routeService.getInputs().values()) {
3119                Endpoint endpoint = consumer.getEndpoint();
3120
3121                // check multiple consumer violation, with the other routes to be started
3122                if (!doCheckMultipleConsumerSupportClash(endpoint, routeInputs)) {
3123                    throw new FailedToStartRouteException(routeService.getId(),
3124                        "Multiple consumers for the same endpoint is not allowed: " + endpoint);
3125                }
3126
3127                // check for multiple consumer violations with existing routes which
3128                // have already been started, or is currently starting
3129                List<Endpoint> existingEndpoints = new ArrayList<Endpoint>();
3130                for (Route existingRoute : getRoutes()) {
3131                    if (route.getId().equals(existingRoute.getId())) {
3132                        // skip ourselves
3133                        continue;
3134                    }
3135                    Endpoint existing = existingRoute.getEndpoint();
3136                    ServiceStatus status = getRouteStatus(existingRoute.getId());
3137                    if (status != null && (status.isStarted() || status.isStarting())) {
3138                        existingEndpoints.add(existing);
3139                    }
3140                }
3141                if (!doCheckMultipleConsumerSupportClash(endpoint, existingEndpoints)) {
3142                    throw new FailedToStartRouteException(routeService.getId(),
3143                            "Multiple consumers for the same endpoint is not allowed: " + endpoint);
3144                }
3145
3146                // start the consumer on the route
3147                log.debug("Route: {} >>> {}", route.getId(), route);
3148                if (resumeOnly) {
3149                    log.debug("Resuming consumer (order: {}) on route: {}", order, route.getId());
3150                } else {
3151                    log.debug("Starting consumer (order: {}) on route: {}", order, route.getId());
3152                }
3153
3154                if (resumeOnly && route.supportsSuspension()) {
3155                    // if we are resuming and the route can be resumed
3156                    ServiceHelper.resumeService(consumer);
3157                    log.info("Route: " + route.getId() + " resumed and consuming from: " + endpoint);
3158                } else {
3159                    // when starting we should invoke the lifecycle strategies
3160                    for (LifecycleStrategy strategy : lifecycleStrategies) {
3161                        strategy.onServiceAdd(this, consumer, route);
3162                    }
3163                    startService(consumer);
3164                    log.info("Route: " + route.getId() + " started and consuming from: " + endpoint);
3165                }
3166
3167                routeInputs.add(endpoint);
3168
3169                // add to the order which they was started, so we know how to stop them in reverse order
3170                // but only add if we haven't already registered it before (we dont want to double add when restarting)
3171                boolean found = false;
3172                for (RouteStartupOrder other : routeStartupOrder) {
3173                    if (other.getRoute().getId().equals(route.getId())) {
3174                        found = true;
3175                        break;
3176                    }
3177                }
3178                if (!found) {
3179                    routeStartupOrder.add(entry.getValue());
3180                }
3181            }
3182
3183            if (resumeOnly) {
3184                routeService.resume();
3185            } else {
3186                // and start the route service (no need to start children as they are already warmed up)
3187                routeService.start(false);
3188            }
3189        }
3190    }
3191
3192    private boolean doCheckMultipleConsumerSupportClash(Endpoint endpoint, List<Endpoint> routeInputs) {
3193        // is multiple consumers supported
3194        boolean multipleConsumersSupported = false;
3195        if (endpoint instanceof MultipleConsumersSupport) {
3196            multipleConsumersSupported = ((MultipleConsumersSupport) endpoint).isMultipleConsumersSupported();
3197        }
3198
3199        if (multipleConsumersSupported) {
3200            // multiple consumer allowed, so return true
3201            return true;
3202        }
3203
3204        // check in progress list
3205        if (routeInputs.contains(endpoint)) {
3206            return false;
3207        }
3208
3209        return true;
3210    }
3211
3212    /**
3213     * Force some lazy initialization to occur upfront before we start any
3214     * components and create routes
3215     */
3216    protected void forceLazyInitialization() {
3217        getRegistry();
3218        getInjector();
3219        getLanguageResolver();
3220        getTypeConverterRegistry();
3221        getTypeConverter();
3222        getRuntimeEndpointRegistry();
3223
3224        if (isTypeConverterStatisticsEnabled() != null) {
3225            getTypeConverterRegistry().getStatistics().setStatisticsEnabled(isTypeConverterStatisticsEnabled());
3226        }
3227    }
3228
3229    /**
3230     * Force clear lazy initialization so they can be re-created on restart
3231     */
3232    protected void forceStopLazyInitialization() {
3233        injector = null;
3234        languageResolver = null;
3235        typeConverterRegistry = null;
3236        typeConverter = null;
3237    }
3238
3239    /**
3240     * Lazily create a default implementation
3241     */
3242    protected TypeConverter createTypeConverter() {
3243        BaseTypeConverterRegistry answer;
3244        if (isLazyLoadTypeConverters()) {
3245            answer = new LazyLoadingTypeConverter(packageScanClassResolver, getInjector(), getDefaultFactoryFinder());
3246        } else {
3247            answer = new DefaultTypeConverter(packageScanClassResolver, getInjector(), getDefaultFactoryFinder());
3248        }
3249        setTypeConverterRegistry(answer);
3250        return answer;
3251    }
3252
3253    /**
3254     * Lazily create a default implementation
3255     */
3256    protected Injector createInjector() {
3257        FactoryFinder finder = getDefaultFactoryFinder();
3258        try {
3259            return (Injector) finder.newInstance("Injector");
3260        } catch (NoFactoryAvailableException e) {
3261            // lets use the default injector
3262            return new DefaultInjector(this);
3263        }
3264    }
3265
3266    /**
3267     * Lazily create a default implementation
3268     */
3269    protected ManagementMBeanAssembler createManagementMBeanAssembler() {
3270        return new DefaultManagementMBeanAssembler(this);
3271    }
3272
3273    /**
3274     * Lazily create a default implementation
3275     */
3276    protected ComponentResolver createComponentResolver() {
3277        return new DefaultComponentResolver();
3278    }
3279
3280    /**
3281     * Lazily create a default implementation
3282     */
3283    protected Registry createRegistry() {
3284        JndiRegistry jndi = new JndiRegistry();
3285        try {
3286            // getContext() will force setting up JNDI
3287            jndi.getContext();
3288            return jndi;
3289        } catch (Throwable e) {
3290            log.debug("Cannot create javax.naming.InitialContext due " + e.getMessage() + ". Will fallback and use SimpleRegistry instead. This exception is ignored.", e);
3291            return new SimpleRegistry();
3292        }
3293    }
3294
3295    /**
3296     * A pluggable strategy to allow an endpoint to be created without requiring
3297     * a component to be its factory, such as for looking up the URI inside some
3298     * {@link Registry}
3299     *
3300     * @param uri the uri for the endpoint to be created
3301     * @return the newly created endpoint or null if it could not be resolved
3302     */
3303    protected Endpoint createEndpoint(String uri) {
3304        Object value = getRegistry().lookupByName(uri);
3305        if (value instanceof Endpoint) {
3306            return (Endpoint) value;
3307        } else if (value instanceof Processor) {
3308            return new ProcessorEndpoint(uri, this, (Processor) value);
3309        } else if (value != null) {
3310            return convertBeanToEndpoint(uri, value);
3311        }
3312        return null;
3313    }
3314
3315    /**
3316     * Strategy method for attempting to convert the bean from a {@link Registry} to an endpoint using
3317     * some kind of transformation or wrapper
3318     *
3319     * @param uri  the uri for the endpoint (and name in the registry)
3320     * @param bean the bean to be converted to an endpoint, which will be not null
3321     * @return a new endpoint
3322     */
3323    protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
3324        throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
3325                + " could not be converted to an Endpoint");
3326    }
3327
3328    /**
3329     * Should we start newly added routes?
3330     */
3331    protected boolean shouldStartRoutes() {
3332        return isStarted() && !isStarting();
3333    }
3334
3335    /**
3336     * Gets the properties component in use.
3337     * Returns {@code null} if no properties component is in use.
3338     */
3339    protected PropertiesComponent getPropertiesComponent() {
3340        return propertiesComponent;
3341    }
3342
3343    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
3344        this.dataFormats = dataFormats;
3345    }
3346
3347    public Map<String, DataFormatDefinition> getDataFormats() {
3348        return dataFormats;
3349    }
3350
3351    public Map<String, String> getProperties() {
3352        return properties;
3353    }
3354
3355    public void setProperties(Map<String, String> properties) {
3356        this.properties = properties;
3357    }
3358
3359    public FactoryFinder getDefaultFactoryFinder() {
3360        if (defaultFactoryFinder == null) {
3361            defaultFactoryFinder = factoryFinderResolver.resolveDefaultFactoryFinder(getClassResolver());
3362        }
3363        return defaultFactoryFinder;
3364    }
3365
3366    public void setFactoryFinderResolver(FactoryFinderResolver resolver) {
3367        this.factoryFinderResolver = resolver;
3368    }
3369
3370    public FactoryFinder getFactoryFinder(String path) throws NoFactoryAvailableException {
3371        synchronized (factories) {
3372            FactoryFinder answer = factories.get(path);
3373            if (answer == null) {
3374                answer = factoryFinderResolver.resolveFactoryFinder(getClassResolver(), path);
3375                factories.put(path, answer);
3376            }
3377            return answer;
3378        }
3379    }
3380
3381    public ClassResolver getClassResolver() {
3382        return classResolver;
3383    }
3384
3385    public void setClassResolver(ClassResolver classResolver) {
3386        this.classResolver = classResolver;
3387    }
3388
3389    public PackageScanClassResolver getPackageScanClassResolver() {
3390        return packageScanClassResolver;
3391    }
3392
3393    public void setPackageScanClassResolver(PackageScanClassResolver packageScanClassResolver) {
3394        this.packageScanClassResolver = packageScanClassResolver;
3395    }
3396
3397    public List<String> getComponentNames() {
3398        synchronized (components) {
3399            List<String> answer = new ArrayList<String>();
3400            for (String name : components.keySet()) {
3401                answer.add(name);
3402            }
3403            return answer;
3404        }
3405    }
3406
3407    public List<String> getLanguageNames() {
3408        synchronized (languages) {
3409            List<String> answer = new ArrayList<String>();
3410            for (String name : languages.keySet()) {
3411                answer.add(name);
3412            }
3413            return answer;
3414        }
3415    }
3416
3417    public ModelJAXBContextFactory getModelJAXBContextFactory() {
3418        if (modelJAXBContextFactory == null) {
3419            modelJAXBContextFactory = new DefaultModelJAXBContextFactory();
3420        }
3421        return modelJAXBContextFactory;
3422    }
3423
3424    public void setModelJAXBContextFactory(final ModelJAXBContextFactory modelJAXBContextFactory) {
3425        this.modelJAXBContextFactory = modelJAXBContextFactory;
3426    }
3427
3428    public NodeIdFactory getNodeIdFactory() {
3429        return nodeIdFactory;
3430    }
3431
3432    public void setNodeIdFactory(NodeIdFactory idFactory) {
3433        this.nodeIdFactory = idFactory;
3434    }
3435
3436    public ManagementStrategy getManagementStrategy() {
3437        return managementStrategy;
3438    }
3439
3440    public void setManagementStrategy(ManagementStrategy managementStrategy) {
3441        this.managementStrategy = managementStrategy;
3442    }
3443
3444    public InterceptStrategy getDefaultTracer() {
3445        if (defaultTracer == null) {
3446            defaultTracer = new Tracer();
3447        }
3448        return defaultTracer;
3449    }
3450
3451    public void setDefaultTracer(InterceptStrategy tracer) {
3452        this.defaultTracer = tracer;
3453    }
3454
3455    public InterceptStrategy getDefaultBacklogTracer() {
3456        if (defaultBacklogTracer == null) {
3457            defaultBacklogTracer = new BacklogTracer(this);
3458        }
3459        return defaultBacklogTracer;
3460    }
3461
3462    public void setDefaultBacklogTracer(InterceptStrategy backlogTracer) {
3463        this.defaultBacklogTracer = backlogTracer;
3464    }
3465
3466    public InterceptStrategy getDefaultBacklogDebugger() {
3467        if (defaultBacklogDebugger == null) {
3468            defaultBacklogDebugger = new BacklogDebugger(this);
3469        }
3470        return defaultBacklogDebugger;
3471    }
3472
3473    public void setDefaultBacklogDebugger(InterceptStrategy defaultBacklogDebugger) {
3474        this.defaultBacklogDebugger = defaultBacklogDebugger;
3475    }
3476
3477    public void disableJMX() {
3478        if (isStarting() || isStarted()) {
3479            throw new IllegalStateException("Disabling JMX can only be done when CamelContext has not been started");
3480        }
3481        managementStrategy = new DefaultManagementStrategy(this);
3482        // must clear lifecycle strategies as we add DefaultManagementLifecycleStrategy by default for JMX support
3483        lifecycleStrategies.clear();
3484    }
3485
3486    public InflightRepository getInflightRepository() {
3487        return inflightRepository;
3488    }
3489
3490    public void setInflightRepository(InflightRepository repository) {
3491        this.inflightRepository = repository;
3492    }
3493
3494    public AsyncProcessorAwaitManager getAsyncProcessorAwaitManager() {
3495        return asyncProcessorAwaitManager;
3496    }
3497
3498    public void setAsyncProcessorAwaitManager(AsyncProcessorAwaitManager asyncProcessorAwaitManager) {
3499        this.asyncProcessorAwaitManager = asyncProcessorAwaitManager;
3500    }
3501
3502    public void setAutoStartup(Boolean autoStartup) {
3503        this.autoStartup = autoStartup;
3504    }
3505
3506    public Boolean isAutoStartup() {
3507        return autoStartup != null && autoStartup;
3508    }
3509
3510    @Deprecated
3511    public Boolean isLazyLoadTypeConverters() {
3512        return lazyLoadTypeConverters != null && lazyLoadTypeConverters;
3513    }
3514
3515    @Deprecated
3516    public void setLazyLoadTypeConverters(Boolean lazyLoadTypeConverters) {
3517        this.lazyLoadTypeConverters = lazyLoadTypeConverters;
3518    }
3519
3520    public Boolean isTypeConverterStatisticsEnabled() {
3521        return typeConverterStatisticsEnabled != null && typeConverterStatisticsEnabled;
3522    }
3523
3524    public void setTypeConverterStatisticsEnabled(Boolean typeConverterStatisticsEnabled) {
3525        this.typeConverterStatisticsEnabled = typeConverterStatisticsEnabled;
3526    }
3527
3528    public Boolean isUseMDCLogging() {
3529        return useMDCLogging != null && useMDCLogging;
3530    }
3531
3532    public void setUseMDCLogging(Boolean useMDCLogging) {
3533        this.useMDCLogging = useMDCLogging;
3534    }
3535
3536    public Boolean isUseBreadcrumb() {
3537        return useBreadcrumb != null && useBreadcrumb;
3538    }
3539
3540    public void setUseBreadcrumb(Boolean useBreadcrumb) {
3541        this.useBreadcrumb = useBreadcrumb;
3542    }
3543
3544    public ClassLoader getApplicationContextClassLoader() {
3545        return applicationContextClassLoader;
3546    }
3547
3548    public void setApplicationContextClassLoader(ClassLoader classLoader) {
3549        applicationContextClassLoader = classLoader;
3550    }
3551
3552    public DataFormatResolver getDataFormatResolver() {
3553        return dataFormatResolver;
3554    }
3555
3556    public void setDataFormatResolver(DataFormatResolver dataFormatResolver) {
3557        this.dataFormatResolver = dataFormatResolver;
3558    }
3559
3560    public DataFormat resolveDataFormat(String name) {
3561        DataFormat answer = dataFormatResolver.resolveDataFormat(name, this);
3562
3563        // inject CamelContext if aware
3564        if (answer != null && answer instanceof CamelContextAware) {
3565            ((CamelContextAware) answer).setCamelContext(this);
3566        }
3567
3568        return answer;
3569    }
3570
3571    public DataFormatDefinition resolveDataFormatDefinition(String name) {
3572        // lookup type and create the data format from it
3573        DataFormatDefinition type = lookup(this, name, DataFormatDefinition.class);
3574        if (type == null && getDataFormats() != null) {
3575            type = getDataFormats().get(name);
3576        }
3577        return type;
3578    }
3579
3580    private static <T> T lookup(CamelContext context, String ref, Class<T> type) {
3581        try {
3582            return context.getRegistry().lookupByNameAndType(ref, type);
3583        } catch (Exception e) {
3584            // need to ignore not same type and return it as null
3585            return null;
3586        }
3587    }
3588
3589    /**
3590     * @deprecated use {@link org.apache.camel.util.CamelContextHelper#lookupPropertiesComponent(org.apache.camel.CamelContext, boolean)}
3591     */
3592    @Deprecated
3593    protected Component lookupPropertiesComponent() {
3594        return CamelContextHelper.lookupPropertiesComponent(this, false);
3595    }
3596
3597    public ShutdownStrategy getShutdownStrategy() {
3598        return shutdownStrategy;
3599    }
3600
3601    public void setShutdownStrategy(ShutdownStrategy shutdownStrategy) {
3602        this.shutdownStrategy = shutdownStrategy;
3603    }
3604
3605    public ShutdownRoute getShutdownRoute() {
3606        return shutdownRoute;
3607    }
3608
3609    public void setShutdownRoute(ShutdownRoute shutdownRoute) {
3610        this.shutdownRoute = shutdownRoute;
3611    }
3612
3613    public ShutdownRunningTask getShutdownRunningTask() {
3614        return shutdownRunningTask;
3615    }
3616
3617    public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
3618        this.shutdownRunningTask = shutdownRunningTask;
3619    }
3620
3621    public void setAllowUseOriginalMessage(Boolean allowUseOriginalMessage) {
3622        this.allowUseOriginalMessage = allowUseOriginalMessage;
3623    }
3624
3625    public Boolean isAllowUseOriginalMessage() {
3626        return allowUseOriginalMessage != null && allowUseOriginalMessage;
3627    }
3628
3629    public ExecutorServiceManager getExecutorServiceManager() {
3630        return this.executorServiceManager;
3631    }
3632
3633    @Deprecated
3634    public org.apache.camel.spi.ExecutorServiceStrategy getExecutorServiceStrategy() {
3635        // its okay to create a new instance as its stateless, and just delegate
3636        // ExecutorServiceManager which is the new API
3637        return new DefaultExecutorServiceStrategy(this);
3638    }
3639
3640    public void setExecutorServiceManager(ExecutorServiceManager executorServiceManager) {
3641        this.executorServiceManager = executorServiceManager;
3642    }
3643
3644    public ProcessorFactory getProcessorFactory() {
3645        return processorFactory;
3646    }
3647
3648    public void setProcessorFactory(ProcessorFactory processorFactory) {
3649        this.processorFactory = processorFactory;
3650    }
3651
3652    public Debugger getDebugger() {
3653        return debugger;
3654    }
3655
3656    public void setDebugger(Debugger debugger) {
3657        this.debugger = debugger;
3658    }
3659
3660    public UuidGenerator getUuidGenerator() {
3661        return uuidGenerator;
3662    }
3663
3664    public void setUuidGenerator(UuidGenerator uuidGenerator) {
3665        this.uuidGenerator = uuidGenerator;
3666    }
3667
3668    public StreamCachingStrategy getStreamCachingStrategy() {
3669        if (streamCachingStrategy == null) {
3670            streamCachingStrategy = new DefaultStreamCachingStrategy();
3671        }
3672        return streamCachingStrategy;
3673    }
3674
3675    public void setStreamCachingStrategy(StreamCachingStrategy streamCachingStrategy) {
3676        this.streamCachingStrategy = streamCachingStrategy;
3677    }
3678
3679    public RestRegistry getRestRegistry() {
3680        return restRegistry;
3681    }
3682
3683    public void setRestRegistry(RestRegistry restRegistry) {
3684        this.restRegistry = restRegistry;
3685    }
3686
3687    @Override
3688    public String getProperty(String name) {
3689        String value = getProperties().get(name);
3690        if (ObjectHelper.isNotEmpty(value)) {
3691            try {
3692                value = resolvePropertyPlaceholders(value);
3693            } catch (Exception e) {
3694                throw new RuntimeCamelException("Error getting property: " + name, e);
3695            }
3696        }
3697        return value;
3698    }
3699
3700    protected Map<String, RouteService> getRouteServices() {
3701        return routeServices;
3702    }
3703
3704    protected ManagementStrategy createManagementStrategy() {
3705        return new ManagementStrategyFactory().create(this, disableJMX || Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED));
3706    }
3707
3708    /**
3709     * Reset context counter to a preset value. Mostly used for tests to ensure a predictable getName()
3710     *
3711     * @param value new value for the context counter
3712     */
3713    public static void setContextCounter(int value) {
3714        DefaultCamelContextNameStrategy.setCounter(value);
3715        DefaultManagementNameStrategy.setCounter(value);
3716    }
3717
3718    private static UuidGenerator createDefaultUuidGenerator() {
3719        if (System.getProperty("com.google.appengine.runtime.environment") != null) {
3720            // either "Production" or "Development"
3721            return new JavaUuidGenerator();
3722        } else {
3723            return new ActiveMQUuidGenerator();
3724        }
3725    }
3726
3727    @Override
3728    public String toString() {
3729        return "CamelContext(" + getName() + ")";
3730    }
3731}