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 (this.routes) {
716            this.routes.removeAll(routes);
717        }
718    }
719
720    void addRouteCollection(Collection<Route> routes) throws Exception {
721        synchronized (this.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            // must resume the route as well
886            Route route = getRoute(routeId);
887            ServiceHelper.resumeService(route);
888        }
889    }
890
891    public synchronized boolean stopRoute(String routeId, long timeout, TimeUnit timeUnit, boolean abortAfterTimeout) throws Exception {
892        RouteService routeService = routeServices.get(routeId);
893        if (routeService != null) {
894            RouteStartupOrder route = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
895
896            boolean completed = getShutdownStrategy().shutdown(this, route, timeout, timeUnit, abortAfterTimeout);
897            if (completed) {
898                // must stop route service as well
899                stopRouteService(routeService, false);
900            } else {
901                // shutdown was aborted, make sure route is re-started properly
902                startRouteService(routeService, false);
903            }
904            return completed;
905        }
906        return false;
907    }
908
909    public synchronized void stopRoute(String routeId) throws Exception {
910        RouteService routeService = routeServices.get(routeId);
911        if (routeService != null) {
912            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
913            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
914            routes.add(order);
915
916            getShutdownStrategy().shutdown(this, routes);
917            // must stop route service as well
918            stopRouteService(routeService, false);
919        }
920    }
921
922    public synchronized void stopRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
923        RouteService routeService = routeServices.get(routeId);
924        if (routeService != null) {
925            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
926            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
927            routes.add(order);
928
929            getShutdownStrategy().shutdown(this, routes, timeout, timeUnit);
930            // must stop route service as well
931            stopRouteService(routeService, false);
932        }
933    }
934
935    public synchronized void shutdownRoute(String routeId) throws Exception {
936        RouteService routeService = routeServices.get(routeId);
937        if (routeService != null) {
938            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
939            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
940            routes.add(order);
941
942            getShutdownStrategy().shutdown(this, routes);
943            // must stop route service as well (and remove the routes from management)
944            stopRouteService(routeService, true);
945        }
946    }
947
948    public synchronized void shutdownRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
949        RouteService routeService = routeServices.get(routeId);
950        if (routeService != null) {
951            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
952            RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
953            routes.add(order);
954
955            getShutdownStrategy().shutdown(this, routes, timeout, timeUnit);
956            // must stop route service as well (and remove the routes from management)
957            stopRouteService(routeService, true);
958        }
959    }
960
961    public synchronized boolean removeRoute(String routeId) throws Exception {
962        // remove the route from ErrorHandlerBuilder if possible
963        if (getErrorHandlerBuilder() instanceof ErrorHandlerBuilderSupport) {
964            ErrorHandlerBuilderSupport builder = (ErrorHandlerBuilderSupport)getErrorHandlerBuilder();
965            builder.removeOnExceptionList(routeId);
966        }
967
968        // gather a map of all the endpoints in use by the routes, so we can known if a given endpoints is in use
969        // by one or more routes, when we remove the route
970        Map<String, Set<Endpoint>> endpointsInUse = new HashMap<String, Set<Endpoint>>();
971        for (Map.Entry<String, RouteService> entry : routeServices.entrySet()) {
972            endpointsInUse.put(entry.getKey(), entry.getValue().gatherEndpoints());
973        }
974
975        RouteService routeService = routeServices.get(routeId);
976        if (routeService != null) {
977            if (getRouteStatus(routeId).isStopped()) {
978                routeService.setRemovingRoutes(true);
979                shutdownRouteService(routeService);
980                removeRouteDefinition(routeId);
981                routeServices.remove(routeId);
982                // remove route from startup order as well, as it was removed
983                Iterator<RouteStartupOrder> it = routeStartupOrder.iterator();
984                while (it.hasNext()) {
985                    RouteStartupOrder order = it.next();
986                    if (order.getRoute().getId().equals(routeId)) {
987                        it.remove();
988                    }
989                }
990
991                // from the route which we have removed, then remove all its private endpoints
992                // (eg the endpoints which are not in use by other routes)
993                Set<Endpoint> toRemove = new LinkedHashSet<Endpoint>();
994                for (Endpoint endpoint : endpointsInUse.get(routeId)) {
995                    // how many times is the endpoint in use
996                    int count = 0;
997                    for (Set<Endpoint> endpoints : endpointsInUse.values()) {
998                        if (endpoints.contains(endpoint)) {
999                            count++;
1000                        }
1001                    }
1002                    // notice we will count ourselves so if there is only 1 then its safe to remove
1003                    if (count <= 1) {
1004                        toRemove.add(endpoint);
1005                    }
1006                }
1007                for (Endpoint endpoint : toRemove) {
1008                    log.debug("Removing: {} which was only in use by route: {}", endpoint, routeId);
1009                    removeEndpoint(endpoint);
1010                }
1011                return true;
1012            } else {
1013                return false;
1014            }
1015        }
1016        return false;
1017    }
1018
1019    public synchronized void suspendRoute(String routeId) throws Exception {
1020        if (!routeSupportsSuspension(routeId)) {
1021            // stop if we suspend is not supported
1022            stopRoute(routeId);
1023            return;
1024        }
1025
1026        RouteService routeService = routeServices.get(routeId);
1027        if (routeService != null) {
1028            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
1029            Route route = routeService.getRoutes().iterator().next();
1030            RouteStartupOrder order = new DefaultRouteStartupOrder(1, route, routeService);
1031            routes.add(order);
1032
1033            getShutdownStrategy().suspend(this, routes);
1034            // must suspend route service as well
1035            suspendRouteService(routeService);
1036            // must suspend the route as well
1037            ServiceHelper.suspendService(route);
1038        }
1039    }
1040
1041    public synchronized void suspendRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
1042        if (!routeSupportsSuspension(routeId)) {
1043            stopRoute(routeId, timeout, timeUnit);
1044            return;
1045        }
1046
1047        RouteService routeService = routeServices.get(routeId);
1048        if (routeService != null) {
1049            List<RouteStartupOrder> routes = new ArrayList<RouteStartupOrder>(1);
1050            Route route = routeService.getRoutes().iterator().next();
1051            RouteStartupOrder order = new DefaultRouteStartupOrder(1, route, routeService);
1052            routes.add(order);
1053
1054            getShutdownStrategy().suspend(this, routes, timeout, timeUnit);
1055            // must suspend route service as well
1056            suspendRouteService(routeService);
1057            // must suspend the route as well
1058            ServiceHelper.suspendService(route);
1059        }
1060    }
1061
1062    public void addService(Object object) throws Exception {
1063        addService(object, true);
1064    }
1065
1066    public void addService(Object object, boolean closeOnShutdown) throws Exception {
1067        doAddService(object, closeOnShutdown);
1068    }
1069
1070    private void doAddService(Object object, boolean closeOnShutdown) throws Exception {
1071        // inject CamelContext
1072        if (object instanceof CamelContextAware) {
1073            CamelContextAware aware = (CamelContextAware) object;
1074            aware.setCamelContext(this);
1075        }
1076
1077        if (object instanceof Service) {
1078            Service service = (Service) object;
1079
1080            for (LifecycleStrategy strategy : lifecycleStrategies) {
1081                if (service instanceof Endpoint) {
1082                    // use specialized endpoint add
1083                    strategy.onEndpointAdd((Endpoint) service);
1084                } else {
1085                    strategy.onServiceAdd(this, service, null);
1086                }
1087            }
1088
1089            // only add to services to close if its a singleton
1090            // otherwise we could for example end up with a lot of prototype scope endpoints
1091            boolean singleton = true; // assume singleton by default
1092            if (service instanceof IsSingleton) {
1093                singleton = ((IsSingleton) service).isSingleton();
1094            }
1095            // do not add endpoints as they have their own list
1096            if (singleton && !(service instanceof Endpoint)) {
1097                // only add to list of services to close if its not already there
1098                if (closeOnShutdown && !hasService(service)) {
1099                    servicesToClose.add(service);
1100                }
1101            }
1102        }
1103
1104        // and then ensure service is started (as stated in the javadoc)
1105        if (object instanceof Service) {
1106            startService((Service)object);
1107        } else if (object instanceof Collection<?>) {
1108            startServices((Collection<?>)object);
1109        }
1110    }
1111
1112    public boolean removeService(Object object) throws Exception {
1113        if (object instanceof Endpoint) {
1114            removeEndpoint((Endpoint) object);
1115            return true;
1116        }
1117        if (object instanceof Service) {
1118            Service service = (Service) object;
1119            for (LifecycleStrategy strategy : lifecycleStrategies) {
1120                strategy.onServiceRemove(this, service, null);
1121            }
1122            return servicesToClose.remove(service);
1123        }
1124        return false;
1125    }
1126
1127    public boolean hasService(Object object) {
1128        if (object instanceof Service) {
1129            Service service = (Service) object;
1130            return servicesToClose.contains(service);
1131        }
1132        return false;
1133    }
1134
1135    @Override
1136    public <T> T hasService(Class<T> type) {
1137        for (Service service : servicesToClose) {
1138            if (type.isInstance(service)) {
1139                return type.cast(service);
1140            }
1141        }
1142        return null;
1143    }
1144
1145    public void addStartupListener(StartupListener listener) throws Exception {
1146        // either add to listener so we can invoke then later when CamelContext has been started
1147        // or invoke the callback right now
1148        if (isStarted()) {
1149            listener.onCamelContextStarted(this, true);
1150        } else {
1151            startupListeners.add(listener);
1152        }
1153    }
1154
1155    public String resolveComponentDefaultName(String javaType) {
1156        // special for some components
1157        // TODO: ActiveMQ 5.11 will include this out of the box, so not needed when its released
1158        if ("org.apache.activemq.camel.component.ActiveMQComponent".equals(javaType)) {
1159            return "jms";
1160        }
1161
1162        // try to find the component by its java type from the in-use components
1163        if (javaType != null) {
1164            // find all the components which will include the default component name
1165            try {
1166                Map<String, Properties> all = CamelContextHelper.findComponents(this);
1167                for (Map.Entry<String, Properties> entry : all.entrySet()) {
1168                    String fqn = (String) entry.getValue().get("class");
1169                    if (javaType.equals(fqn)) {
1170                        // is there component docs for that name?
1171                        String name = entry.getKey();
1172                        String json = getComponentParameterJsonSchema(name);
1173                        if (json != null) {
1174                            return name;
1175                        }
1176                    }
1177                }
1178            } catch (Exception e) {
1179                // ignore
1180                return null;
1181            }
1182        }
1183
1184        // could not find a component with that name
1185        return null;
1186    }
1187
1188    public Map<String, Properties> findComponents() throws LoadPropertiesException, IOException {
1189        return CamelContextHelper.findComponents(this);
1190    }
1191
1192    public Map<String, Properties> findEips() throws LoadPropertiesException, IOException {
1193        return CamelContextHelper.findEips(this);
1194    }
1195
1196    public String getComponentDocumentation(String componentName) throws IOException {
1197        // use the component factory finder to find the package name of the component class, which is the location
1198        // where the documentation exists as well
1199        FactoryFinder finder = getFactoryFinder(DefaultComponentResolver.RESOURCE_PATH);
1200        try {
1201            Class<?> clazz = finder.findClass(componentName);
1202            if (clazz == null) {
1203                // fallback and find existing component
1204                Component existing = hasComponent(componentName);
1205                if (existing != null) {
1206                    clazz = existing.getClass();
1207                } else {
1208                    return null;
1209                }
1210            }
1211
1212            String packageName = clazz.getPackage().getName();
1213            packageName = packageName.replace('.', '/');
1214            String path = packageName + "/" + componentName + ".html";
1215
1216            ClassResolver resolver = getClassResolver();
1217            InputStream inputStream = resolver.loadResourceAsStream(path);
1218            log.debug("Loading component documentation for: {} using class resolver: {} -> {}", new Object[]{componentName, resolver, inputStream});
1219            if (inputStream != null) {
1220                try {
1221                    return IOHelper.loadText(inputStream);
1222                } finally {
1223                    IOHelper.close(inputStream);
1224                }
1225            }
1226            // special for ActiveMQ as it is really just JMS
1227            if ("ActiveMQComponent".equals(clazz.getSimpleName())) {
1228                return getComponentDocumentation("jms");
1229            } else {
1230                return null;
1231            }
1232        } catch (ClassNotFoundException e) {
1233            return null;
1234        }
1235    }
1236
1237    public String getComponentParameterJsonSchema(String componentName) throws IOException {
1238        // use the component factory finder to find the package name of the component class, which is the location
1239        // where the documentation exists as well
1240        FactoryFinder finder = getFactoryFinder(DefaultComponentResolver.RESOURCE_PATH);
1241        try {
1242            Class<?> clazz = finder.findClass(componentName);
1243            if (clazz == null) {
1244                // fallback and find existing component
1245                Component existing = hasComponent(componentName);
1246                if (existing != null) {
1247                    clazz = existing.getClass();
1248                } else {
1249                    return null;
1250                }
1251            }
1252
1253            String packageName = clazz.getPackage().getName();
1254            packageName = packageName.replace('.', '/');
1255            String path = packageName + "/" + componentName + ".json";
1256
1257            ClassResolver resolver = getClassResolver();
1258            InputStream inputStream = resolver.loadResourceAsStream(path);
1259            log.debug("Loading component JSON Schema for: {} using class resolver: {} -> {}", new Object[]{componentName, resolver, inputStream});
1260            if (inputStream != null) {
1261                try {
1262                    return IOHelper.loadText(inputStream);
1263                } finally {
1264                    IOHelper.close(inputStream);
1265                }
1266            }
1267            // special for ActiveMQ as it is really just JMS
1268            if ("ActiveMQComponent".equals(clazz.getSimpleName())) {
1269                return getComponentParameterJsonSchema("jms");
1270            } else {
1271                return null;
1272            }
1273        } catch (ClassNotFoundException e) {
1274            return null;
1275        }
1276    }
1277
1278    public String getDataFormatParameterJsonSchema(String dataFormatName) throws IOException {
1279        // use the dataformat factory finder to find the package name of the dataformat class, which is the location
1280        // where the documentation exists as well
1281        FactoryFinder finder = getFactoryFinder(DefaultDataFormatResolver.DATAFORMAT_RESOURCE_PATH);
1282        try {
1283            Class<?> clazz = finder.findClass(dataFormatName);
1284            if (clazz == null) {
1285                return null;
1286            }
1287
1288            String packageName = clazz.getPackage().getName();
1289            packageName = packageName.replace('.', '/');
1290            String path = packageName + "/" + dataFormatName + ".json";
1291
1292            ClassResolver resolver = getClassResolver();
1293            InputStream inputStream = resolver.loadResourceAsStream(path);
1294            log.debug("Loading dataformat JSON Schema for: {} using class resolver: {} -> {}", new Object[]{dataFormatName, resolver, inputStream});
1295            if (inputStream != null) {
1296                try {
1297                    return IOHelper.loadText(inputStream);
1298                } finally {
1299                    IOHelper.close(inputStream);
1300                }
1301            }
1302            return null;
1303
1304        } catch (ClassNotFoundException e) {
1305            return null;
1306        }
1307    }
1308
1309    public String getLanguageParameterJsonSchema(String languageName) throws IOException {
1310        // use the language factory finder to find the package name of the language class, which is the location
1311        // where the documentation exists as well
1312        FactoryFinder finder = getFactoryFinder(DefaultLanguageResolver.LANGUAGE_RESOURCE_PATH);
1313        try {
1314            Class<?> clazz = finder.findClass(languageName);
1315            if (clazz == null) {
1316                return null;
1317            }
1318
1319            String packageName = clazz.getPackage().getName();
1320            packageName = packageName.replace('.', '/');
1321            String path = packageName + "/" + languageName + ".json";
1322
1323            ClassResolver resolver = getClassResolver();
1324            InputStream inputStream = resolver.loadResourceAsStream(path);
1325            log.debug("Loading language JSON Schema for: {} using class resolver: {} -> {}", new Object[]{languageName, resolver, inputStream});
1326            if (inputStream != null) {
1327                try {
1328                    return IOHelper.loadText(inputStream);
1329                } finally {
1330                    IOHelper.close(inputStream);
1331                }
1332            }
1333            return null;
1334
1335        } catch (ClassNotFoundException e) {
1336            return null;
1337        }
1338    }
1339
1340    public String getEipParameterJsonSchema(String eipName) throws IOException {
1341        // the eip json schema may be in some of the sub-packages so look until we find it
1342        String[] subPackages = new String[]{"", "/config", "/dataformat", "/language", "/loadbalancer", "/rest"};
1343        for (String sub : subPackages) {
1344            String path = CamelContextHelper.MODEL_DOCUMENTATION_PREFIX + sub + "/" + eipName + ".json";
1345            ClassResolver resolver = getClassResolver();
1346            InputStream inputStream = resolver.loadResourceAsStream(path);
1347            if (inputStream != null) {
1348                log.debug("Loading eip JSON Schema for: {} using class resolver: {} -> {}", new Object[]{eipName, resolver, inputStream});
1349                try {
1350                    return IOHelper.loadText(inputStream);
1351                } finally {
1352                    IOHelper.close(inputStream);
1353                }
1354            }
1355        }
1356        return null;
1357    }
1358
1359    public String explainEipJson(String nameOrId, boolean includeAllOptions) {
1360        try {
1361            // try to find the id within all known routes and their eips
1362            String eipName = nameOrId;
1363            NamedNode target = null;
1364            for (RouteDefinition route : getRouteDefinitions()) {
1365                if (route.getId().equals(nameOrId)) {
1366                    target = route;
1367                    break;
1368                }
1369                for (FromDefinition from : route.getInputs()) {
1370                    if (nameOrId.equals(from.getId())) {
1371                        target = route;
1372                        break;
1373                    }
1374                }
1375                Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
1376                while (it.hasNext()) {
1377                    ProcessorDefinition def = it.next();
1378                    if (nameOrId.equals(def.getId())) {
1379                        target = def;
1380                        break;
1381                    }
1382                }
1383                if (target != null) {
1384                    break;
1385                }
1386            }
1387
1388            if (target != null) {
1389                eipName = target.getShortName();
1390            }
1391
1392            String json = getEipParameterJsonSchema(eipName);
1393            if (json == null) {
1394                return null;
1395            }
1396
1397            // overlay with runtime parameters that id uses at runtime
1398            if (target != null) {
1399                List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
1400
1401                // selected rows to use for answer
1402                Map<String, String[]> selected = new LinkedHashMap<String, String[]>();
1403
1404                // extract options from the node
1405                Map<String, Object> options = new LinkedHashMap<String, Object>();
1406                IntrospectionSupport.getProperties(target, options, "", false);
1407                // remove outputs which we do not want to include
1408                options.remove("outputs");
1409
1410                // include other rows
1411                for (Map<String, String> row : rows) {
1412                    String name = row.get("name");
1413                    String kind = row.get("kind");
1414                    String label = row.get("label");
1415                    String required = row.get("required");
1416                    String value = row.get("value");
1417                    String defaultValue = row.get("defaultValue");
1418                    String type = row.get("type");
1419                    String javaType = row.get("javaType");
1420                    String deprecated = row.get("deprecated");
1421                    String description = row.get("description");
1422
1423                    // find the configured option
1424                    Object o = options.get(name);
1425                    if (o != null) {
1426                        value = o.toString();
1427                    }
1428
1429                    value = URISupport.sanitizePath(value);
1430
1431                    if (includeAllOptions || o != null) {
1432                        // add as selected row
1433                        if (!selected.containsKey(name)) {
1434                            selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1435                        }
1436                    }
1437                }
1438
1439                json = ObjectHelper.before(json, "  \"properties\": {");
1440
1441                StringBuilder buffer = new StringBuilder("  \"properties\": {");
1442
1443                boolean first = true;
1444                for (String[] row : selected.values()) {
1445                    if (first) {
1446                        first = false;
1447                    } else {
1448                        buffer.append(",");
1449                    }
1450                    buffer.append("\n    ");
1451
1452                    String name = row[0];
1453                    String kind = row[1];
1454                    String label = row[2];
1455                    String required = row[3];
1456                    String type = row[4];
1457                    String javaType = row[5];
1458                    String deprecated = row[6];
1459                    String value = row[7];
1460                    String defaultValue = row[8];
1461                    String description = row[9];
1462
1463                    // add json of the option
1464                    buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1465                    CollectionStringBuffer csb = new CollectionStringBuffer();
1466                    if (kind != null) {
1467                        csb.append("\"kind\": \"" + kind + "\"");
1468                    }
1469                    if (label != null) {
1470                        csb.append("\"label\": \"" + label + "\"");
1471                    }
1472                    if (required != null) {
1473                        csb.append("\"required\": \"" + required + "\"");
1474                    }
1475                    if (type != null) {
1476                        csb.append("\"type\": \"" + type + "\"");
1477                    }
1478                    if (javaType != null) {
1479                        csb.append("\"javaType\": \"" + javaType + "\"");
1480                    }
1481                    if (deprecated != null) {
1482                        csb.append("\"deprecated\": \"" + deprecated + "\"");
1483                    }
1484                    if (value != null) {
1485                        csb.append("\"value\": \"" + value + "\"");
1486                    }
1487                    if (defaultValue != null) {
1488                        csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1489                    }
1490                    if (description != null) {
1491                        csb.append("\"description\": \"" + description + "\"");
1492                    }
1493                    if (!csb.isEmpty()) {
1494                        buffer.append(csb.toString());
1495                    }
1496                    buffer.append(" }");
1497                }
1498
1499                buffer.append("\n  }\n}\n");
1500
1501                // insert the original first part of the json into the start of the buffer
1502                buffer.insert(0, json);
1503                return buffer.toString();
1504            }
1505
1506            return json;
1507        } catch (Exception e) {
1508            // ignore and return empty response
1509            return null;
1510        }
1511    }
1512
1513    public String explainComponentJson(String componentName, boolean includeAllOptions) {
1514        try {
1515            String json = getComponentParameterJsonSchema(componentName);
1516            if (json == null) {
1517                return null;
1518            }
1519
1520            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("componentProperties", json, true);
1521
1522            // selected rows to use for answer
1523            Map<String, String[]> selected = new LinkedHashMap<String, String[]>();
1524
1525            // insert values from component
1526            Component component = getComponent(componentName);
1527            Map<String, Object> options = new HashMap<String, Object>();
1528            IntrospectionSupport.getProperties(component, options, null);
1529
1530            for (Map.Entry<String, Object> entry : options.entrySet()) {
1531                String name = entry.getKey();
1532
1533                // skip unwanted options which is default inherited from DefaultComponent
1534                if ("camelContext".equals(name) || "endpointClass".equals(name)) {
1535                    continue;
1536                }
1537
1538                String value = "";
1539                if (entry.getValue() != null) {
1540                    value = entry.getValue().toString();
1541                }
1542                value = URISupport.sanitizePath(value);
1543
1544                // find type and description from the json schema
1545                String type = null;
1546                String kind = null;
1547                String label = null;
1548                String required = null;
1549                String javaType = null;
1550                String deprecated = null;
1551                String defaultValue = null;
1552                String description = null;
1553                for (Map<String, String> row : rows) {
1554                    if (name.equals(row.get("name"))) {
1555                        type = row.get("type");
1556                        kind = row.get("kind");
1557                        label = row.get("label");
1558                        required = row.get("required");
1559                        javaType = row.get("javaType");
1560                        deprecated = row.get("deprecated");
1561                        defaultValue = row.get("defaultValue");
1562                        description = row.get("description");
1563                        break;
1564                    }
1565                }
1566
1567                // add as selected row
1568                selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1569            }
1570
1571            // include other rows
1572            for (Map<String, String> row : rows) {
1573                String name = row.get("name");
1574                String kind = row.get("kind");
1575                String label = row.get("label");
1576                String required = row.get("required");
1577                String value = row.get("value");
1578                String defaultValue = row.get("defaultValue");
1579                String type = row.get("type");
1580                String javaType = row.get("javaType");
1581                String deprecated = row.get("deprecated");
1582                value = URISupport.sanitizePath(value);
1583                String description = row.get("description");
1584
1585                // always include path options
1586                if (includeAllOptions) {
1587                    // add as selected row
1588                    if (!selected.containsKey(name)) {
1589                        selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1590                    }
1591                }
1592            }
1593
1594            json = ObjectHelper.before(json, "  \"componentProperties\": {");
1595
1596            StringBuilder buffer = new StringBuilder("  \"componentProperties\": {");
1597
1598            boolean first = true;
1599            for (String[] row : selected.values()) {
1600                if (first) {
1601                    first = false;
1602                } else {
1603                    buffer.append(",");
1604                }
1605                buffer.append("\n    ");
1606
1607                String name = row[0];
1608                String kind = row[1];
1609                String label = row[2];
1610                String required = row[3];
1611                String type = row[4];
1612                String javaType = row[5];
1613                String deprecated = row[6];
1614                String value = row[7];
1615                String defaultValue = row[8];
1616                String description = row[9];
1617
1618                // add json of the option
1619                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1620                CollectionStringBuffer csb = new CollectionStringBuffer();
1621                if (kind != null) {
1622                    csb.append("\"kind\": \"" + kind + "\"");
1623                }
1624                if (label != null) {
1625                    csb.append("\"label\": \"" + label + "\"");
1626                }
1627                if (required != null) {
1628                    csb.append("\"required\": \"" + required + "\"");
1629                }
1630                if (type != null) {
1631                    csb.append("\"type\": \"" + type + "\"");
1632                }
1633                if (javaType != null) {
1634                    csb.append("\"javaType\": \"" + javaType + "\"");
1635                }
1636                if (deprecated != null) {
1637                    csb.append("\"deprecated\": \"" + deprecated + "\"");
1638                }
1639                if (value != null) {
1640                    csb.append("\"value\": \"" + value + "\"");
1641                }
1642                if (defaultValue != null) {
1643                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1644                }
1645                if (description != null) {
1646                    csb.append("\"description\": \"" + description + "\"");
1647                }
1648                if (!csb.isEmpty()) {
1649                    buffer.append(csb.toString());
1650                }
1651                buffer.append(" }");
1652            }
1653
1654            buffer.append("\n  }\n}\n");
1655
1656            // insert the original first part of the json into the start of the buffer
1657            buffer.insert(0, json);
1658            return buffer.toString();
1659
1660        } catch (Exception e) {
1661            // ignore and return empty response
1662            return null;
1663        }
1664    }
1665
1666    public String explainEndpointJson(String uri, boolean includeAllOptions) {
1667        try {
1668            URI u = new URI(uri);
1669
1670            String json = getComponentParameterJsonSchema(u.getScheme());
1671            if (json == null) {
1672                return null;
1673            }
1674
1675            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
1676
1677            // selected rows to use for answer
1678            Map<String, String[]> selected = new LinkedHashMap<String, String[]>();
1679            Map<String, String[]> uriOptions = new LinkedHashMap<String, String[]>();
1680
1681            // insert values from uri
1682            Map<String, Object> options = URISupport.parseParameters(u);
1683
1684            // extract consumer. prefix options
1685            Map<String, Object> consumerOptions = IntrospectionSupport.extractProperties(options, "consumer.");
1686            // and add back again without the consumer. prefix as that json schema omits that
1687            options.putAll(consumerOptions);
1688
1689            for (Map.Entry<String, Object> entry : options.entrySet()) {
1690                String name = entry.getKey();
1691                String value = "";
1692                if (entry.getValue() != null) {
1693                    value = entry.getValue().toString();
1694                }
1695                value = URISupport.sanitizePath(value);
1696
1697                // find type and description from the json schema
1698                String type = null;
1699                String kind = null;
1700                String label = null;
1701                String required = null;
1702                String javaType = null;
1703                String deprecated = null;
1704                String defaultValue = null;
1705                String description = null;
1706                for (Map<String, String> row : rows) {
1707                    if (name.equals(row.get("name"))) {
1708                        type = row.get("type");
1709                        kind = row.get("kind");
1710                        label = row.get("label");
1711                        required = row.get("required");
1712                        javaType = row.get("javaType");
1713                        deprecated = row.get("deprecated");
1714                        defaultValue = row.get("defaultValue");
1715                        description = row.get("description");
1716                        break;
1717                    }
1718                }
1719
1720                // remember this option from the uri
1721                uriOptions.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1722            }
1723
1724            // include other rows
1725            for (Map<String, String> row : rows) {
1726                String name = row.get("name");
1727                String kind = row.get("kind");
1728                String label = row.get("label");
1729                String required = row.get("required");
1730                String value = row.get("value");
1731                String defaultValue = row.get("defaultValue");
1732                String type = row.get("type");
1733                String javaType = row.get("javaType");
1734                String deprecated = row.get("deprecated");
1735                value = URISupport.sanitizePath(value);
1736                String description = row.get("description");
1737
1738                if ("path".equals(kind)) {
1739                    // if its the path option then we need to grab the actual value from the uri, which is the remainder path
1740                    value = URISupport.extractRemainderPath(u, false);
1741                    value = URISupport.sanitizePath(value);
1742                }
1743
1744                boolean isUriOption = uriOptions.containsKey(name);
1745
1746                // always include from uri or path options
1747                if (includeAllOptions || isUriOption || "path".equals(kind)) {
1748                    if (!selected.containsKey(name)) {
1749                        // add as selected row, but take the value from uri options if it was from there
1750                        if (isUriOption) {
1751                            selected.put(name, uriOptions.get(name));
1752                        } else {
1753                            selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1754                        }
1755                    }
1756                }
1757            }
1758
1759            json = ObjectHelper.before(json, "  \"properties\": {");
1760
1761            StringBuilder buffer = new StringBuilder("  \"properties\": {");
1762
1763            boolean first = true;
1764            for (String[] row : selected.values()) {
1765                if (first) {
1766                    first = false;
1767                } else {
1768                    buffer.append(",");
1769                }
1770                buffer.append("\n    ");
1771
1772                String name = row[0];
1773                String kind = row[1];
1774                String label = row[2];
1775                String required = row[3];
1776                String type = row[4];
1777                String javaType = row[5];
1778                String deprecated = row[6];
1779                String value = row[7];
1780                String defaultValue = row[8];
1781                String description = row[9];
1782
1783                // add json of the option
1784                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1785                CollectionStringBuffer csb = new CollectionStringBuffer();
1786                if (kind != null) {
1787                    csb.append("\"kind\": \"" + kind + "\"");
1788                }
1789                if (label != null) {
1790                    csb.append("\"label\": \"" + label + "\"");
1791                }
1792                if (required != null) {
1793                    csb.append("\"required\": \"" + required + "\"");
1794                }
1795                if (type != null) {
1796                    csb.append("\"type\": \"" + type + "\"");
1797                }
1798                if (javaType != null) {
1799                    csb.append("\"javaType\": \"" + javaType + "\"");
1800                }
1801                if (deprecated != null) {
1802                    csb.append("\"deprecated\": \"" + deprecated + "\"");
1803                }
1804                if (value != null) {
1805                    csb.append("\"value\": \"" + value + "\"");
1806                }
1807                if (defaultValue != null) {
1808                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1809                }
1810                if (description != null) {
1811                    csb.append("\"description\": \"" + description + "\"");
1812                }
1813                if (!csb.isEmpty()) {
1814                    buffer.append(csb.toString());
1815                }
1816                buffer.append(" }");
1817            }
1818
1819            buffer.append("\n  }\n}\n");
1820
1821            // insert the original first part of the json into the start of the buffer
1822            buffer.insert(0, json);
1823            return buffer.toString();
1824
1825        } catch (Exception e) {
1826            // ignore and return empty response
1827            return null;
1828        }
1829    }
1830
1831    public String createRouteStaticEndpointJson(String routeId) {
1832        // lets include dynamic as well as we want as much data as possible
1833        return createRouteStaticEndpointJson(routeId, true);
1834    }
1835
1836    public String createRouteStaticEndpointJson(String routeId, boolean includeDynamic) {
1837        List<RouteDefinition> routes = new ArrayList<RouteDefinition>();
1838        if (routeId != null) {
1839            RouteDefinition route = getRouteDefinition(routeId);
1840            if (route == null) {
1841                throw new IllegalArgumentException("Route with id " + routeId + " does not exist");
1842            }
1843            routes.add(route);
1844        } else {
1845            routes.addAll(getRouteDefinitions());
1846        }
1847
1848        StringBuilder buffer = new StringBuilder("{\n  \"routes\": {");
1849        boolean firstRoute = true;
1850        for (RouteDefinition route : routes) {
1851            if (!firstRoute) {
1852                buffer.append("\n    },");
1853            } else {
1854                firstRoute = false;
1855            }
1856
1857            String id = route.getId();
1858            buffer.append("\n    \"").append(id).append("\": {");
1859            buffer.append("\n      \"inputs\": [");
1860            // for inputs we do not need to check dynamic as we have the data from the route definition
1861            Set<String> inputs = RouteDefinitionHelper.gatherAllStaticEndpointUris(this, route, true, false);
1862            boolean first = true;
1863            for (String input : inputs) {
1864                if (!first) {
1865                    buffer.append(",");
1866                } else {
1867                    first = false;
1868                }
1869                buffer.append("\n        ");
1870                buffer.append(StringHelper.toJson("uri", input, true));
1871            }
1872            buffer.append("\n      ]");
1873
1874            buffer.append(",");
1875            buffer.append("\n      \"outputs\": [");
1876            Set<String> outputs = RouteDefinitionHelper.gatherAllEndpointUris(this, route, false, true, includeDynamic);
1877            first = true;
1878            for (String output : outputs) {
1879                if (!first) {
1880                    buffer.append(",");
1881                } else {
1882                    first = false;
1883                }
1884                buffer.append("\n        ");
1885                buffer.append(StringHelper.toJson("uri", output, true));
1886            }
1887            buffer.append("\n      ]");
1888        }
1889        if (!firstRoute) {
1890            buffer.append("\n    }");
1891        }
1892        buffer.append("\n  }\n}\n");
1893
1894        return buffer.toString();
1895    }
1896
1897    // Helper methods
1898    // -----------------------------------------------------------------------
1899
1900    public Language resolveLanguage(String language) {
1901        Language answer;
1902        synchronized (languages) {
1903            answer = languages.get(language);
1904
1905            // check if the language is singleton, if so return the shared instance
1906            if (answer instanceof IsSingleton) {
1907                boolean singleton = ((IsSingleton) answer).isSingleton();
1908                if (singleton) {
1909                    return answer;
1910                }
1911            }
1912
1913            // language not known or not singleton, then use resolver
1914            answer = getLanguageResolver().resolveLanguage(language, this);
1915
1916            // inject CamelContext if aware
1917            if (answer != null) {
1918                if (answer instanceof CamelContextAware) {
1919                    ((CamelContextAware) answer).setCamelContext(this);
1920                }
1921                if (answer instanceof Service) {
1922                    try {
1923                        startService((Service) answer);
1924                    } catch (Exception e) {
1925                        throw ObjectHelper.wrapRuntimeCamelException(e);
1926                    }
1927                }
1928
1929                languages.put(language, answer);
1930            }
1931        }
1932
1933        return answer;
1934    }
1935
1936    public String getPropertyPrefixToken() {
1937        PropertiesComponent pc = getPropertiesComponent();
1938
1939        if (pc != null) {
1940            return pc.getPrefixToken();
1941        } else {
1942            return null;
1943        }
1944    }
1945
1946    public String getPropertySuffixToken() {
1947        PropertiesComponent pc = getPropertiesComponent();
1948
1949        if (pc != null) {
1950            return pc.getSuffixToken();
1951        } else {
1952            return null;
1953        }
1954    }
1955
1956    public String resolvePropertyPlaceholders(String text) throws Exception {
1957        // While it is more efficient to only do the lookup if we are sure we need the component,
1958        // with custom tokens, we cannot know if the URI contains a property or not without having
1959        // the component.  We also lose fail-fast behavior for the missing component with this change.
1960        PropertiesComponent pc = getPropertiesComponent();
1961
1962        // Do not parse uris that are designated for the properties component as it will handle that itself
1963        if (text != null && !text.startsWith("properties:")) {
1964            // No component, assume default tokens.
1965            if (pc == null && text.contains(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
1966                // lookup existing properties component, or force create a new default component
1967                pc = (PropertiesComponent) CamelContextHelper.lookupPropertiesComponent(this, true);
1968            }
1969
1970            if (pc != null && text.contains(pc.getPrefixToken())) {
1971                // the parser will throw exception if property key was not found
1972                String answer = pc.parseUri(text);
1973                log.debug("Resolved text: {} -> {}", text, answer);
1974                return answer;
1975            }
1976        }
1977
1978        // return original text as is
1979        return text;
1980    }
1981
1982    // Properties
1983    // -----------------------------------------------------------------------
1984
1985    public TypeConverter getTypeConverter() {
1986        if (typeConverter == null) {
1987            synchronized (this) {
1988                // we can synchronize on this as there is only one instance
1989                // of the camel context (its the container)
1990                typeConverter = createTypeConverter();
1991                try {
1992                    // must add service eager
1993                    addService(typeConverter);
1994                } catch (Exception e) {
1995                    throw ObjectHelper.wrapRuntimeCamelException(e);
1996                }
1997            }
1998        }
1999        return typeConverter;
2000    }
2001
2002    public void setTypeConverter(TypeConverter typeConverter) {
2003        this.typeConverter = typeConverter;
2004        try {
2005            // must add service eager
2006            addService(typeConverter);
2007        } catch (Exception e) {
2008            throw ObjectHelper.wrapRuntimeCamelException(e);
2009        }
2010    }
2011
2012    public TypeConverterRegistry getTypeConverterRegistry() {
2013        if (typeConverterRegistry == null) {
2014            // init type converter as its lazy
2015            if (typeConverter == null) {
2016                getTypeConverter();
2017            }
2018            if (typeConverter instanceof TypeConverterRegistry) {
2019                typeConverterRegistry = (TypeConverterRegistry) typeConverter;
2020            }
2021        }
2022        return typeConverterRegistry;
2023    }
2024
2025    public void setTypeConverterRegistry(TypeConverterRegistry typeConverterRegistry) {
2026        this.typeConverterRegistry = typeConverterRegistry;
2027    }
2028
2029    public Injector getInjector() {
2030        if (injector == null) {
2031            injector = createInjector();
2032        }
2033        return injector;
2034    }
2035
2036    public void setInjector(Injector injector) {
2037        this.injector = injector;
2038    }
2039
2040    public ManagementMBeanAssembler getManagementMBeanAssembler() {
2041        return managementMBeanAssembler;
2042    }
2043
2044    public void setManagementMBeanAssembler(ManagementMBeanAssembler managementMBeanAssembler) {
2045        this.managementMBeanAssembler = managementMBeanAssembler;
2046    }
2047
2048    public ComponentResolver getComponentResolver() {
2049        if (componentResolver == null) {
2050            componentResolver = createComponentResolver();
2051        }
2052        return componentResolver;
2053    }
2054
2055    public void setComponentResolver(ComponentResolver componentResolver) {
2056        this.componentResolver = componentResolver;
2057    }
2058
2059    public LanguageResolver getLanguageResolver() {
2060        if (languageResolver == null) {
2061            languageResolver = new DefaultLanguageResolver();
2062        }
2063        return languageResolver;
2064    }
2065
2066    public void setLanguageResolver(LanguageResolver languageResolver) {
2067        this.languageResolver = languageResolver;
2068    }
2069
2070    public boolean isAutoCreateComponents() {
2071        return autoCreateComponents;
2072    }
2073
2074    public void setAutoCreateComponents(boolean autoCreateComponents) {
2075        this.autoCreateComponents = autoCreateComponents;
2076    }
2077
2078    public Registry getRegistry() {
2079        if (registry == null) {
2080            registry = createRegistry();
2081            setRegistry(registry);
2082        }
2083        return registry;
2084    }
2085
2086    public <T> T getRegistry(Class<T> type) {
2087        Registry reg = getRegistry();
2088
2089        // unwrap the property placeholder delegate
2090        if (reg instanceof PropertyPlaceholderDelegateRegistry) {
2091            reg = ((PropertyPlaceholderDelegateRegistry) reg).getRegistry();
2092        }
2093
2094        if (type.isAssignableFrom(reg.getClass())) {
2095            return type.cast(reg);
2096        } else if (reg instanceof CompositeRegistry) {
2097            List<Registry> list = ((CompositeRegistry) reg).getRegistryList();
2098            for (Registry r : list) {
2099                if (type.isAssignableFrom(r.getClass())) {
2100                    return type.cast(r);
2101                }
2102            }
2103        }
2104        return null;
2105    }
2106
2107    /**
2108     * Sets the registry to the given JNDI context
2109     *
2110     * @param jndiContext is the JNDI context to use as the registry
2111     * @see #setRegistry(org.apache.camel.spi.Registry)
2112     */
2113    public void setJndiContext(Context jndiContext) {
2114        setRegistry(new JndiRegistry(jndiContext));
2115    }
2116
2117    public void setRegistry(Registry registry) {
2118        // wrap the registry so we always do property placeholder lookups
2119        if (!(registry instanceof PropertyPlaceholderDelegateRegistry)) {
2120            registry = new PropertyPlaceholderDelegateRegistry(this, registry);
2121        }
2122        this.registry = registry;
2123    }
2124
2125    public List<LifecycleStrategy> getLifecycleStrategies() {
2126        return lifecycleStrategies;
2127    }
2128
2129    public void setLifecycleStrategies(List<LifecycleStrategy> lifecycleStrategies) {
2130        this.lifecycleStrategies = lifecycleStrategies;
2131    }
2132
2133    public void addLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
2134        this.lifecycleStrategies.add(lifecycleStrategy);
2135    }
2136
2137    public void setupRoutes(boolean done) {
2138        if (done) {
2139            isSetupRoutes.remove();
2140        } else {
2141            isSetupRoutes.set(true);
2142        }
2143    }
2144
2145    public synchronized List<RouteDefinition> getRouteDefinitions() {
2146        return routeDefinitions;
2147    }
2148
2149    public synchronized RouteDefinition getRouteDefinition(String id) {
2150        for (RouteDefinition route : routeDefinitions) {
2151            if (route.idOrCreate(nodeIdFactory).equals(id)) {
2152                return route;
2153            }
2154        }
2155        return null;
2156    }
2157
2158    public synchronized List<RestDefinition> getRestDefinitions() {
2159        return restDefinitions;
2160    }
2161
2162    public void addRestDefinitions(Collection<RestDefinition> restDefinitions) throws Exception {
2163        if (restDefinitions == null || restDefinitions.isEmpty()) {
2164            return;
2165        }
2166
2167        this.restDefinitions.addAll(restDefinitions);
2168    }
2169
2170    public RestConfiguration getRestConfiguration() {
2171        return restConfiguration;
2172    }
2173
2174    public void setRestConfiguration(RestConfiguration restConfiguration) {
2175        this.restConfiguration = restConfiguration;
2176    }
2177
2178    public List<InterceptStrategy> getInterceptStrategies() {
2179        return interceptStrategies;
2180    }
2181
2182    public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
2183        this.interceptStrategies = interceptStrategies;
2184    }
2185
2186    public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
2187        getInterceptStrategies().add(interceptStrategy);
2188
2189        // for backwards compatible or if user add them here instead of the setXXX methods
2190
2191        if (interceptStrategy instanceof Tracer) {
2192            setTracing(true);
2193        } else if (interceptStrategy instanceof HandleFault) {
2194            setHandleFault(true);
2195        } else if (interceptStrategy instanceof StreamCaching) {
2196            setStreamCaching(true);
2197        } else if (interceptStrategy instanceof Delayer) {
2198            setDelayer(((Delayer)interceptStrategy).getDelay());
2199        }
2200    }
2201
2202    public List<RoutePolicyFactory> getRoutePolicyFactories() {
2203        return routePolicyFactories;
2204    }
2205
2206    public void setRoutePolicyFactories(List<RoutePolicyFactory> routePolicyFactories) {
2207        this.routePolicyFactories = routePolicyFactories;
2208    }
2209
2210    public void addRoutePolicyFactory(RoutePolicyFactory routePolicyFactory) {
2211        getRoutePolicyFactories().add(routePolicyFactory);
2212    }
2213
2214    public void setStreamCaching(Boolean cache) {
2215        this.streamCache = cache;
2216    }
2217
2218    public Boolean isStreamCaching() {
2219        return streamCache;
2220    }
2221
2222    public void setTracing(Boolean tracing) {
2223        this.trace = tracing;
2224    }
2225
2226    public Boolean isTracing() {
2227        return trace;
2228    }
2229
2230    public Boolean isMessageHistory() {
2231        return messageHistory;
2232    }
2233
2234    public void setMessageHistory(Boolean messageHistory) {
2235        this.messageHistory = messageHistory;
2236    }
2237
2238    public Boolean isHandleFault() {
2239        return handleFault;
2240    }
2241
2242    public void setHandleFault(Boolean handleFault) {
2243        this.handleFault = handleFault;
2244    }
2245
2246    public Long getDelayer() {
2247        return delay;
2248    }
2249
2250    public void setDelayer(Long delay) {
2251        this.delay = delay;
2252    }
2253
2254    public ProducerTemplate createProducerTemplate() {
2255        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2256        return createProducerTemplate(size);
2257    }
2258
2259    public ProducerTemplate createProducerTemplate(int maximumCacheSize) {
2260        DefaultProducerTemplate answer = new DefaultProducerTemplate(this);
2261        answer.setMaximumCacheSize(maximumCacheSize);
2262        // start it so its ready to use
2263        try {
2264            startService(answer);
2265        } catch (Exception e) {
2266            throw ObjectHelper.wrapRuntimeCamelException(e);
2267        }
2268        return answer;
2269    }
2270
2271    public ConsumerTemplate createConsumerTemplate() {
2272        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2273        return createConsumerTemplate(size);
2274    }
2275
2276    public ConsumerTemplate createConsumerTemplate(int maximumCacheSize) {
2277        DefaultConsumerTemplate answer = new DefaultConsumerTemplate(this);
2278        answer.setMaximumCacheSize(maximumCacheSize);
2279        // start it so its ready to use
2280        try {
2281            startService(answer);
2282        } catch (Exception e) {
2283            throw ObjectHelper.wrapRuntimeCamelException(e);
2284        }
2285        return answer;
2286    }
2287
2288    public ErrorHandlerBuilder getErrorHandlerBuilder() {
2289        return (ErrorHandlerBuilder)errorHandlerBuilder;
2290    }
2291
2292    public void setErrorHandlerBuilder(ErrorHandlerFactory errorHandlerBuilder) {
2293        this.errorHandlerBuilder = errorHandlerBuilder;
2294    }
2295
2296    public ScheduledExecutorService getErrorHandlerExecutorService() {
2297        synchronized (errorHandlerExecutorServiceLock) {
2298            if (errorHandlerExecutorService == null) {
2299                // setup default thread pool for error handler
2300                errorHandlerExecutorService = getExecutorServiceManager().newDefaultScheduledThreadPool("ErrorHandlerRedeliveryThreadPool", "ErrorHandlerRedeliveryTask");
2301            }
2302        }
2303        return errorHandlerExecutorService;
2304    }
2305
2306    public void setProducerServicePool(ServicePool<Endpoint, Producer> producerServicePool) {
2307        this.producerServicePool = producerServicePool;
2308    }
2309
2310    public ServicePool<Endpoint, Producer> getProducerServicePool() {
2311        return producerServicePool;
2312    }
2313
2314    public ServicePool<Endpoint, PollingConsumer> getPollingConsumerServicePool() {
2315        return pollingConsumerServicePool;
2316    }
2317
2318    public void setPollingConsumerServicePool(ServicePool<Endpoint, PollingConsumer> pollingConsumerServicePool) {
2319        this.pollingConsumerServicePool = pollingConsumerServicePool;
2320    }
2321
2322    public UnitOfWorkFactory getUnitOfWorkFactory() {
2323        return unitOfWorkFactory;
2324    }
2325
2326    public void setUnitOfWorkFactory(UnitOfWorkFactory unitOfWorkFactory) {
2327        this.unitOfWorkFactory = unitOfWorkFactory;
2328    }
2329
2330    public RuntimeEndpointRegistry getRuntimeEndpointRegistry() {
2331        return runtimeEndpointRegistry;
2332    }
2333
2334    public void setRuntimeEndpointRegistry(RuntimeEndpointRegistry runtimeEndpointRegistry) {
2335        this.runtimeEndpointRegistry = runtimeEndpointRegistry;
2336    }
2337
2338    public String getUptime() {
2339        // compute and log uptime
2340        if (startDate == null) {
2341            return "not started";
2342        }
2343        long delta = new Date().getTime() - startDate.getTime();
2344        return TimeUtils.printDuration(delta);
2345    }
2346
2347    @Override
2348    protected void doSuspend() throws Exception {
2349        EventHelper.notifyCamelContextSuspending(this);
2350
2351        log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is suspending");
2352        StopWatch watch = new StopWatch();
2353
2354        // update list of started routes to be suspended
2355        // because we only want to suspend started routes
2356        // (so when we resume we only resume the routes which actually was suspended)
2357        for (Map.Entry<String, RouteService> entry : getRouteServices().entrySet()) {
2358            if (entry.getValue().getStatus().isStarted()) {
2359                suspendedRouteServices.put(entry.getKey(), entry.getValue());
2360            }
2361        }
2362
2363        // assemble list of startup ordering so routes can be shutdown accordingly
2364        List<RouteStartupOrder> orders = new ArrayList<RouteStartupOrder>();
2365        for (Map.Entry<String, RouteService> entry : suspendedRouteServices.entrySet()) {
2366            Route route = entry.getValue().getRoutes().iterator().next();
2367            Integer order = entry.getValue().getRouteDefinition().getStartupOrder();
2368            if (order == null) {
2369                order = defaultRouteStartupOrder++;
2370            }
2371            orders.add(new DefaultRouteStartupOrder(order, route, entry.getValue()));
2372        }
2373
2374        // suspend routes using the shutdown strategy so it can shutdown in correct order
2375        // routes which doesn't support suspension will be stopped instead
2376        getShutdownStrategy().suspend(this, orders);
2377
2378        // mark the route services as suspended or stopped
2379        for (RouteService service : suspendedRouteServices.values()) {
2380            if (routeSupportsSuspension(service.getId())) {
2381                service.suspend();
2382            } else {
2383                service.stop();
2384            }
2385        }
2386
2387        watch.stop();
2388        if (log.isInfoEnabled()) {
2389            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is suspended in " + TimeUtils.printDuration(watch.taken()));
2390        }
2391
2392        EventHelper.notifyCamelContextSuspended(this);
2393    }
2394
2395    @Override
2396    protected void doResume() throws Exception {
2397        try {
2398            EventHelper.notifyCamelContextResuming(this);
2399
2400            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is resuming");
2401            StopWatch watch = new StopWatch();
2402
2403            // start the suspended routes (do not check for route clashes, and indicate)
2404            doStartOrResumeRoutes(suspendedRouteServices, false, true, true, false);
2405
2406            // mark the route services as resumed (will be marked as started) as well
2407            for (RouteService service : suspendedRouteServices.values()) {
2408                if (routeSupportsSuspension(service.getId())) {
2409                    service.resume();
2410                } else {
2411                    service.start();
2412                }
2413            }
2414
2415            watch.stop();
2416            if (log.isInfoEnabled()) {
2417                log.info("Resumed " + suspendedRouteServices.size() + " routes");
2418                log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") resumed in " + TimeUtils.printDuration(watch.taken()));
2419            }
2420
2421            // and clear the list as they have been resumed
2422            suspendedRouteServices.clear();
2423
2424            EventHelper.notifyCamelContextResumed(this);
2425        } catch (Exception e) {
2426            EventHelper.notifyCamelContextResumeFailed(this, e);
2427            throw e;
2428        }
2429    }
2430
2431    public void start() throws Exception {
2432        startDate = new Date();
2433        stopWatch.restart();
2434        log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is starting");
2435
2436        doNotStartRoutesOnFirstStart = !firstStartDone && !isAutoStartup();
2437
2438        // if the context was configured with auto startup = false, and we are already started,
2439        // then we may need to start the routes on the 2nd start call
2440        if (firstStartDone && !isAutoStartup() && isStarted()) {
2441            // invoke this logic to warm up the routes and if possible also start the routes
2442            doStartOrResumeRoutes(routeServices, true, true, false, true);
2443        }
2444
2445        // super will invoke doStart which will prepare internal services and start routes etc.
2446        try {
2447            firstStartDone = true;
2448            super.start();
2449        } catch (VetoCamelContextStartException e) {
2450            if (e.isRethrowException()) {
2451                throw e;
2452            } else {
2453                log.info("CamelContext ({}) vetoed to not start due {}", getName(), e.getMessage());
2454                // swallow exception and change state of this camel context to stopped
2455                stop();
2456                return;
2457            }
2458        }
2459
2460        stopWatch.stop();
2461        if (log.isInfoEnabled()) {
2462            // count how many routes are actually started
2463            int started = 0;
2464            for (Route route : getRoutes()) {
2465                if (getRouteStatus(route.getId()).isStarted()) {
2466                    started++;
2467                }
2468            }
2469            log.info("Total " + getRoutes().size() + " routes, of which " + started + " is started.");
2470            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") started in " + TimeUtils.printDuration(stopWatch.taken()));
2471        }
2472        EventHelper.notifyCamelContextStarted(this);
2473    }
2474
2475    // Implementation methods
2476    // -----------------------------------------------------------------------
2477
2478    protected synchronized void doStart() throws Exception {
2479        doWithDefinedClassLoader(new Callable<Void>() {
2480            @Override
2481            public Void call() throws Exception {
2482                try {
2483                    doStartCamel();
2484                    return null;
2485                } catch (Exception e) {
2486                    // fire event that we failed to start
2487                    EventHelper.notifyCamelContextStartupFailed(DefaultCamelContext.this, e);
2488                    // rethrow cause
2489                    throw e;
2490                }
2491            }
2492        });
2493    }
2494
2495    private <T> T doWithDefinedClassLoader(Callable<T> callable) throws Exception {
2496        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
2497        try {
2498            // Using the ApplicationClassLoader as the default for TCCL
2499            if (applicationContextClassLoader != null) {
2500                Thread.currentThread().setContextClassLoader(applicationContextClassLoader);
2501            }
2502            return callable.call();
2503        } finally {
2504            Thread.currentThread().setContextClassLoader(tccl);
2505        }
2506    }
2507
2508    private void doStartCamel() throws Exception {
2509
2510        if (classResolver instanceof CamelContextAware) {
2511            ((CamelContextAware) classResolver).setCamelContext(this);
2512        }
2513
2514        if (log.isDebugEnabled()) {
2515            log.debug("Using ClassResolver={}, PackageScanClassResolver={}, ApplicationContextClassLoader={}",
2516                    new Object[]{getClassResolver(), getPackageScanClassResolver(), getApplicationContextClassLoader()});
2517        }
2518
2519        if (isStreamCaching()) {
2520            log.info("StreamCaching is enabled on CamelContext: {}", getName());
2521        }
2522
2523        if (isTracing()) {
2524            // tracing is added in the DefaultChannel so we can enable it on the fly
2525            log.info("Tracing is enabled on CamelContext: {}", getName());
2526        }
2527
2528        if (isUseMDCLogging()) {
2529            // log if MDC has been enabled
2530            log.info("MDC logging is enabled on CamelContext: {}", getName());
2531        }
2532
2533        if (isHandleFault()) {
2534            // only add a new handle fault if not already configured
2535            if (HandleFault.getHandleFault(this) == null) {
2536                log.info("HandleFault is enabled on CamelContext: {}", getName());
2537                addInterceptStrategy(new HandleFault());
2538            }
2539        }
2540
2541        if (getDelayer() != null && getDelayer() > 0) {
2542            log.info("Delayer is enabled with: {} ms. on CamelContext: {}", getDelayer(), getName());
2543        }
2544
2545        // register debugger
2546        if (getDebugger() != null) {
2547            log.info("Debugger: {} is enabled on CamelContext: {}", getDebugger(), getName());
2548            // register this camel context on the debugger
2549            getDebugger().setCamelContext(this);
2550            startService(getDebugger());
2551            addInterceptStrategy(new Debug(getDebugger()));
2552        }
2553
2554        // start management strategy before lifecycles are started
2555        ManagementStrategy managementStrategy = getManagementStrategy();
2556        // inject CamelContext if aware
2557        if (managementStrategy instanceof CamelContextAware) {
2558            ((CamelContextAware) managementStrategy).setCamelContext(this);
2559        }
2560        ServiceHelper.startService(managementStrategy);
2561
2562        // start lifecycle strategies
2563        ServiceHelper.startServices(lifecycleStrategies);
2564        Iterator<LifecycleStrategy> it = lifecycleStrategies.iterator();
2565        while (it.hasNext()) {
2566            LifecycleStrategy strategy = it.next();
2567            try {
2568                strategy.onContextStart(this);
2569            } catch (VetoCamelContextStartException e) {
2570                // okay we should not start Camel since it was vetoed
2571                log.warn("Lifecycle strategy vetoed starting CamelContext ({}) due {}", getName(), e.getMessage());
2572                throw e;
2573            } catch (Exception e) {
2574                log.warn("Lifecycle strategy " + strategy + " failed starting CamelContext ({}) due {}", getName(), e.getMessage());
2575                throw e;
2576            }
2577        }
2578
2579        // start notifiers as services
2580        for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
2581            if (notifier instanceof Service) {
2582                Service service = (Service) notifier;
2583                for (LifecycleStrategy strategy : lifecycleStrategies) {
2584                    strategy.onServiceAdd(this, service, null);
2585                }
2586            }
2587            if (notifier instanceof Service) {
2588                startService((Service)notifier);
2589            }
2590        }
2591
2592        // must let some bootstrap service be started before we can notify the starting event
2593        EventHelper.notifyCamelContextStarting(this);
2594
2595        forceLazyInitialization();
2596
2597        // re-create endpoint registry as the cache size limit may be set after the constructor of this instance was called.
2598        // and we needed to create endpoints up-front as it may be accessed before this context is started
2599        endpoints = new DefaultEndpointRegistry(this, endpoints);
2600        addService(endpoints);
2601        // special for executorServiceManager as want to stop it manually
2602        doAddService(executorServiceManager, false);
2603        addService(producerServicePool);
2604        addService(inflightRepository);
2605        addService(asyncProcessorAwaitManager);
2606        addService(shutdownStrategy);
2607        addService(packageScanClassResolver);
2608        addService(restRegistry);
2609
2610        if (runtimeEndpointRegistry != null) {
2611            if (runtimeEndpointRegistry instanceof EventNotifier) {
2612                getManagementStrategy().addEventNotifier((EventNotifier) runtimeEndpointRegistry);
2613            }
2614            addService(runtimeEndpointRegistry);
2615        }
2616
2617        // eager lookup any configured properties component to avoid subsequent lookup attempts which may impact performance
2618        // due we use properties component for property placeholder resolution at runtime
2619        Component existing = CamelContextHelper.lookupPropertiesComponent(this, false);
2620        if (existing != null) {
2621            // store reference to the existing properties component
2622            if (existing instanceof PropertiesComponent) {
2623                propertiesComponent = (PropertiesComponent) existing;
2624            } else {
2625                // properties component must be expected type
2626                throw new IllegalArgumentException("Found properties component of type: " + existing.getClass() + " instead of expected: " + PropertiesComponent.class);
2627            }
2628        }
2629
2630        // start components
2631        startServices(components.values());
2632
2633        // start the route definitions before the routes is started
2634        startRouteDefinitions(routeDefinitions);
2635
2636        // 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
2637        boolean streamCachingInUse = isStreamCaching();
2638        if (!streamCachingInUse) {
2639            for (RouteDefinition route : routeDefinitions) {
2640                Boolean routeCache = CamelContextHelper.parseBoolean(this, route.getStreamCache());
2641                if (routeCache != null && routeCache) {
2642                    streamCachingInUse = true;
2643                    break;
2644                }
2645            }
2646        }
2647
2648        if (isAllowUseOriginalMessage()) {
2649            log.info("AllowUseOriginalMessage is enabled. If access to the original message is not needed,"
2650                    + " then its recommended to turn this option off as it may improve performance.");
2651        }
2652
2653        if (streamCachingInUse) {
2654            // stream caching is in use so enable the strategy
2655            getStreamCachingStrategy().setEnabled(true);
2656            addService(getStreamCachingStrategy());
2657        } else {
2658            // log if stream caching is not in use as this can help people to enable it if they use streams
2659            log.info("StreamCaching is not in use. If using streams then its recommended to enable stream caching."
2660                    + " See more details at http://camel.apache.org/stream-caching.html");
2661        }
2662
2663        // start routes
2664        if (doNotStartRoutesOnFirstStart) {
2665            log.debug("Skip starting of routes as CamelContext has been configured with autoStartup=false");
2666        }
2667
2668        // invoke this logic to warmup the routes and if possible also start the routes
2669        doStartOrResumeRoutes(routeServices, true, !doNotStartRoutesOnFirstStart, false, true);
2670
2671        // starting will continue in the start method
2672    }
2673
2674    protected synchronized void doStop() throws Exception {
2675        stopWatch.restart();
2676        log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is shutting down");
2677        EventHelper.notifyCamelContextStopping(this);
2678
2679        // stop route inputs in the same order as they was started so we stop the very first inputs first
2680        try {
2681            // force shutting down routes as they may otherwise cause shutdown to hang
2682            shutdownStrategy.shutdownForced(this, getRouteStartupOrder());
2683        } catch (Throwable e) {
2684            log.warn("Error occurred while shutting down routes. This exception will be ignored.", e);
2685        }
2686        getRouteStartupOrder().clear();
2687
2688        // shutdown await manager to trigger interrupt of blocked threads to attempt to free these threads graceful
2689        shutdownServices(asyncProcessorAwaitManager);
2690
2691        shutdownServices(routeServices.values());
2692        // do not clear route services or startup listeners as we can start Camel again and get the route back as before
2693
2694        // but clear any suspend routes
2695        suspendedRouteServices.clear();
2696
2697        // stop consumers from the services to close first, such as POJO consumer (eg @Consumer)
2698        // which we need to stop after the routes, as a POJO consumer is essentially a route also
2699        for (Service service : servicesToClose) {
2700            if (service instanceof Consumer) {
2701                shutdownServices(service);
2702            }
2703        }
2704
2705        // the stop order is important
2706
2707        // shutdown default error handler thread pool
2708        if (errorHandlerExecutorService != null) {
2709            // force shutting down the thread pool
2710            getExecutorServiceManager().shutdownNow(errorHandlerExecutorService);
2711            errorHandlerExecutorService = null;
2712        }
2713
2714        // shutdown debugger
2715        ServiceHelper.stopAndShutdownService(getDebugger());
2716
2717        shutdownServices(endpoints.values());
2718        endpoints.clear();
2719
2720        shutdownServices(components.values());
2721        components.clear();
2722
2723        shutdownServices(languages.values());
2724        languages.clear();
2725
2726        try {
2727            for (LifecycleStrategy strategy : lifecycleStrategies) {
2728                strategy.onContextStop(this);
2729            }
2730        } catch (Throwable e) {
2731            log.warn("Error occurred while stopping lifecycle strategies. This exception will be ignored.", e);
2732        }
2733
2734        // shutdown services as late as possible
2735        shutdownServices(servicesToClose);
2736        servicesToClose.clear();
2737
2738        // must notify that we are stopped before stopping the management strategy
2739        EventHelper.notifyCamelContextStopped(this);
2740
2741        // stop the notifier service
2742        for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
2743            shutdownServices(notifier);
2744        }
2745
2746        // shutdown executor service and management as the last one
2747        shutdownServices(executorServiceManager);
2748        shutdownServices(managementStrategy);
2749        shutdownServices(managementMBeanAssembler);
2750        shutdownServices(lifecycleStrategies);
2751        // do not clear lifecycleStrategies as we can start Camel again and get the route back as before
2752
2753        // stop the lazy created so they can be re-created on restart
2754        forceStopLazyInitialization();
2755
2756        // stop to clear introspection cache
2757        IntrospectionSupport.stop();
2758
2759        stopWatch.stop();
2760        if (log.isInfoEnabled()) {
2761            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") uptime {}", getUptime());
2762            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") is shutdown in " + TimeUtils.printDuration(stopWatch.taken()));
2763        }
2764
2765        // and clear start date
2766        startDate = null;
2767
2768        // [TODO] Remove in 3.0
2769        Container.Instance.unmanage(this);
2770    }
2771
2772    /**
2773     * Starts or resumes the routes
2774     *
2775     * @param routeServices  the routes to start (will only start a route if its not already started)
2776     * @param checkClash     whether to check for startup ordering clash
2777     * @param startConsumer  whether the route consumer should be started. Can be used to warmup the route without starting the consumer.
2778     * @param resumeConsumer whether the route consumer should be resumed.
2779     * @param addingRoutes   whether we are adding new routes
2780     * @throws Exception is thrown if error starting routes
2781     */
2782    protected void doStartOrResumeRoutes(Map<String, RouteService> routeServices, boolean checkClash,
2783                                         boolean startConsumer, boolean resumeConsumer, boolean addingRoutes) throws Exception {
2784        isStartingRoutes.set(true);
2785        try {
2786            // filter out already started routes
2787            Map<String, RouteService> filtered = new LinkedHashMap<String, RouteService>();
2788            for (Map.Entry<String, RouteService> entry : routeServices.entrySet()) {
2789                boolean startable = false;
2790
2791                Consumer consumer = entry.getValue().getRoutes().iterator().next().getConsumer();
2792                if (consumer instanceof SuspendableService) {
2793                    // consumer could be suspended, which is not reflected in the RouteService status
2794                    startable = ((SuspendableService) consumer).isSuspended();
2795                }
2796
2797                if (!startable && consumer instanceof StatefulService) {
2798                    // consumer could be stopped, which is not reflected in the RouteService status
2799                    startable = ((StatefulService) consumer).getStatus().isStartable();
2800                } else if (!startable) {
2801                    // no consumer so use state from route service
2802                    startable = entry.getValue().getStatus().isStartable();
2803                }
2804
2805                if (startable) {
2806                    filtered.put(entry.getKey(), entry.getValue());
2807                }
2808            }
2809
2810            if (!filtered.isEmpty()) {
2811                // the context is now considered started (i.e. isStarted() == true))
2812                // starting routes is done after, not during context startup
2813                safelyStartRouteServices(checkClash, startConsumer, resumeConsumer, addingRoutes, filtered.values());
2814            }
2815
2816            // we are finished starting routes, so remove flag before we emit the startup listeners below
2817            isStartingRoutes.remove();
2818
2819            // now notify any startup aware listeners as all the routes etc has been started,
2820            // allowing the listeners to do custom work after routes has been started
2821            for (StartupListener startup : startupListeners) {
2822                startup.onCamelContextStarted(this, isStarted());
2823            }
2824        } finally {
2825            isStartingRoutes.remove();
2826        }
2827    }
2828
2829    protected boolean routeSupportsSuspension(String routeId) {
2830        RouteService routeService = routeServices.get(routeId);
2831        if (routeService != null) {
2832            return routeService.getRoutes().iterator().next().supportsSuspension();
2833        }
2834        return false;
2835    }
2836
2837    private void shutdownServices(Object service) {
2838        // do not rethrow exception as we want to keep shutting down in case of problems
2839
2840        // allow us to do custom work before delegating to service helper
2841        try {
2842            if (service instanceof Service) {
2843                ServiceHelper.stopAndShutdownService(service);
2844            } else if (service instanceof Collection) {
2845                ServiceHelper.stopAndShutdownServices((Collection<?>)service);
2846            }
2847        } catch (Throwable e) {
2848            log.warn("Error occurred while shutting down service: " + service + ". This exception will be ignored.", e);
2849            // fire event
2850            EventHelper.notifyServiceStopFailure(this, service, e);
2851        }
2852    }
2853
2854    private void shutdownServices(Collection<?> services) {
2855        // reverse stopping by default
2856        shutdownServices(services, true);
2857    }
2858
2859    private void shutdownServices(Collection<?> services, boolean reverse) {
2860        Collection<?> list = services;
2861        if (reverse) {
2862            List<Object> reverseList = new ArrayList<Object>(services);
2863            Collections.reverse(reverseList);
2864            list = reverseList;
2865        }
2866
2867        for (Object service : list) {
2868            shutdownServices(service);
2869        }
2870    }
2871
2872    private void startService(Service service) throws Exception {
2873        // and register startup aware so they can be notified when
2874        // camel context has been started
2875        if (service instanceof StartupListener) {
2876            StartupListener listener = (StartupListener) service;
2877            addStartupListener(listener);
2878        }
2879
2880        if (service instanceof CamelContextAware) {
2881            CamelContextAware aware = (CamelContextAware) service;
2882            aware.setCamelContext(this);
2883        }
2884
2885        service.start();
2886    }
2887
2888    private void startServices(Collection<?> services) throws Exception {
2889        for (Object element : services) {
2890            if (element instanceof Service) {
2891                startService((Service)element);
2892            }
2893        }
2894    }
2895
2896    private void stopServices(Object service) throws Exception {
2897        // allow us to do custom work before delegating to service helper
2898        try {
2899            ServiceHelper.stopService(service);
2900        } catch (Exception e) {
2901            // fire event
2902            EventHelper.notifyServiceStopFailure(this, service, e);
2903            // rethrow to signal error with stopping
2904            throw e;
2905        }
2906    }
2907
2908    protected void startRouteDefinitions(Collection<RouteDefinition> list) throws Exception {
2909        if (list != null) {
2910            for (RouteDefinition route : list) {
2911                startRoute(route);
2912            }
2913        }
2914    }
2915
2916    /**
2917     * Starts the given route service
2918     */
2919    protected synchronized void startRouteService(RouteService routeService, boolean addingRoutes) throws Exception {
2920        // we may already be starting routes so remember this, so we can unset accordingly in finally block
2921        boolean alreadyStartingRoutes = isStartingRoutes();
2922        if (!alreadyStartingRoutes) {
2923            isStartingRoutes.set(true);
2924        }
2925
2926        try {
2927            // the route service could have been suspended, and if so then resume it instead
2928            if (routeService.getStatus().isSuspended()) {
2929                resumeRouteService(routeService);
2930            } else {
2931                // start the route service
2932                routeServices.put(routeService.getId(), routeService);
2933                if (shouldStartRoutes()) {
2934                    // this method will log the routes being started
2935                    safelyStartRouteServices(true, true, true, false, addingRoutes, routeService);
2936                    // start route services if it was configured to auto startup and we are not adding routes
2937                    boolean autoStartup = routeService.getRouteDefinition().isAutoStartup(this) && this.isAutoStartup();
2938                    if (!addingRoutes || autoStartup) {
2939                        // start the route since auto start is enabled or we are starting a route (not adding new routes)
2940                        routeService.start();
2941                    }
2942                }
2943            }
2944        } finally {
2945            if (!alreadyStartingRoutes) {
2946                isStartingRoutes.remove();
2947            }
2948        }
2949    }
2950
2951    /**
2952     * Resumes the given route service
2953     */
2954    protected synchronized void resumeRouteService(RouteService routeService) throws Exception {
2955        // the route service could have been stopped, and if so then start it instead
2956        if (!routeService.getStatus().isSuspended()) {
2957            startRouteService(routeService, false);
2958        } else {
2959            // resume the route service
2960            if (shouldStartRoutes()) {
2961                // this method will log the routes being started
2962                safelyStartRouteServices(true, false, true, true, false, routeService);
2963                // must resume route service as well
2964                routeService.resume();
2965            }
2966        }
2967    }
2968
2969    protected synchronized void stopRouteService(RouteService routeService, boolean removingRoutes) throws Exception {
2970        routeService.setRemovingRoutes(removingRoutes);
2971        stopRouteService(routeService);
2972    }
2973
2974    protected void logRouteState(Route route, String state) {
2975        if (log.isInfoEnabled()) {
2976            if (route.getConsumer() != null) {
2977                log.info("Route: {} is {}, was consuming from: {}", new Object[]{route.getId(), state, route.getConsumer().getEndpoint()});
2978            } else {
2979                log.info("Route: {} is {}.", route.getId(), state);
2980            }
2981        }
2982    }
2983
2984    protected synchronized void stopRouteService(RouteService routeService) throws Exception {
2985        routeService.stop();
2986        for (Route route : routeService.getRoutes()) {
2987            logRouteState(route, "stopped");
2988        }
2989    }
2990
2991    protected synchronized void shutdownRouteService(RouteService routeService) throws Exception {
2992        routeService.shutdown();
2993        for (Route route : routeService.getRoutes()) {
2994            logRouteState(route, "shutdown and removed");
2995        }
2996    }
2997
2998    protected synchronized void suspendRouteService(RouteService routeService) throws Exception {
2999        routeService.setRemovingRoutes(false);
3000        routeService.suspend();
3001        for (Route route : routeService.getRoutes()) {
3002            logRouteState(route, "suspended");
3003        }
3004    }
3005
3006    /**
3007     * Starts the routes services in a proper manner which ensures the routes will be started in correct order,
3008     * check for clash and that the routes will also be shutdown in correct order as well.
3009     * <p/>
3010     * This method <b>must</b> be used to start routes in a safe manner.
3011     *
3012     * @param checkClash     whether to check for startup order clash
3013     * @param startConsumer  whether the route consumer should be started. Can be used to warmup the route without starting the consumer.
3014     * @param resumeConsumer whether the route consumer should be resumed.
3015     * @param addingRoutes   whether we are adding new routes
3016     * @param routeServices  the routes
3017     * @throws Exception is thrown if error starting the routes
3018     */
3019    protected synchronized void safelyStartRouteServices(boolean checkClash, boolean startConsumer, boolean resumeConsumer,
3020                                                         boolean addingRoutes, Collection<RouteService> routeServices) throws Exception {
3021        // list of inputs to start when all the routes have been prepared for starting
3022        // we use a tree map so the routes will be ordered according to startup order defined on the route
3023        Map<Integer, DefaultRouteStartupOrder> inputs = new TreeMap<Integer, DefaultRouteStartupOrder>();
3024
3025        // figure out the order in which the routes should be started
3026        for (RouteService routeService : routeServices) {
3027            DefaultRouteStartupOrder order = doPrepareRouteToBeStarted(routeService);
3028            // check for clash before we add it as input
3029            if (checkClash) {
3030                doCheckStartupOrderClash(order, inputs);
3031            }
3032            inputs.put(order.getStartupOrder(), order);
3033        }
3034
3035        // warm up routes before we start them
3036        doWarmUpRoutes(inputs, startConsumer);
3037
3038        if (startConsumer) {
3039            if (resumeConsumer) {
3040                // and now resume the routes
3041                doResumeRouteConsumers(inputs, addingRoutes);
3042            } else {
3043                // and now start the routes
3044                // and check for clash with multiple consumers of the same endpoints which is not allowed
3045                doStartRouteConsumers(inputs, addingRoutes);
3046            }
3047        }
3048
3049        // inputs no longer needed
3050        inputs.clear();
3051    }
3052
3053    /**
3054     * @see #safelyStartRouteServices(boolean,boolean,boolean,boolean,java.util.Collection)
3055     */
3056    protected synchronized void safelyStartRouteServices(boolean forceAutoStart, boolean checkClash, boolean startConsumer,
3057                                                         boolean resumeConsumer, boolean addingRoutes, RouteService... routeServices) throws Exception {
3058        safelyStartRouteServices(checkClash, startConsumer, resumeConsumer, addingRoutes, Arrays.asList(routeServices));
3059    }
3060
3061    private DefaultRouteStartupOrder doPrepareRouteToBeStarted(RouteService routeService) {
3062        // add the inputs from this route service to the list to start afterwards
3063        // should be ordered according to the startup number
3064        Integer startupOrder = routeService.getRouteDefinition().getStartupOrder();
3065        if (startupOrder == null) {
3066            // auto assign a default startup order
3067            startupOrder = defaultRouteStartupOrder++;
3068        }
3069
3070        // create holder object that contains information about this route to be started
3071        Route route = routeService.getRoutes().iterator().next();
3072        return new DefaultRouteStartupOrder(startupOrder, route, routeService);
3073    }
3074
3075    private boolean doCheckStartupOrderClash(DefaultRouteStartupOrder answer, Map<Integer, DefaultRouteStartupOrder> inputs) throws FailedToStartRouteException {
3076        // check for clash by startupOrder id
3077        DefaultRouteStartupOrder other = inputs.get(answer.getStartupOrder());
3078        if (other != null && answer != other) {
3079            String otherId = other.getRoute().getId();
3080            throw new FailedToStartRouteException(answer.getRoute().getId(), "startupOrder clash. Route " + otherId + " already has startupOrder "
3081                + answer.getStartupOrder() + " configured which this route have as well. Please correct startupOrder to be unique among all your routes.");
3082        }
3083        // check in existing already started as well
3084        for (RouteStartupOrder order : routeStartupOrder) {
3085            String otherId = order.getRoute().getId();
3086            if (answer.getRoute().getId().equals(otherId)) {
3087                // its the same route id so skip clash check as its the same route (can happen when using suspend/resume)
3088            } else if (answer.getStartupOrder() == order.getStartupOrder()) {
3089                throw new FailedToStartRouteException(answer.getRoute().getId(), "startupOrder clash. Route " + otherId + " already has startupOrder "
3090                    + answer.getStartupOrder() + " configured which this route have as well. Please correct startupOrder to be unique among all your routes.");
3091            }
3092        }
3093        return true;
3094    }
3095
3096    private void doWarmUpRoutes(Map<Integer, DefaultRouteStartupOrder> inputs, boolean autoStartup) throws Exception {
3097        // now prepare the routes by starting its services before we start the input
3098        for (Map.Entry<Integer, DefaultRouteStartupOrder> entry : inputs.entrySet()) {
3099            // defer starting inputs till later as we want to prepare the routes by starting
3100            // all their processors and child services etc.
3101            // then later we open the floods to Camel by starting the inputs
3102            // what this does is to ensure Camel is more robust on starting routes as all routes
3103            // will then be prepared in time before we start inputs which will consume messages to be routed
3104            RouteService routeService = entry.getValue().getRouteService();
3105            log.debug("Warming up route id: {} having autoStartup={}", routeService.getId(), autoStartup);
3106            routeService.warmUp();
3107        }
3108    }
3109
3110    private void doResumeRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean addingRoutes) throws Exception {
3111        doStartOrResumeRouteConsumers(inputs, true, addingRoutes);
3112    }
3113
3114    private void doStartRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean addingRoutes) throws Exception {
3115        doStartOrResumeRouteConsumers(inputs, false, addingRoutes);
3116    }
3117
3118    private void doStartOrResumeRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean resumeOnly, boolean addingRoute) throws Exception {
3119        List<Endpoint> routeInputs = new ArrayList<Endpoint>();
3120
3121        for (Map.Entry<Integer, DefaultRouteStartupOrder> entry : inputs.entrySet()) {
3122            Integer order = entry.getKey();
3123            Route route = entry.getValue().getRoute();
3124            RouteService routeService = entry.getValue().getRouteService();
3125
3126            // if we are starting camel, then skip routes which are configured to not be auto started
3127            boolean autoStartup = routeService.getRouteDefinition().isAutoStartup(this) && this.isAutoStartup();
3128            if (addingRoute && !autoStartup) {
3129                log.info("Skipping starting of route " + routeService.getId() + " as its configured with autoStartup=false");
3130                continue;
3131            }
3132
3133            // start the service
3134            for (Consumer consumer : routeService.getInputs().values()) {
3135                Endpoint endpoint = consumer.getEndpoint();
3136
3137                // check multiple consumer violation, with the other routes to be started
3138                if (!doCheckMultipleConsumerSupportClash(endpoint, routeInputs)) {
3139                    throw new FailedToStartRouteException(routeService.getId(),
3140                        "Multiple consumers for the same endpoint is not allowed: " + endpoint);
3141                }
3142
3143                // check for multiple consumer violations with existing routes which
3144                // have already been started, or is currently starting
3145                List<Endpoint> existingEndpoints = new ArrayList<Endpoint>();
3146                for (Route existingRoute : getRoutes()) {
3147                    if (route.getId().equals(existingRoute.getId())) {
3148                        // skip ourselves
3149                        continue;
3150                    }
3151                    Endpoint existing = existingRoute.getEndpoint();
3152                    ServiceStatus status = getRouteStatus(existingRoute.getId());
3153                    if (status != null && (status.isStarted() || status.isStarting())) {
3154                        existingEndpoints.add(existing);
3155                    }
3156                }
3157                if (!doCheckMultipleConsumerSupportClash(endpoint, existingEndpoints)) {
3158                    throw new FailedToStartRouteException(routeService.getId(),
3159                            "Multiple consumers for the same endpoint is not allowed: " + endpoint);
3160                }
3161
3162                // start the consumer on the route
3163                log.debug("Route: {} >>> {}", route.getId(), route);
3164                if (resumeOnly) {
3165                    log.debug("Resuming consumer (order: {}) on route: {}", order, route.getId());
3166                } else {
3167                    log.debug("Starting consumer (order: {}) on route: {}", order, route.getId());
3168                }
3169
3170                if (resumeOnly && route.supportsSuspension()) {
3171                    // if we are resuming and the route can be resumed
3172                    ServiceHelper.resumeService(consumer);
3173                    log.info("Route: " + route.getId() + " resumed and consuming from: " + endpoint);
3174                } else {
3175                    // when starting we should invoke the lifecycle strategies
3176                    for (LifecycleStrategy strategy : lifecycleStrategies) {
3177                        strategy.onServiceAdd(this, consumer, route);
3178                    }
3179                    startService(consumer);
3180                    log.info("Route: " + route.getId() + " started and consuming from: " + endpoint);
3181                }
3182
3183                routeInputs.add(endpoint);
3184
3185                // add to the order which they was started, so we know how to stop them in reverse order
3186                // but only add if we haven't already registered it before (we dont want to double add when restarting)
3187                boolean found = false;
3188                for (RouteStartupOrder other : routeStartupOrder) {
3189                    if (other.getRoute().getId().equals(route.getId())) {
3190                        found = true;
3191                        break;
3192                    }
3193                }
3194                if (!found) {
3195                    routeStartupOrder.add(entry.getValue());
3196                }
3197            }
3198
3199            if (resumeOnly) {
3200                routeService.resume();
3201            } else {
3202                // and start the route service (no need to start children as they are already warmed up)
3203                routeService.start(false);
3204            }
3205        }
3206    }
3207
3208    private boolean doCheckMultipleConsumerSupportClash(Endpoint endpoint, List<Endpoint> routeInputs) {
3209        // is multiple consumers supported
3210        boolean multipleConsumersSupported = false;
3211        if (endpoint instanceof MultipleConsumersSupport) {
3212            multipleConsumersSupported = ((MultipleConsumersSupport) endpoint).isMultipleConsumersSupported();
3213        }
3214
3215        if (multipleConsumersSupported) {
3216            // multiple consumer allowed, so return true
3217            return true;
3218        }
3219
3220        // check in progress list
3221        if (routeInputs.contains(endpoint)) {
3222            return false;
3223        }
3224
3225        return true;
3226    }
3227
3228    /**
3229     * Force some lazy initialization to occur upfront before we start any
3230     * components and create routes
3231     */
3232    protected void forceLazyInitialization() {
3233        getRegistry();
3234        getInjector();
3235        getLanguageResolver();
3236        getTypeConverterRegistry();
3237        getTypeConverter();
3238        getRuntimeEndpointRegistry();
3239
3240        if (isTypeConverterStatisticsEnabled() != null) {
3241            getTypeConverterRegistry().getStatistics().setStatisticsEnabled(isTypeConverterStatisticsEnabled());
3242        }
3243    }
3244
3245    /**
3246     * Force clear lazy initialization so they can be re-created on restart
3247     */
3248    protected void forceStopLazyInitialization() {
3249        injector = null;
3250        languageResolver = null;
3251        typeConverterRegistry = null;
3252        typeConverter = null;
3253    }
3254
3255    /**
3256     * Lazily create a default implementation
3257     */
3258    protected TypeConverter createTypeConverter() {
3259        BaseTypeConverterRegistry answer;
3260        if (isLazyLoadTypeConverters()) {
3261            answer = new LazyLoadingTypeConverter(packageScanClassResolver, getInjector(), getDefaultFactoryFinder());
3262        } else {
3263            answer = new DefaultTypeConverter(packageScanClassResolver, getInjector(), getDefaultFactoryFinder());
3264        }
3265        setTypeConverterRegistry(answer);
3266        return answer;
3267    }
3268
3269    /**
3270     * Lazily create a default implementation
3271     */
3272    protected Injector createInjector() {
3273        FactoryFinder finder = getDefaultFactoryFinder();
3274        try {
3275            return (Injector) finder.newInstance("Injector");
3276        } catch (NoFactoryAvailableException e) {
3277            // lets use the default injector
3278            return new DefaultInjector(this);
3279        }
3280    }
3281
3282    /**
3283     * Lazily create a default implementation
3284     */
3285    protected ManagementMBeanAssembler createManagementMBeanAssembler() {
3286        return new DefaultManagementMBeanAssembler(this);
3287    }
3288
3289    /**
3290     * Lazily create a default implementation
3291     */
3292    protected ComponentResolver createComponentResolver() {
3293        return new DefaultComponentResolver();
3294    }
3295
3296    /**
3297     * Lazily create a default implementation
3298     */
3299    protected Registry createRegistry() {
3300        JndiRegistry jndi = new JndiRegistry();
3301        try {
3302            // getContext() will force setting up JNDI
3303            jndi.getContext();
3304            return jndi;
3305        } catch (Throwable e) {
3306            log.debug("Cannot create javax.naming.InitialContext due " + e.getMessage() + ". Will fallback and use SimpleRegistry instead. This exception is ignored.", e);
3307            return new SimpleRegistry();
3308        }
3309    }
3310
3311    /**
3312     * A pluggable strategy to allow an endpoint to be created without requiring
3313     * a component to be its factory, such as for looking up the URI inside some
3314     * {@link Registry}
3315     *
3316     * @param uri the uri for the endpoint to be created
3317     * @return the newly created endpoint or null if it could not be resolved
3318     */
3319    protected Endpoint createEndpoint(String uri) {
3320        Object value = getRegistry().lookupByName(uri);
3321        if (value instanceof Endpoint) {
3322            return (Endpoint) value;
3323        } else if (value instanceof Processor) {
3324            return new ProcessorEndpoint(uri, this, (Processor) value);
3325        } else if (value != null) {
3326            return convertBeanToEndpoint(uri, value);
3327        }
3328        return null;
3329    }
3330
3331    /**
3332     * Strategy method for attempting to convert the bean from a {@link Registry} to an endpoint using
3333     * some kind of transformation or wrapper
3334     *
3335     * @param uri  the uri for the endpoint (and name in the registry)
3336     * @param bean the bean to be converted to an endpoint, which will be not null
3337     * @return a new endpoint
3338     */
3339    protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
3340        throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
3341                + " could not be converted to an Endpoint");
3342    }
3343
3344    /**
3345     * Should we start newly added routes?
3346     */
3347    protected boolean shouldStartRoutes() {
3348        return isStarted() && !isStarting();
3349    }
3350
3351    /**
3352     * Gets the properties component in use.
3353     * Returns {@code null} if no properties component is in use.
3354     */
3355    protected PropertiesComponent getPropertiesComponent() {
3356        return propertiesComponent;
3357    }
3358
3359    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
3360        this.dataFormats = dataFormats;
3361    }
3362
3363    public Map<String, DataFormatDefinition> getDataFormats() {
3364        return dataFormats;
3365    }
3366
3367    public Map<String, String> getProperties() {
3368        return properties;
3369    }
3370
3371    public void setProperties(Map<String, String> properties) {
3372        this.properties = properties;
3373    }
3374
3375    public FactoryFinder getDefaultFactoryFinder() {
3376        if (defaultFactoryFinder == null) {
3377            defaultFactoryFinder = factoryFinderResolver.resolveDefaultFactoryFinder(getClassResolver());
3378        }
3379        return defaultFactoryFinder;
3380    }
3381
3382    public void setFactoryFinderResolver(FactoryFinderResolver resolver) {
3383        this.factoryFinderResolver = resolver;
3384    }
3385
3386    public FactoryFinder getFactoryFinder(String path) throws NoFactoryAvailableException {
3387        synchronized (factories) {
3388            FactoryFinder answer = factories.get(path);
3389            if (answer == null) {
3390                answer = factoryFinderResolver.resolveFactoryFinder(getClassResolver(), path);
3391                factories.put(path, answer);
3392            }
3393            return answer;
3394        }
3395    }
3396
3397    public ClassResolver getClassResolver() {
3398        return classResolver;
3399    }
3400
3401    public void setClassResolver(ClassResolver classResolver) {
3402        this.classResolver = classResolver;
3403    }
3404
3405    public PackageScanClassResolver getPackageScanClassResolver() {
3406        return packageScanClassResolver;
3407    }
3408
3409    public void setPackageScanClassResolver(PackageScanClassResolver packageScanClassResolver) {
3410        this.packageScanClassResolver = packageScanClassResolver;
3411    }
3412
3413    public List<String> getComponentNames() {
3414        synchronized (components) {
3415            List<String> answer = new ArrayList<String>();
3416            for (String name : components.keySet()) {
3417                answer.add(name);
3418            }
3419            return answer;
3420        }
3421    }
3422
3423    public List<String> getLanguageNames() {
3424        synchronized (languages) {
3425            List<String> answer = new ArrayList<String>();
3426            for (String name : languages.keySet()) {
3427                answer.add(name);
3428            }
3429            return answer;
3430        }
3431    }
3432
3433    public ModelJAXBContextFactory getModelJAXBContextFactory() {
3434        if (modelJAXBContextFactory == null) {
3435            modelJAXBContextFactory = new DefaultModelJAXBContextFactory();
3436        }
3437        return modelJAXBContextFactory;
3438    }
3439
3440    public void setModelJAXBContextFactory(final ModelJAXBContextFactory modelJAXBContextFactory) {
3441        this.modelJAXBContextFactory = modelJAXBContextFactory;
3442    }
3443
3444    public NodeIdFactory getNodeIdFactory() {
3445        return nodeIdFactory;
3446    }
3447
3448    public void setNodeIdFactory(NodeIdFactory idFactory) {
3449        this.nodeIdFactory = idFactory;
3450    }
3451
3452    public ManagementStrategy getManagementStrategy() {
3453        return managementStrategy;
3454    }
3455
3456    public void setManagementStrategy(ManagementStrategy managementStrategy) {
3457        this.managementStrategy = managementStrategy;
3458    }
3459
3460    public InterceptStrategy getDefaultTracer() {
3461        if (defaultTracer == null) {
3462            defaultTracer = new Tracer();
3463        }
3464        return defaultTracer;
3465    }
3466
3467    public void setDefaultTracer(InterceptStrategy tracer) {
3468        this.defaultTracer = tracer;
3469    }
3470
3471    public InterceptStrategy getDefaultBacklogTracer() {
3472        if (defaultBacklogTracer == null) {
3473            defaultBacklogTracer = BacklogTracer.createTracer(this);
3474        }
3475        return defaultBacklogTracer;
3476    }
3477
3478    public void setDefaultBacklogTracer(InterceptStrategy backlogTracer) {
3479        this.defaultBacklogTracer = backlogTracer;
3480    }
3481
3482    public InterceptStrategy getDefaultBacklogDebugger() {
3483        if (defaultBacklogDebugger == null) {
3484            defaultBacklogDebugger = new BacklogDebugger(this);
3485        }
3486        return defaultBacklogDebugger;
3487    }
3488
3489    public void setDefaultBacklogDebugger(InterceptStrategy defaultBacklogDebugger) {
3490        this.defaultBacklogDebugger = defaultBacklogDebugger;
3491    }
3492
3493    public void disableJMX() {
3494        if (isStarting() || isStarted()) {
3495            throw new IllegalStateException("Disabling JMX can only be done when CamelContext has not been started");
3496        }
3497        managementStrategy = new DefaultManagementStrategy(this);
3498        // must clear lifecycle strategies as we add DefaultManagementLifecycleStrategy by default for JMX support
3499        lifecycleStrategies.clear();
3500    }
3501
3502    public InflightRepository getInflightRepository() {
3503        return inflightRepository;
3504    }
3505
3506    public void setInflightRepository(InflightRepository repository) {
3507        this.inflightRepository = repository;
3508    }
3509
3510    public AsyncProcessorAwaitManager getAsyncProcessorAwaitManager() {
3511        return asyncProcessorAwaitManager;
3512    }
3513
3514    public void setAsyncProcessorAwaitManager(AsyncProcessorAwaitManager asyncProcessorAwaitManager) {
3515        this.asyncProcessorAwaitManager = asyncProcessorAwaitManager;
3516    }
3517
3518    public void setAutoStartup(Boolean autoStartup) {
3519        this.autoStartup = autoStartup;
3520    }
3521
3522    public Boolean isAutoStartup() {
3523        return autoStartup != null && autoStartup;
3524    }
3525
3526    @Deprecated
3527    public Boolean isLazyLoadTypeConverters() {
3528        return lazyLoadTypeConverters != null && lazyLoadTypeConverters;
3529    }
3530
3531    @Deprecated
3532    public void setLazyLoadTypeConverters(Boolean lazyLoadTypeConverters) {
3533        this.lazyLoadTypeConverters = lazyLoadTypeConverters;
3534    }
3535
3536    public Boolean isTypeConverterStatisticsEnabled() {
3537        return typeConverterStatisticsEnabled != null && typeConverterStatisticsEnabled;
3538    }
3539
3540    public void setTypeConverterStatisticsEnabled(Boolean typeConverterStatisticsEnabled) {
3541        this.typeConverterStatisticsEnabled = typeConverterStatisticsEnabled;
3542    }
3543
3544    public Boolean isUseMDCLogging() {
3545        return useMDCLogging != null && useMDCLogging;
3546    }
3547
3548    public void setUseMDCLogging(Boolean useMDCLogging) {
3549        this.useMDCLogging = useMDCLogging;
3550    }
3551
3552    public Boolean isUseBreadcrumb() {
3553        return useBreadcrumb != null && useBreadcrumb;
3554    }
3555
3556    public void setUseBreadcrumb(Boolean useBreadcrumb) {
3557        this.useBreadcrumb = useBreadcrumb;
3558    }
3559
3560    public ClassLoader getApplicationContextClassLoader() {
3561        return applicationContextClassLoader;
3562    }
3563
3564    public void setApplicationContextClassLoader(ClassLoader classLoader) {
3565        applicationContextClassLoader = classLoader;
3566    }
3567
3568    public DataFormatResolver getDataFormatResolver() {
3569        return dataFormatResolver;
3570    }
3571
3572    public void setDataFormatResolver(DataFormatResolver dataFormatResolver) {
3573        this.dataFormatResolver = dataFormatResolver;
3574    }
3575
3576    public DataFormat resolveDataFormat(String name) {
3577        DataFormat answer = dataFormatResolver.resolveDataFormat(name, this);
3578
3579        // inject CamelContext if aware
3580        if (answer != null && answer instanceof CamelContextAware) {
3581            ((CamelContextAware) answer).setCamelContext(this);
3582        }
3583
3584        return answer;
3585    }
3586
3587    public DataFormatDefinition resolveDataFormatDefinition(String name) {
3588        // lookup type and create the data format from it
3589        DataFormatDefinition type = lookup(this, name, DataFormatDefinition.class);
3590        if (type == null && getDataFormats() != null) {
3591            type = getDataFormats().get(name);
3592        }
3593        return type;
3594    }
3595
3596    private static <T> T lookup(CamelContext context, String ref, Class<T> type) {
3597        try {
3598            return context.getRegistry().lookupByNameAndType(ref, type);
3599        } catch (Exception e) {
3600            // need to ignore not same type and return it as null
3601            return null;
3602        }
3603    }
3604
3605    /**
3606     * @deprecated use {@link org.apache.camel.util.CamelContextHelper#lookupPropertiesComponent(org.apache.camel.CamelContext, boolean)}
3607     */
3608    @Deprecated
3609    protected Component lookupPropertiesComponent() {
3610        return CamelContextHelper.lookupPropertiesComponent(this, false);
3611    }
3612
3613    public ShutdownStrategy getShutdownStrategy() {
3614        return shutdownStrategy;
3615    }
3616
3617    public void setShutdownStrategy(ShutdownStrategy shutdownStrategy) {
3618        this.shutdownStrategy = shutdownStrategy;
3619    }
3620
3621    public ShutdownRoute getShutdownRoute() {
3622        return shutdownRoute;
3623    }
3624
3625    public void setShutdownRoute(ShutdownRoute shutdownRoute) {
3626        this.shutdownRoute = shutdownRoute;
3627    }
3628
3629    public ShutdownRunningTask getShutdownRunningTask() {
3630        return shutdownRunningTask;
3631    }
3632
3633    public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
3634        this.shutdownRunningTask = shutdownRunningTask;
3635    }
3636
3637    public void setAllowUseOriginalMessage(Boolean allowUseOriginalMessage) {
3638        this.allowUseOriginalMessage = allowUseOriginalMessage;
3639    }
3640
3641    public Boolean isAllowUseOriginalMessage() {
3642        return allowUseOriginalMessage != null && allowUseOriginalMessage;
3643    }
3644
3645    public ExecutorServiceManager getExecutorServiceManager() {
3646        return this.executorServiceManager;
3647    }
3648
3649    @Deprecated
3650    public org.apache.camel.spi.ExecutorServiceStrategy getExecutorServiceStrategy() {
3651        // its okay to create a new instance as its stateless, and just delegate
3652        // ExecutorServiceManager which is the new API
3653        return new DefaultExecutorServiceStrategy(this);
3654    }
3655
3656    public void setExecutorServiceManager(ExecutorServiceManager executorServiceManager) {
3657        this.executorServiceManager = executorServiceManager;
3658    }
3659
3660    public ProcessorFactory getProcessorFactory() {
3661        return processorFactory;
3662    }
3663
3664    public void setProcessorFactory(ProcessorFactory processorFactory) {
3665        this.processorFactory = processorFactory;
3666    }
3667
3668    public Debugger getDebugger() {
3669        return debugger;
3670    }
3671
3672    public void setDebugger(Debugger debugger) {
3673        this.debugger = debugger;
3674    }
3675
3676    public UuidGenerator getUuidGenerator() {
3677        return uuidGenerator;
3678    }
3679
3680    public void setUuidGenerator(UuidGenerator uuidGenerator) {
3681        this.uuidGenerator = uuidGenerator;
3682    }
3683
3684    public StreamCachingStrategy getStreamCachingStrategy() {
3685        if (streamCachingStrategy == null) {
3686            streamCachingStrategy = new DefaultStreamCachingStrategy();
3687        }
3688        return streamCachingStrategy;
3689    }
3690
3691    public void setStreamCachingStrategy(StreamCachingStrategy streamCachingStrategy) {
3692        this.streamCachingStrategy = streamCachingStrategy;
3693    }
3694
3695    public RestRegistry getRestRegistry() {
3696        return restRegistry;
3697    }
3698
3699    public void setRestRegistry(RestRegistry restRegistry) {
3700        this.restRegistry = restRegistry;
3701    }
3702
3703    @Override
3704    public String getProperty(String name) {
3705        String value = getProperties().get(name);
3706        if (ObjectHelper.isNotEmpty(value)) {
3707            try {
3708                value = resolvePropertyPlaceholders(value);
3709            } catch (Exception e) {
3710                throw new RuntimeCamelException("Error getting property: " + name, e);
3711            }
3712        }
3713        return value;
3714    }
3715
3716    protected Map<String, RouteService> getRouteServices() {
3717        return routeServices;
3718    }
3719
3720    protected ManagementStrategy createManagementStrategy() {
3721        return new ManagementStrategyFactory().create(this, disableJMX || Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED));
3722    }
3723
3724    /**
3725     * Reset context counter to a preset value. Mostly used for tests to ensure a predictable getName()
3726     *
3727     * @param value new value for the context counter
3728     */
3729    public static void setContextCounter(int value) {
3730        DefaultCamelContextNameStrategy.setCounter(value);
3731        DefaultManagementNameStrategy.setCounter(value);
3732    }
3733
3734    private static UuidGenerator createDefaultUuidGenerator() {
3735        if (System.getProperty("com.google.appengine.runtime.environment") != null) {
3736            // either "Production" or "Development"
3737            return new JavaUuidGenerator();
3738        } else {
3739            return new ActiveMQUuidGenerator();
3740        }
3741    }
3742
3743    @Override
3744    public String toString() {
3745        return "CamelContext(" + getName() + ")";
3746    }
3747}