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.Comparator;
027import java.util.Date;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.Iterator;
031import java.util.LinkedHashMap;
032import java.util.LinkedHashSet;
033import java.util.List;
034import java.util.Map;
035import java.util.Properties;
036import java.util.Set;
037import java.util.TreeMap;
038import java.util.concurrent.Callable;
039import java.util.concurrent.ConcurrentHashMap;
040import java.util.concurrent.CopyOnWriteArrayList;
041import java.util.concurrent.ScheduledExecutorService;
042import java.util.concurrent.TimeUnit;
043import java.util.concurrent.atomic.AtomicBoolean;
044import java.util.concurrent.atomic.AtomicInteger;
045import java.util.function.Function;
046
047import javax.management.MalformedObjectNameException;
048import javax.management.ObjectName;
049import javax.naming.Context;
050import javax.xml.bind.JAXBContext;
051import javax.xml.bind.Unmarshaller;
052
053import org.apache.camel.CamelContext;
054import org.apache.camel.CamelContextAware;
055import org.apache.camel.Component;
056import org.apache.camel.Consumer;
057import org.apache.camel.ConsumerTemplate;
058import org.apache.camel.Endpoint;
059import org.apache.camel.ErrorHandlerFactory;
060import org.apache.camel.ExtendedStartupListener;
061import org.apache.camel.FailedToStartRouteException;
062import org.apache.camel.FluentProducerTemplate;
063import org.apache.camel.IsSingleton;
064import org.apache.camel.MultipleConsumersSupport;
065import org.apache.camel.NamedNode;
066import org.apache.camel.NoFactoryAvailableException;
067import org.apache.camel.NoSuchEndpointException;
068import org.apache.camel.PollingConsumer;
069import org.apache.camel.Processor;
070import org.apache.camel.Producer;
071import org.apache.camel.ProducerTemplate;
072import org.apache.camel.ResolveEndpointFailedException;
073import org.apache.camel.Route;
074import org.apache.camel.RoutesBuilder;
075import org.apache.camel.RuntimeCamelException;
076import org.apache.camel.Service;
077import org.apache.camel.ServiceStatus;
078import org.apache.camel.ShutdownRoute;
079import org.apache.camel.ShutdownRunningTask;
080import org.apache.camel.StartupListener;
081import org.apache.camel.StatefulService;
082import org.apache.camel.Suspendable;
083import org.apache.camel.SuspendableService;
084import org.apache.camel.TypeConverter;
085import org.apache.camel.VetoCamelContextStartException;
086import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
087import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
088import org.apache.camel.api.management.mbean.ManagedRouteMBean;
089import org.apache.camel.builder.DefaultFluentProducerTemplate;
090import org.apache.camel.builder.ErrorHandlerBuilder;
091import org.apache.camel.builder.ErrorHandlerBuilderSupport;
092import org.apache.camel.component.properties.PropertiesComponent;
093import org.apache.camel.health.HealthCheckRegistry;
094import org.apache.camel.impl.converter.BaseTypeConverterRegistry;
095import org.apache.camel.impl.converter.DefaultTypeConverter;
096import org.apache.camel.impl.converter.LazyLoadingTypeConverter;
097import org.apache.camel.impl.health.DefaultHealthCheckRegistry;
098import org.apache.camel.impl.transformer.TransformerKey;
099import org.apache.camel.impl.validator.ValidatorKey;
100import org.apache.camel.management.DefaultManagementMBeanAssembler;
101import org.apache.camel.management.DefaultManagementStrategy;
102import org.apache.camel.management.JmxSystemPropertyKeys;
103import org.apache.camel.management.ManagementStrategyFactory;
104import org.apache.camel.model.DataFormatDefinition;
105import org.apache.camel.model.FromDefinition;
106import org.apache.camel.model.HystrixConfigurationDefinition;
107import org.apache.camel.model.ModelCamelContext;
108import org.apache.camel.model.ModelHelper;
109import org.apache.camel.model.ProcessorDefinition;
110import org.apache.camel.model.ProcessorDefinitionHelper;
111import org.apache.camel.model.RouteDefinition;
112import org.apache.camel.model.RouteDefinitionHelper;
113import org.apache.camel.model.RoutesDefinition;
114import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
115import org.apache.camel.model.rest.RestDefinition;
116import org.apache.camel.model.rest.RestsDefinition;
117import org.apache.camel.model.transformer.TransformerDefinition;
118import org.apache.camel.model.validator.ValidatorDefinition;
119import org.apache.camel.processor.interceptor.BacklogDebugger;
120import org.apache.camel.processor.interceptor.BacklogTracer;
121import org.apache.camel.processor.interceptor.Debug;
122import org.apache.camel.processor.interceptor.Delayer;
123import org.apache.camel.processor.interceptor.HandleFault;
124import org.apache.camel.processor.interceptor.StreamCaching;
125import org.apache.camel.processor.interceptor.Tracer;
126import org.apache.camel.runtimecatalog.DefaultRuntimeCamelCatalog;
127import org.apache.camel.runtimecatalog.RuntimeCamelCatalog;
128import org.apache.camel.spi.AsyncProcessorAwaitManager;
129import org.apache.camel.spi.CamelContextNameStrategy;
130import org.apache.camel.spi.ClassResolver;
131import org.apache.camel.spi.ComponentResolver;
132import org.apache.camel.spi.Container;
133import org.apache.camel.spi.DataFormat;
134import org.apache.camel.spi.DataFormatResolver;
135import org.apache.camel.spi.DataType;
136import org.apache.camel.spi.Debugger;
137import org.apache.camel.spi.EndpointRegistry;
138import org.apache.camel.spi.EndpointStrategy;
139import org.apache.camel.spi.EventNotifier;
140import org.apache.camel.spi.ExecutorServiceManager;
141import org.apache.camel.spi.FactoryFinder;
142import org.apache.camel.spi.FactoryFinderResolver;
143import org.apache.camel.spi.HeadersMapFactory;
144import org.apache.camel.spi.InflightRepository;
145import org.apache.camel.spi.Injector;
146import org.apache.camel.spi.InterceptStrategy;
147import org.apache.camel.spi.Language;
148import org.apache.camel.spi.LanguageResolver;
149import org.apache.camel.spi.LifecycleStrategy;
150import org.apache.camel.spi.LogListener;
151import org.apache.camel.spi.ManagementMBeanAssembler;
152import org.apache.camel.spi.ManagementNameStrategy;
153import org.apache.camel.spi.ManagementStrategy;
154import org.apache.camel.spi.MessageHistoryFactory;
155import org.apache.camel.spi.ModelJAXBContextFactory;
156import org.apache.camel.spi.NodeIdFactory;
157import org.apache.camel.spi.PackageScanClassResolver;
158import org.apache.camel.spi.ProcessorFactory;
159import org.apache.camel.spi.Registry;
160import org.apache.camel.spi.ReloadStrategy;
161import org.apache.camel.spi.RestConfiguration;
162import org.apache.camel.spi.RestRegistry;
163import org.apache.camel.spi.RouteContext;
164import org.apache.camel.spi.RouteController;
165import org.apache.camel.spi.RouteError;
166import org.apache.camel.spi.RoutePolicyFactory;
167import org.apache.camel.spi.RouteStartupOrder;
168import org.apache.camel.spi.RuntimeEndpointRegistry;
169import org.apache.camel.spi.ServicePool;
170import org.apache.camel.spi.ShutdownStrategy;
171import org.apache.camel.spi.StreamCachingStrategy;
172import org.apache.camel.spi.Transformer;
173import org.apache.camel.spi.TransformerRegistry;
174import org.apache.camel.spi.TypeConverterRegistry;
175import org.apache.camel.spi.UnitOfWorkFactory;
176import org.apache.camel.spi.UuidGenerator;
177import org.apache.camel.spi.Validator;
178import org.apache.camel.spi.ValidatorRegistry;
179import org.apache.camel.support.ServiceSupport;
180import org.apache.camel.util.CamelContextHelper;
181import org.apache.camel.util.CollectionStringBuffer;
182import org.apache.camel.util.EndpointHelper;
183import org.apache.camel.util.EventHelper;
184import org.apache.camel.util.IOHelper;
185import org.apache.camel.util.IntrospectionSupport;
186import org.apache.camel.util.JsonSchemaHelper;
187import org.apache.camel.util.LoadPropertiesException;
188import org.apache.camel.util.ObjectHelper;
189import org.apache.camel.util.OrderedComparator;
190import org.apache.camel.util.ServiceHelper;
191import org.apache.camel.util.StopWatch;
192import org.apache.camel.util.StringHelper;
193import org.apache.camel.util.StringQuoteHelper;
194import org.apache.camel.util.TimeUtils;
195import org.apache.camel.util.URISupport;
196import org.apache.camel.util.jsse.SSLContextParameters;
197import org.slf4j.Logger;
198import org.slf4j.LoggerFactory;
199import org.slf4j.MDC;
200
201import static org.apache.camel.impl.MDCUnitOfWork.MDC_CAMEL_CONTEXT_ID;
202
203/**
204 * Represents the context used to configure routes and the policies to use.
205 *
206 * @version
207 */
208@SuppressWarnings("deprecation")
209public class DefaultCamelContext extends ServiceSupport implements ModelCamelContext, Suspendable {
210    private final Logger log = LoggerFactory.getLogger(getClass());
211    private final AtomicBoolean vetoStated = new AtomicBoolean();
212    private JAXBContext jaxbContext;
213    private CamelContextNameStrategy nameStrategy = createCamelContextNameStrategy();
214    private ManagementNameStrategy managementNameStrategy = createManagementNameStrategy();
215    private String managementName;
216    private ClassLoader applicationContextClassLoader;
217    private EndpointRegistry<EndpointKey> endpoints;
218    private final AtomicInteger endpointKeyCounter = new AtomicInteger();
219    private final List<EndpointStrategy> endpointStrategies = new ArrayList<>();
220    private final Map<String, Component> components = new ConcurrentHashMap<>();
221    private final Set<Route> routes = new LinkedHashSet<>();
222    private final List<Service> servicesToStop = new CopyOnWriteArrayList<>();
223    private final List<StartupListener> startupListeners = new CopyOnWriteArrayList<>();
224    private final DeferServiceStartupListener deferStartupListener = new DeferServiceStartupListener();
225    private TypeConverter typeConverter;
226    private TypeConverterRegistry typeConverterRegistry;
227    private Injector injector;
228    private ComponentResolver componentResolver;
229    private boolean autoCreateComponents = true;
230    private LanguageResolver languageResolver;
231    private final Map<String, Language> languages = new HashMap<>();
232    private Registry registry;
233    private List<LifecycleStrategy> lifecycleStrategies = new CopyOnWriteArrayList<>();
234    private ManagementStrategy managementStrategy;
235    private ManagementMBeanAssembler managementMBeanAssembler;
236    private final List<RouteDefinition> routeDefinitions = new ArrayList<>();
237    private final List<RestDefinition> restDefinitions = new ArrayList<>();
238    private Map<String, RestConfiguration> restConfigurations = new ConcurrentHashMap<>();
239    private Map<String, ServiceCallConfigurationDefinition> serviceCallConfigurations = new ConcurrentHashMap<>();
240    private Map<String, HystrixConfigurationDefinition> hystrixConfigurations = new ConcurrentHashMap<>();
241    private RestRegistry restRegistry = new DefaultRestRegistry();
242    private List<InterceptStrategy> interceptStrategies = new ArrayList<>();
243    private List<RoutePolicyFactory> routePolicyFactories = new ArrayList<>();
244    private Set<LogListener> logListeners = new LinkedHashSet<>();
245    private HeadersMapFactory headersMapFactory = createHeadersMapFactory();
246    // special flags to control the first startup which can are special
247    private volatile boolean firstStartDone;
248    private volatile boolean doNotStartRoutesOnFirstStart;
249    private final ThreadLocal<Boolean> isStartingRoutes = new ThreadLocal<>();
250    private final ThreadLocal<Boolean> isSetupRoutes = new ThreadLocal<>();
251    private Boolean autoStartup = Boolean.TRUE;
252    private Boolean trace = Boolean.FALSE;
253    private Boolean messageHistory = Boolean.TRUE;
254    private Boolean logMask = Boolean.FALSE;
255    private Boolean logExhaustedMessageBody = Boolean.FALSE;
256    private Boolean streamCache = Boolean.FALSE;
257    private Boolean handleFault = Boolean.FALSE;
258    private Boolean disableJMX = Boolean.FALSE;
259    private Boolean lazyLoadTypeConverters = Boolean.FALSE;
260    private Boolean loadTypeConverters = Boolean.TRUE;
261    private Boolean typeConverterStatisticsEnabled = Boolean.FALSE;
262    private Boolean useMDCLogging = Boolean.FALSE;
263    private Boolean useDataType = Boolean.FALSE;
264    private Boolean useBreadcrumb = Boolean.TRUE;
265    private Boolean allowUseOriginalMessage = Boolean.FALSE;
266    private Long delay;
267    private ErrorHandlerFactory errorHandlerBuilder;
268    private final Object errorHandlerExecutorServiceLock = new Object();
269    private ScheduledExecutorService errorHandlerExecutorService;
270    private Map<String, DataFormatDefinition> dataFormats = new HashMap<>();
271    private DataFormatResolver dataFormatResolver;
272    private Map<String, String> globalOptions = new HashMap<>();
273    private FactoryFinderResolver factoryFinderResolver;
274    private FactoryFinder defaultFactoryFinder;
275    private PropertiesComponent propertiesComponent;
276    private StreamCachingStrategy streamCachingStrategy;
277    private final Map<String, FactoryFinder> factories = new HashMap<>();
278    private final Map<String, RouteService> routeServices = new LinkedHashMap<>();
279    private final Map<String, RouteService> suspendedRouteServices = new LinkedHashMap<>();
280    private ClassResolver classResolver = createClassResolver();
281    private PackageScanClassResolver packageScanClassResolver;
282    // we use a capacity of 100 per endpoint, so for the same endpoint we have at most 100 producers in the pool
283    // so if we have 6 endpoints in the pool, we can have 6 x 100 producers in total
284    private ServicePool<Endpoint, Producer> producerServicePool = createProducerServicePool();
285    private ServicePool<Endpoint, PollingConsumer> pollingConsumerServicePool = createPollingConsumerServicePool();
286    private NodeIdFactory nodeIdFactory = createNodeIdFactory();
287    private ProcessorFactory processorFactory = createProcessorFactory();
288    private MessageHistoryFactory messageHistoryFactory = createMessageHistoryFactory();
289    private InterceptStrategy defaultTracer;
290    private InterceptStrategy defaultBacklogTracer;
291    private InterceptStrategy defaultBacklogDebugger;
292    private InflightRepository inflightRepository = createInflightRepository();
293    private AsyncProcessorAwaitManager asyncProcessorAwaitManager = createAsyncProcessorAwaitManager();
294    private RuntimeEndpointRegistry runtimeEndpointRegistry;
295    private final List<RouteStartupOrder> routeStartupOrder = new ArrayList<>();
296    // start auto assigning route ids using numbering 1000 and upwards
297    private int defaultRouteStartupOrder = 1000;
298    private ShutdownStrategy shutdownStrategy = createShutdownStrategy();
299    private ShutdownRoute shutdownRoute = ShutdownRoute.Default;
300    private ShutdownRunningTask shutdownRunningTask = ShutdownRunningTask.CompleteCurrentTaskOnly;
301    private ExecutorServiceManager executorServiceManager;
302    private Debugger debugger;
303    private UuidGenerator uuidGenerator = createDefaultUuidGenerator();
304    private UnitOfWorkFactory unitOfWorkFactory = createUnitOfWorkFactory();
305    private final StopWatch stopWatch = new StopWatch(false);
306    private Date startDate;
307    private ModelJAXBContextFactory modelJAXBContextFactory;
308    private List<TransformerDefinition> transformers = new ArrayList<>();
309    private TransformerRegistry<TransformerKey> transformerRegistry;
310    private List<ValidatorDefinition> validators = new ArrayList<>();
311    private ValidatorRegistry<ValidatorKey> validatorRegistry;
312    private ReloadStrategy reloadStrategy;
313    private final RuntimeCamelCatalog runtimeCamelCatalog = createRuntimeCamelCatalog();
314    private SSLContextParameters sslContextParameters;
315    private final ThreadLocal<Set<String>> componentsInCreation = new ThreadLocal<Set<String>>() {
316        @Override
317        public Set<String> initialValue() {
318            return new HashSet<>();
319        }
320    };
321    private RouteController routeController = createRouteController();
322    private HealthCheckRegistry healthCheckRegistry = createHealthCheckRegistry();
323
324
325    /**
326     * Creates the {@link CamelContext} using {@link JndiRegistry} as registry,
327     * but will silently fallback and use {@link SimpleRegistry} if JNDI cannot be used.
328     * <p/>
329     * Use one of the other constructors to force use an explicit registry / JNDI.
330     */
331    public DefaultCamelContext() {
332        this.executorServiceManager = createExecutorServiceManager();
333
334        // create a provisional (temporary) endpoint registry at first since end users may access endpoints before CamelContext is started
335        // we will later transfer the endpoints to the actual DefaultEndpointRegistry later, but we do this to starup Camel faster.
336        this.endpoints = new ProvisionalEndpointRegistry();
337
338        // add the defer service startup listener
339        this.startupListeners.add(deferStartupListener);
340
341        packageScanClassResolver = createPackageScanClassResolver();
342
343        // setup management strategy first since end users may use it to add event notifiers
344        // using the management strategy before the CamelContext has been started
345        this.managementStrategy = createManagementStrategy();
346        this.managementMBeanAssembler = createManagementMBeanAssembler();
347
348        // Call all registered trackers with this context
349        // Note, this may use a partially constructed object
350        CamelContextTrackerRegistry.INSTANCE.contextCreated(this);
351    }
352
353    /**
354     * Creates the {@link CamelContext} using the given JNDI context as the registry
355     *
356     * @param jndiContext the JNDI context
357     */
358    public DefaultCamelContext(Context jndiContext) {
359        this();
360        setJndiContext(jndiContext);
361    }
362
363    /**
364     * Creates the {@link CamelContext} using the given registry
365     *
366     * @param registry the registry
367     */
368    public DefaultCamelContext(Registry registry) {
369        this();
370        setRegistry(registry);
371    }
372
373    public <T extends CamelContext> T adapt(Class<T> type) {
374        return type.cast(this);
375    }
376
377    public boolean isVetoStarted() {
378        return vetoStated.get();
379    }
380
381    public String getName() {
382        return getNameStrategy().getName();
383    }
384
385    /**
386     * Sets the name of the this context.
387     *
388     * @param name the name
389     */
390    public void setName(String name) {
391        // use an explicit name strategy since an explicit name was provided to be used
392        this.nameStrategy = new ExplicitCamelContextNameStrategy(name);
393    }
394
395    public CamelContextNameStrategy getNameStrategy() {
396        return nameStrategy;
397    }
398
399    public void setNameStrategy(CamelContextNameStrategy nameStrategy) {
400        this.nameStrategy = nameStrategy;
401    }
402
403    public ManagementNameStrategy getManagementNameStrategy() {
404        return managementNameStrategy;
405    }
406
407    public void setManagementNameStrategy(ManagementNameStrategy managementNameStrategy) {
408        this.managementNameStrategy = managementNameStrategy;
409    }
410
411    public String getManagementName() {
412        return managementName;
413    }
414
415    public void setManagementName(String managementName) {
416        this.managementName = managementName;
417    }
418
419    public Component hasComponent(String componentName) {
420        return components.get(componentName);
421    }
422
423    public void addComponent(String componentName, final Component component) {
424        ObjectHelper.notNull(component, "component");
425        component.setCamelContext(this);
426        Component oldValue = components.putIfAbsent(componentName, component);
427        if (oldValue != null) {
428            throw new IllegalArgumentException("Cannot add component as its already previously added: " + componentName);
429        }
430        postInitComponent(componentName, component);
431    }
432
433    private void postInitComponent(String componentName, final Component component) {
434        for (LifecycleStrategy strategy : lifecycleStrategies) {
435            strategy.onComponentAdd(componentName, component);
436        }
437
438        // keep reference to properties component up to date
439        if (component instanceof PropertiesComponent && "properties".equals(componentName)) {
440            propertiesComponent = (PropertiesComponent) component;
441        }
442    }
443
444    public Component getComponent(String name) {
445        return getComponent(name, autoCreateComponents, true);
446    }
447
448    public Component getComponent(String name, boolean autoCreateComponents) {
449        return getComponent(name, autoCreateComponents, true);
450    }
451
452    public Component getComponent(String name, boolean autoCreateComponents, boolean autoStart) {
453        // Check if the named component is already being created, that would mean
454        // that the initComponent has triggered a new getComponent
455        if (componentsInCreation.get().contains(name)) {
456            throw new IllegalStateException("Circular dependency detected, the component " + name + " is already being created");
457        }
458
459        try {
460            // Flag used to mark a component of being created.
461            final AtomicBoolean created = new AtomicBoolean(false);
462
463            // atomic operation to get/create a component. Avoid global locks.
464            final Component component = components.computeIfAbsent(name, new Function<String, Component>() {
465                @Override
466                public Component apply(String comp) {
467                    created.set(true);
468                    return DefaultCamelContext.this.initComponent(name, autoCreateComponents);
469                }
470            });
471
472            // Start the component after its creation as if it is a component proxy
473            // that creates/start a delegated component, we may end up in a deadlock
474            if (component != null && created.get() && autoStart && (isStarted() || isStarting())) {
475                // If the component is looked up after the context is started,
476                // lets start it up.
477                if (component instanceof Service) {
478                    startService((Service)component);
479                }
480            }
481
482            return  component;
483        } catch (Exception e) {
484            throw new RuntimeCamelException("Cannot auto create component: " + name, e);
485        } finally {
486            // remove the reference to the component being created
487            componentsInCreation.get().remove(name);
488        }
489    }
490    
491    /**
492     * Function to initialize a component and auto start. Returns null if the autoCreateComponents is disabled
493     */
494    private Component initComponent(String name, boolean autoCreateComponents) {
495        Component component = null;
496        if (autoCreateComponents) {
497            try {
498                if (log.isDebugEnabled()) {
499                    log.debug("Using ComponentResolver: {} to resolve component with name: {}", getComponentResolver(), name);
500                }
501
502                // Mark the component as being created so we can detect circular
503                // requests.
504                //
505                // In spring apps, the component resolver may trigger a new getComponent
506                // because of the underlying bean factory and as the endpoints are
507                // registered as singleton, the spring factory creates the bean
508                // and then check the type so the getComponent is always triggered.
509                //
510                // Simple circular dependency:
511                //
512                //   <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
513                //     <route>
514                //       <from id="twitter" uri="twitter://timeline/home?type=polling"/>
515                //       <log message="Got ${body}"/>
516                //     </route>
517                //   </camelContext>
518                //
519                // Complex circular dependency:
520                //
521                //   <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
522                //     <route>
523                //       <from id="log" uri="seda:test"/>
524                //       <to id="seda" uri="log:test"/>
525                //     </route>
526                //   </camelContext>
527                //
528                // This would freeze the app (lock or infinite loop).
529                //
530                // See https://issues.apache.org/jira/browse/CAMEL-11225
531                componentsInCreation.get().add(name);
532
533                component = getComponentResolver().resolveComponent(name, this);
534                if (component != null) {
535                    component.setCamelContext(this);
536                    postInitComponent(name, component);
537                }
538            } catch (Exception e) {
539                throw new RuntimeCamelException("Cannot auto create component: " + name, e);
540            }
541        }
542        return component;
543    }
544
545    public <T extends Component> T getComponent(String name, Class<T> componentType) {
546        Component component = getComponent(name);
547        if (componentType.isInstance(component)) {
548            return componentType.cast(component);
549        } else {
550            String message;
551            if (component == null) {
552                message = "Did not find component given by the name: " + name;
553            } else {
554                message = "Found component of type: " + component.getClass() + " instead of expected: " + componentType;
555            }
556            throw new IllegalArgumentException(message);
557        }
558    }
559
560    public Component resolveComponent(String name) {
561        Component answer = hasComponent(name);
562        if (answer == null) {
563            try {
564                answer = getComponentResolver().resolveComponent(name, this);
565            } catch (Exception e) {
566                throw new RuntimeCamelException("Cannot resolve component: " + name, e);
567            }
568        }
569        return answer;
570    }
571
572    public Component removeComponent(String componentName) {
573        Component oldComponent = components.remove(componentName);
574        if (oldComponent != null) {
575            try {
576                stopServices(oldComponent);
577            } catch (Exception e) {
578                log.warn("Error stopping component " + oldComponent + ". This exception will be ignored.", e);
579            }
580            for (LifecycleStrategy strategy : lifecycleStrategies) {
581                strategy.onComponentRemove(componentName, oldComponent);
582            }
583        }
584        // keep reference to properties component up to date
585        if (oldComponent != null && "properties".equals(componentName)) {
586            propertiesComponent = null;
587        }
588        return oldComponent;
589    }
590
591    // Endpoint Management Methods
592    // -----------------------------------------------------------------------
593
594    public EndpointRegistry<EndpointKey> getEndpointRegistry() {
595        return endpoints;
596    }
597
598    public Collection<Endpoint> getEndpoints() {
599        return new ArrayList<>(endpoints.values());
600    }
601
602    public Map<String, Endpoint> getEndpointMap() {
603        Map<String, Endpoint> answer = new TreeMap<>();
604        for (Map.Entry<EndpointKey, Endpoint> entry : endpoints.entrySet()) {
605            answer.put(entry.getKey().get(), entry.getValue());
606        }
607        return answer;
608    }
609
610    public Endpoint hasEndpoint(String uri) {
611        return endpoints.get(getEndpointKey(uri));
612    }
613
614    public Endpoint addEndpoint(String uri, Endpoint endpoint) throws Exception {
615        Endpoint oldEndpoint;
616
617        startService(endpoint);
618        oldEndpoint = endpoints.remove(getEndpointKey(uri));
619        for (LifecycleStrategy strategy : lifecycleStrategies) {
620            strategy.onEndpointAdd(endpoint);
621        }
622        addEndpointToRegistry(uri, endpoint);
623        if (oldEndpoint != null) {
624            stopServices(oldEndpoint);
625        }
626
627        return oldEndpoint;
628    }
629
630    public void removeEndpoint(Endpoint endpoint) throws Exception {
631        removeEndpoints(endpoint.getEndpointUri());
632    }
633
634    public Collection<Endpoint> removeEndpoints(String uri) throws Exception {
635        Collection<Endpoint> answer = new ArrayList<>();
636        Endpoint oldEndpoint = endpoints.remove(getEndpointKey(uri));
637        if (oldEndpoint != null) {
638            answer.add(oldEndpoint);
639            stopServices(oldEndpoint);
640        } else {
641            for (Map.Entry<EndpointKey, Endpoint> entry : endpoints.entrySet()) {
642                oldEndpoint = entry.getValue();
643                if (EndpointHelper.matchEndpoint(this, oldEndpoint.getEndpointUri(), uri)) {
644                    try {
645                        stopServices(oldEndpoint);
646                    } catch (Exception e) {
647                        log.warn("Error stopping endpoint " + oldEndpoint + ". This exception will be ignored.", e);
648                    }
649                    answer.add(oldEndpoint);
650                    endpoints.remove(entry.getKey());
651                }
652            }
653        }
654
655        // notify lifecycle its being removed
656        for (Endpoint endpoint : answer) {
657            for (LifecycleStrategy strategy : lifecycleStrategies) {
658                strategy.onEndpointRemove(endpoint);
659            }
660        }
661
662        return answer;
663    }
664
665    public Endpoint getEndpoint(String uri) {
666        StringHelper.notEmpty(uri, "uri");
667
668        log.trace("Getting endpoint with uri: {}", uri);
669
670        // in case path has property placeholders then try to let property component resolve those
671        try {
672            uri = resolvePropertyPlaceholders(uri);
673        } catch (Exception e) {
674            throw new ResolveEndpointFailedException(uri, e);
675        }
676
677        final String rawUri = uri;
678
679        // normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order
680        uri = normalizeEndpointUri(uri);
681
682        log.trace("Getting endpoint with raw uri: {}, normalized uri: {}", rawUri, uri);
683
684        Endpoint answer;
685        String scheme = null;
686        // use optimized method to get the endpoint uri
687        EndpointKey key = getEndpointKeyPreNormalized(uri);
688        answer = endpoints.get(key);
689        if (answer == null) {
690            try {
691                // Use the URI prefix to find the component.
692                String splitURI[] = StringHelper.splitOnCharacter(uri, ":", 2);
693                if (splitURI[1] != null) {
694                    scheme = splitURI[0];
695                    log.trace("Endpoint uri: {} is from component with name: {}", uri, scheme);
696                    Component component = getComponent(scheme);
697
698                    // Ask the component to resolve the endpoint.
699                    if (component != null) {
700                        log.trace("Creating endpoint from uri: {} using component: {}", uri, component);
701
702                        // Have the component create the endpoint if it can.
703                        if (component.useRawUri()) {
704                            answer = component.createEndpoint(rawUri);
705                        } else {
706                            answer = component.createEndpoint(uri);
707                        }
708
709                        if (answer != null && log.isDebugEnabled()) {
710                            log.debug("{} converted to endpoint: {} by component: {}", URISupport.sanitizeUri(uri), answer, component);
711                        }
712                    }
713                }
714
715                if (answer == null) {
716                    // no component then try in registry and elsewhere
717                    answer = createEndpoint(uri);
718                    log.trace("No component to create endpoint from uri: {} fallback lookup in registry -> {}", uri, answer);
719                }
720
721                if (answer == null && splitURI[1] == null) {
722                    // the uri has no context-path which is rare and it was not referring to an endpoint in the registry
723                    // so try to see if it can be created by a component
724
725                    int pos = uri.indexOf('?');
726                    String componentName = pos > 0 ? uri.substring(0, pos) : uri;
727
728                    Component component = getComponent(componentName);
729
730                    // Ask the component to resolve the endpoint.
731                    if (component != null) {
732                        log.trace("Creating endpoint from uri: {} using component: {}", uri, component);
733
734                        // Have the component create the endpoint if it can.
735                        if (component.useRawUri()) {
736                            answer = component.createEndpoint(rawUri);
737                        } else {
738                            answer = component.createEndpoint(uri);
739                        }
740
741                        if (answer != null && log.isDebugEnabled()) {
742                            log.debug("{} converted to endpoint: {} by component: {}", URISupport.sanitizeUri(uri), answer, component);
743                        }
744                    }
745
746                }
747
748                if (answer != null) {
749                    addService(answer);
750                    answer = addEndpointToRegistry(uri, answer);
751                }
752            } catch (Exception e) {
753                throw new ResolveEndpointFailedException(uri, e);
754            }
755        }
756
757        // unknown scheme
758        if (answer == null && scheme != null) {
759            throw new ResolveEndpointFailedException(uri, "No component found with scheme: " + scheme);
760        }
761
762        return answer;
763    }
764
765    public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
766        Endpoint endpoint = getEndpoint(name);
767        if (endpoint == null) {
768            throw new NoSuchEndpointException(name);
769        }
770        if (endpoint instanceof InterceptSendToEndpoint) {
771            endpoint = ((InterceptSendToEndpoint) endpoint).getDelegate();
772        }
773        if (endpointType.isInstance(endpoint)) {
774            return endpointType.cast(endpoint);
775        } else {
776            throw new IllegalArgumentException("The endpoint is not of type: " + endpointType
777                + " but is: " + endpoint.getClass().getCanonicalName());
778        }
779    }
780
781    public void addRegisterEndpointCallback(EndpointStrategy strategy) {
782        if (!endpointStrategies.contains(strategy)) {
783            // let it be invoked for already registered endpoints so it can catch-up.
784            endpointStrategies.add(strategy);
785            for (Endpoint endpoint : getEndpoints()) {
786                Endpoint newEndpoint = strategy.registerEndpoint(endpoint.getEndpointUri(), endpoint);
787                if (newEndpoint != null) {
788                    // put will replace existing endpoint with the new endpoint
789                    endpoints.put(getEndpointKey(endpoint.getEndpointUri()), newEndpoint);
790                }
791            }
792        }
793    }
794
795    /**
796     * Strategy to add the given endpoint to the internal endpoint registry
797     *
798     * @param uri      uri of the endpoint
799     * @param endpoint the endpoint to add
800     * @return the added endpoint
801     */
802    protected Endpoint addEndpointToRegistry(String uri, Endpoint endpoint) {
803        StringHelper.notEmpty(uri, "uri");
804        ObjectHelper.notNull(endpoint, "endpoint");
805
806        // if there is endpoint strategies, then use the endpoints they return
807        // as this allows to intercept endpoints etc.
808        for (EndpointStrategy strategy : endpointStrategies) {
809            endpoint = strategy.registerEndpoint(uri, endpoint);
810        }
811        endpoints.put(getEndpointKey(uri, endpoint), endpoint);
812        return endpoint;
813    }
814
815    /**
816     * Normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order.
817     *
818     * @param uri the uri
819     * @return normalized uri
820     * @throws ResolveEndpointFailedException if uri cannot be normalized
821     */
822    protected static String normalizeEndpointUri(String uri) {
823        try {
824            uri = URISupport.normalizeUri(uri);
825        } catch (Exception e) {
826            throw new ResolveEndpointFailedException(uri, e);
827        }
828        return uri;
829    }
830
831    /**
832     * Gets the endpoint key to use for lookup or whe adding endpoints to the {@link DefaultEndpointRegistry}
833     *
834     * @param uri the endpoint uri
835     * @return the key
836     */
837    protected EndpointKey getEndpointKey(String uri) {
838        return new EndpointKey(uri);
839    }
840
841    /**
842     * Gets the endpoint key to use for lookup or whe adding endpoints to the {@link DefaultEndpointRegistry}
843     *
844     * @param uri the endpoint uri which is pre normalized
845     * @return the key
846     */
847    protected EndpointKey getEndpointKeyPreNormalized(String uri) {
848        return new EndpointKey(uri, true);
849    }
850
851    /**
852     * Gets the endpoint key to use for lookup or whe adding endpoints to the {@link DefaultEndpointRegistry}
853     *
854     * @param uri      the endpoint uri
855     * @param endpoint the endpoint
856     * @return the key
857     */
858    protected EndpointKey getEndpointKey(String uri, Endpoint endpoint) {
859        if (endpoint != null && !endpoint.isSingleton()) {
860            int counter = endpointKeyCounter.incrementAndGet();
861            return new EndpointKey(uri + ":" + counter);
862        } else {
863            return new EndpointKey(uri);
864        }
865    }
866
867    // Route Management Methods
868    // -----------------------------------------------------------------------
869
870    @Override
871    public void setRouteController(RouteController routeController) {
872        this.routeController = routeController;
873        this.routeController.setCamelContext(this);
874    }
875
876    @Override
877    public RouteController getRouteController() {
878        return routeController;
879    }
880
881    public List<RouteStartupOrder> getRouteStartupOrder() {
882        return routeStartupOrder;
883    }
884
885    public List<Route> getRoutes() {
886        // lets return a copy of the collection as objects are removed later when services are stopped
887        if (routes.isEmpty()) {
888            return Collections.emptyList();
889        } else {
890            synchronized (routes) {
891                return new ArrayList<>(routes);
892            }
893        }
894    }
895
896    public Route getRoute(String id) {
897        if (id != null) {
898            for (Route route : getRoutes()) {
899                if (route.getId().equals(id)) {
900                    return route;
901                }
902            }
903        }
904        return null;
905    }
906
907    public Processor getProcessor(String id) {
908        for (Route route : getRoutes()) {
909            List<Processor> list = route.filter(id);
910            if (list.size() == 1) {
911                return list.get(0);
912            }
913        }
914        return null;
915    }
916
917    public <T extends Processor> T getProcessor(String id, Class<T> type) {
918        Processor answer = getProcessor(id);
919        if (answer != null) {
920            return type.cast(answer);
921        }
922        return null;
923    }
924
925    public <T extends ManagedProcessorMBean> T getManagedProcessor(String id, Class<T> type) {
926        // jmx must be enabled
927        if (getManagementStrategy().getManagementAgent() == null) {
928            return null;
929        }
930
931        Processor processor = getProcessor(id);
932        ProcessorDefinition def = getProcessorDefinition(id);
933
934        // processor may be null if its anonymous inner class or as lambda
935        if (def != null) {
936            try {
937                ObjectName on = getManagementStrategy().getManagementNamingStrategy().getObjectNameForProcessor(this, processor, def);
938                return getManagementStrategy().getManagementAgent().newProxyClient(on, type);
939            } catch (MalformedObjectNameException e) {
940                throw ObjectHelper.wrapRuntimeCamelException(e);
941            }
942        }
943
944        return null;
945    }
946
947    public <T extends ManagedRouteMBean> T getManagedRoute(String routeId, Class<T> type) {
948        // jmx must be enabled
949        if (getManagementStrategy().getManagementAgent() == null) {
950            return null;
951        }
952
953        Route route = getRoute(routeId);
954
955        if (route != null) {
956            try {
957                ObjectName on = getManagementStrategy().getManagementNamingStrategy().getObjectNameForRoute(route);
958                return getManagementStrategy().getManagementAgent().newProxyClient(on, type);
959            } catch (MalformedObjectNameException e) {
960                throw ObjectHelper.wrapRuntimeCamelException(e);
961            }
962        }
963
964        return null;
965    }
966
967    public ManagedCamelContextMBean getManagedCamelContext() {
968        // jmx must be enabled
969        if (getManagementStrategy().getManagementAgent() == null) {
970            return null;
971        }
972
973        try {
974            ObjectName on = getManagementStrategy().getManagementNamingStrategy().getObjectNameForCamelContext(this);
975            return getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedCamelContextMBean.class);
976        } catch (MalformedObjectNameException e) {
977            throw ObjectHelper.wrapRuntimeCamelException(e);
978        }
979    }
980
981    public ProcessorDefinition getProcessorDefinition(String id) {
982        for (RouteDefinition route : getRouteDefinitions()) {
983            Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
984            while (it.hasNext()) {
985                ProcessorDefinition proc = it.next();
986                if (id.equals(proc.getId())) {
987                    return proc;
988                }
989            }
990        }
991        return null;
992    }
993
994    public <T extends ProcessorDefinition> T getProcessorDefinition(String id, Class<T> type) {
995        ProcessorDefinition answer = getProcessorDefinition(id);
996        if (answer != null) {
997            return type.cast(answer);
998        }
999        return null;
1000    }
1001
1002    @Deprecated
1003    public void setRoutes(List<Route> routes) {
1004        throw new UnsupportedOperationException("Overriding existing routes is not supported yet, use addRouteCollection instead");
1005    }
1006
1007    void removeRouteCollection(Collection<Route> routes) {
1008        synchronized (this.routes) {
1009            this.routes.removeAll(routes);
1010        }
1011    }
1012
1013    void addRouteCollection(Collection<Route> routes) throws Exception {
1014        synchronized (this.routes) {
1015            this.routes.addAll(routes);
1016        }
1017    }
1018
1019    public void addRoutes(final RoutesBuilder builder) throws Exception {
1020        log.debug("Adding routes from builder: {}", builder);
1021        doWithDefinedClassLoader(new Callable<Void>() {
1022            @Override
1023            public Void call() throws Exception {
1024                builder.addRoutesToCamelContext(DefaultCamelContext.this);
1025                return null;
1026            }
1027        });
1028    }
1029
1030    public synchronized RoutesDefinition loadRoutesDefinition(InputStream is) throws Exception {
1031        return ModelHelper.loadRoutesDefinition(this, is);
1032    }
1033
1034    public synchronized RestsDefinition loadRestsDefinition(InputStream is) throws Exception {
1035        // load routes using JAXB
1036        if (jaxbContext == null) {
1037            // must use classloader from CamelContext to have JAXB working
1038            jaxbContext = getModelJAXBContextFactory().newJAXBContext();
1039        }
1040
1041        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
1042        Object result = unmarshaller.unmarshal(is);
1043
1044        if (result == null) {
1045            throw new IOException("Cannot unmarshal to rests using JAXB from input stream: " + is);
1046        }
1047
1048        // can either be routes or a single route
1049        RestsDefinition answer;
1050        if (result instanceof RestDefinition) {
1051            RestDefinition rest = (RestDefinition) result;
1052            answer = new RestsDefinition();
1053            answer.getRests().add(rest);
1054        } else if (result instanceof RestsDefinition) {
1055            answer = (RestsDefinition) result;
1056        } else {
1057            throw new IllegalArgumentException("Unmarshalled object is an unsupported type: " + ObjectHelper.className(result) + " -> " + result);
1058        }
1059
1060        return answer;
1061    }
1062
1063    public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
1064        if (routeDefinitions == null || routeDefinitions.isEmpty()) {
1065            return;
1066        }
1067        for (RouteDefinition routeDefinition : routeDefinitions) {
1068            removeRouteDefinition(routeDefinition);
1069        }
1070        this.routeDefinitions.addAll(routeDefinitions);
1071        if (shouldStartRoutes()) {
1072            startRouteDefinitions(routeDefinitions);
1073        }
1074    }
1075
1076    public void addRouteDefinition(RouteDefinition routeDefinition) throws Exception {
1077        addRouteDefinitions(Arrays.asList(routeDefinition));
1078    }
1079
1080    /**
1081     * Removes the route definition with the given key.
1082     *
1083     * @return true if one or more routes was removed
1084     */
1085    protected boolean removeRouteDefinition(String key) {
1086        boolean answer = false;
1087        Iterator<RouteDefinition> iter = routeDefinitions.iterator();
1088        while (iter.hasNext()) {
1089            RouteDefinition route = iter.next();
1090            if (route.idOrCreate(nodeIdFactory).equals(key)) {
1091                iter.remove();
1092                answer = true;
1093            }
1094        }
1095        return answer;
1096    }
1097
1098    public synchronized void removeRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception {
1099        for (RouteDefinition routeDefinition : routeDefinitions) {
1100            removeRouteDefinition(routeDefinition);
1101        }
1102    }
1103
1104    public synchronized void removeRouteDefinition(RouteDefinition routeDefinition) throws Exception {
1105        RouteDefinition toBeRemoved = routeDefinition;
1106        String id = routeDefinition.getId();
1107        if (id != null) {
1108            // remove existing route
1109            stopRoute(id);
1110            removeRoute(id);
1111            toBeRemoved = getRouteDefinition(id);
1112        }
1113        this.routeDefinitions.remove(toBeRemoved);
1114    }
1115
1116    public ServiceStatus getRouteStatus(String key) {
1117        RouteService routeService = routeServices.get(key);
1118        if (routeService != null) {
1119            return routeService.getStatus();
1120        }
1121        return null;
1122    }
1123
1124    public void startRoute(RouteDefinition route) throws Exception {
1125        // assign ids to the routes and validate that the id's is all unique
1126        RouteDefinitionHelper.forceAssignIds(this, routeDefinitions);
1127        String duplicate = RouteDefinitionHelper.validateUniqueIds(route, routeDefinitions);
1128        if (duplicate != null) {
1129            throw new FailedToStartRouteException(route.getId(), "duplicate id detected: " + duplicate + ". Please correct ids to be unique among all your routes.");
1130        }
1131
1132        // indicate we are staring the route using this thread so
1133        // we are able to query this if needed
1134        isStartingRoutes.set(true);
1135        try {
1136            // must ensure route is prepared, before we can start it
1137            route.prepare(this);
1138
1139            List<Route> routes = new ArrayList<>();
1140            List<RouteContext> routeContexts = route.addRoutes(this, routes);
1141            RouteService routeService = new RouteService(this, route, routeContexts, routes);
1142            startRouteService(routeService, true);
1143        } finally {
1144            // we are done staring routes
1145            isStartingRoutes.remove();
1146        }
1147    }
1148
1149    public boolean isStartingRoutes() {
1150        Boolean answer = isStartingRoutes.get();
1151        return answer != null && answer;
1152    }
1153
1154    public boolean isSetupRoutes() {
1155        Boolean answer = isSetupRoutes.get();
1156        return answer != null && answer;
1157    }
1158
1159    public void stopRoute(RouteDefinition route) throws Exception {
1160        stopRoute(route.idOrCreate(nodeIdFactory));
1161    }
1162
1163    public void startAllRoutes() throws Exception {
1164        doStartOrResumeRoutes(routeServices, true, true, false, false);
1165    }
1166
1167    public synchronized void startRoute(String routeId) throws Exception {
1168        DefaultRouteError.reset(this, routeId);
1169
1170        RouteService routeService = routeServices.get(routeId);
1171        if (routeService != null) {
1172            try {
1173                startRouteService(routeService, false);
1174            } catch (Exception e) {
1175                DefaultRouteError.set(this, routeId, RouteError.Phase.START, e);
1176                throw e;
1177            }
1178        }
1179    }
1180
1181    public synchronized void resumeRoute(String routeId) throws Exception {
1182        DefaultRouteError.reset(this, routeId);
1183
1184        try {
1185            if (!routeSupportsSuspension(routeId)) {
1186                // start route if suspension is not supported
1187                startRoute(routeId);
1188                return;
1189            }
1190
1191            RouteService routeService = routeServices.get(routeId);
1192            if (routeService != null) {
1193                resumeRouteService(routeService);
1194                // must resume the route as well
1195                Route route = getRoute(routeId);
1196                ServiceHelper.resumeService(route);
1197            }
1198        } catch (Exception e) {
1199            DefaultRouteError.set(this, routeId, RouteError.Phase.RESUME, e);
1200            throw e;
1201        }
1202    }
1203
1204    public synchronized boolean stopRoute(String routeId, long timeout, TimeUnit timeUnit, boolean abortAfterTimeout) throws Exception {
1205        DefaultRouteError.reset(this, routeId);
1206
1207        RouteService routeService = routeServices.get(routeId);
1208        if (routeService != null) {
1209            try {
1210                RouteStartupOrder route = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1211
1212                boolean completed = getShutdownStrategy().shutdown(this, route, timeout, timeUnit, abortAfterTimeout);
1213                if (completed) {
1214                    // must stop route service as well
1215                    stopRouteService(routeService, false);
1216                } else {
1217                    // shutdown was aborted, make sure route is re-started properly
1218                    startRouteService(routeService, false);
1219                }
1220                return completed;
1221            } catch (Exception e) {
1222                DefaultRouteError.set(this, routeId, RouteError.Phase.STOP, e);
1223                throw e;
1224            }
1225        }
1226
1227        return false;
1228    }
1229
1230    public synchronized void stopRoute(String routeId) throws Exception {
1231        DefaultRouteError.reset(this, routeId);
1232
1233        RouteService routeService = routeServices.get(routeId);
1234        if (routeService != null) {
1235            try {
1236                List<RouteStartupOrder> routes = new ArrayList<>(1);
1237                RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1238                routes.add(order);
1239
1240                getShutdownStrategy().shutdown(this, routes);
1241                // must stop route service as well
1242                stopRouteService(routeService, false);
1243            } catch (Exception e) {
1244                DefaultRouteError.set(this, routeId, RouteError.Phase.STOP, e);
1245                throw e;
1246            }
1247        }
1248    }
1249
1250    public synchronized void stopRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
1251        DefaultRouteError.reset(this, routeId);
1252
1253        RouteService routeService = routeServices.get(routeId);
1254        if (routeService != null) {
1255            try {
1256                List<RouteStartupOrder> routes = new ArrayList<>(1);
1257                RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1258                routes.add(order);
1259
1260                getShutdownStrategy().shutdown(this, routes, timeout, timeUnit);
1261                // must stop route service as well
1262                stopRouteService(routeService, false);
1263            } catch (Exception e) {
1264                DefaultRouteError.set(this, routeId, RouteError.Phase.STOP, e);
1265                throw e;
1266            }
1267        }
1268    }
1269
1270    public synchronized void shutdownRoute(String routeId) throws Exception {
1271        DefaultRouteError.reset(this, routeId);
1272
1273        RouteService routeService = routeServices.get(routeId);
1274        if (routeService != null) {
1275            try {
1276                List<RouteStartupOrder> routes = new ArrayList<>(1);
1277                RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1278                routes.add(order);
1279
1280                getShutdownStrategy().shutdown(this, routes);
1281                // must stop route service as well (and remove the routes from management)
1282                stopRouteService(routeService, true);
1283            } catch (Exception e) {
1284                DefaultRouteError.set(this, routeId, RouteError.Phase.SHUTDOWN, e);
1285                throw e;
1286            }
1287        }
1288    }
1289
1290    public synchronized void shutdownRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
1291        DefaultRouteError.reset(this, routeId);
1292
1293        RouteService routeService = routeServices.get(routeId);
1294        if (routeService != null) {
1295            try {
1296                List<RouteStartupOrder> routes = new ArrayList<>(1);
1297                RouteStartupOrder order = new DefaultRouteStartupOrder(1, routeService.getRoutes().iterator().next(), routeService);
1298                routes.add(order);
1299
1300                getShutdownStrategy().shutdown(this, routes, timeout, timeUnit);
1301                // must stop route service as well (and remove the routes from management)
1302                stopRouteService(routeService, true);
1303            } catch (Exception e) {
1304                DefaultRouteError.set(this, routeId, RouteError.Phase.SHUTDOWN, e);
1305                throw e;
1306            }
1307        }
1308    }
1309
1310    public synchronized boolean removeRoute(String routeId) throws Exception {
1311        DefaultRouteError.reset(this, routeId);
1312
1313        // remove the route from ErrorHandlerBuilder if possible
1314        if (getErrorHandlerBuilder() instanceof ErrorHandlerBuilderSupport) {
1315            ErrorHandlerBuilderSupport builder = (ErrorHandlerBuilderSupport)getErrorHandlerBuilder();
1316            builder.removeOnExceptionList(routeId);
1317        }
1318
1319        // gather a map of all the endpoints in use by the routes, so we can known if a given endpoints is in use
1320        // by one or more routes, when we remove the route
1321        Map<String, Set<Endpoint>> endpointsInUse = new HashMap<>();
1322        for (Map.Entry<String, RouteService> entry : routeServices.entrySet()) {
1323            endpointsInUse.put(entry.getKey(), entry.getValue().gatherEndpoints());
1324        }
1325
1326        RouteService routeService = routeServices.get(routeId);
1327        if (routeService != null) {
1328            if (getRouteStatus(routeId).isStopped()) {
1329                try {
1330                    routeService.setRemovingRoutes(true);
1331                    shutdownRouteService(routeService);
1332                    removeRouteDefinition(routeId);
1333                    routeServices.remove(routeId);
1334                    // remove route from startup order as well, as it was removed
1335                    Iterator<RouteStartupOrder> it = routeStartupOrder.iterator();
1336                    while (it.hasNext()) {
1337                        RouteStartupOrder order = it.next();
1338                        if (order.getRoute().getId().equals(routeId)) {
1339                            it.remove();
1340                        }
1341                    }
1342
1343                    // from the route which we have removed, then remove all its private endpoints
1344                    // (eg the endpoints which are not in use by other routes)
1345                    Set<Endpoint> toRemove = new LinkedHashSet<>();
1346                    for (Endpoint endpoint : endpointsInUse.get(routeId)) {
1347                        // how many times is the endpoint in use
1348                        int count = 0;
1349                        for (Set<Endpoint> endpoints : endpointsInUse.values()) {
1350                            if (endpoints.contains(endpoint)) {
1351                                count++;
1352                            }
1353                        }
1354                        // notice we will count ourselves so if there is only 1 then its safe to remove
1355                        if (count <= 1) {
1356                            toRemove.add(endpoint);
1357                        }
1358                    }
1359                    for (Endpoint endpoint : toRemove) {
1360                        log.debug("Removing: {} which was only in use by route: {}", endpoint, routeId);
1361                        removeEndpoint(endpoint);
1362                    }
1363                } catch  (Exception e) {
1364                    DefaultRouteError.set(this, routeId, RouteError.Phase.REMOVE, e);
1365                    throw e;
1366                }
1367
1368                return true;
1369            } else {
1370                return false;
1371            }
1372        }
1373        return false;
1374    }
1375
1376    public synchronized void suspendRoute(String routeId) throws Exception {
1377        try {
1378            DefaultRouteError.reset(this, routeId);
1379
1380            if (!routeSupportsSuspension(routeId)) {
1381                // stop if we suspend is not supported
1382                stopRoute(routeId);
1383                return;
1384            }
1385
1386            RouteService routeService = routeServices.get(routeId);
1387            if (routeService != null) {
1388                List<RouteStartupOrder> routes = new ArrayList<>(1);
1389                Route route = routeService.getRoutes().iterator().next();
1390                RouteStartupOrder order = new DefaultRouteStartupOrder(1, route, routeService);
1391                routes.add(order);
1392
1393                getShutdownStrategy().suspend(this, routes);
1394                // must suspend route service as well
1395                suspendRouteService(routeService);
1396                // must suspend the route as well
1397                if (route instanceof SuspendableService) {
1398                    ((SuspendableService) route).suspend();
1399                }
1400            }
1401        } catch (Exception e) {
1402            DefaultRouteError.set(this, routeId, RouteError.Phase.SUSPEND, e);
1403            throw e;
1404        }
1405    }
1406
1407    public synchronized void suspendRoute(String routeId, long timeout, TimeUnit timeUnit) throws Exception {
1408        DefaultRouteError.reset(this, routeId);
1409
1410        try {
1411            if (!routeSupportsSuspension(routeId)) {
1412                stopRoute(routeId, timeout, timeUnit);
1413                return;
1414            }
1415
1416            RouteService routeService = routeServices.get(routeId);
1417            if (routeService != null) {
1418                List<RouteStartupOrder> routes = new ArrayList<>(1);
1419                Route route = routeService.getRoutes().iterator().next();
1420                RouteStartupOrder order = new DefaultRouteStartupOrder(1, route, routeService);
1421                routes.add(order);
1422
1423                getShutdownStrategy().suspend(this, routes, timeout, timeUnit);
1424                // must suspend route service as well
1425                suspendRouteService(routeService);
1426                // must suspend the route as well
1427                if (route instanceof SuspendableService) {
1428                    ((SuspendableService) route).suspend();
1429                }
1430            }
1431        } catch (Exception e) {
1432            DefaultRouteError.set(this, routeId, RouteError.Phase.SUSPEND, e);
1433            throw e;
1434        }
1435    }
1436
1437    public void addService(Object object) throws Exception {
1438        addService(object, true);
1439    }
1440
1441    public void addService(Object object, boolean stopOnShutdown) throws Exception {
1442        doAddService(object, stopOnShutdown, false);
1443    }
1444
1445    @Override
1446    public void addService(Object object, boolean stopOnShutdown, boolean forceStart) throws Exception {
1447        doAddService(object, stopOnShutdown, forceStart);
1448    }
1449
1450    private void doAddService(Object object, boolean stopOnShutdown, boolean forceStart) throws Exception {
1451
1452        // inject CamelContext
1453        if (object instanceof CamelContextAware) {
1454            CamelContextAware aware = (CamelContextAware) object;
1455            aware.setCamelContext(this);
1456        }
1457
1458        if (object instanceof Service) {
1459            Service service = (Service) object;
1460
1461            for (LifecycleStrategy strategy : lifecycleStrategies) {
1462                if (service instanceof Endpoint) {
1463                    // use specialized endpoint add
1464                    strategy.onEndpointAdd((Endpoint) service);
1465                } else {
1466                    strategy.onServiceAdd(this, service, null);
1467                }
1468            }
1469
1470            if (!forceStart) {
1471                // now start the service (and defer starting if CamelContext is starting up itself)
1472                deferStartService(object, stopOnShutdown);
1473            } else {
1474                // only add to services to close if its a singleton
1475                // otherwise we could for example end up with a lot of prototype scope endpoints
1476                boolean singleton = true; // assume singleton by default
1477                if (object instanceof IsSingleton) {
1478                    singleton = ((IsSingleton) service).isSingleton();
1479                }
1480                // do not add endpoints as they have their own list
1481                if (singleton && !(service instanceof Endpoint)) {
1482                    // only add to list of services to stop if its not already there
1483                    if (stopOnShutdown && !hasService(service)) {
1484                        servicesToStop.add(service);
1485                    }
1486                }
1487                ServiceHelper.startService(service);
1488            }
1489        }
1490    }
1491
1492    public boolean removeService(Object object) throws Exception {
1493        if (object instanceof Endpoint) {
1494            removeEndpoint((Endpoint) object);
1495            return true;
1496        }
1497        if (object instanceof Service) {
1498            Service service = (Service) object;
1499            for (LifecycleStrategy strategy : lifecycleStrategies) {
1500                strategy.onServiceRemove(this, service, null);
1501            }
1502            return servicesToStop.remove(service);
1503        }
1504        return false;
1505    }
1506
1507    public boolean hasService(Object object) {
1508        if (object instanceof Service) {
1509            Service service = (Service) object;
1510            return servicesToStop.contains(service);
1511        }
1512        return false;
1513    }
1514
1515    @Override
1516    public <T> T hasService(Class<T> type) {
1517        for (Service service : servicesToStop) {
1518            if (type.isInstance(service)) {
1519                return type.cast(service);
1520            }
1521        }
1522        return null;
1523    }
1524
1525    @Override
1526    public <T> Set<T> hasServices(Class<T> type) {
1527        Set<T> set = new HashSet<>();
1528        for (Service service : servicesToStop) {
1529            if (type.isInstance(service)) {
1530                set.add((T) service);
1531            }
1532        }
1533        return set;
1534    }
1535
1536    public void deferStartService(Object object, boolean stopOnShutdown) throws Exception {
1537        if (object instanceof Service) {
1538            Service service = (Service) object;
1539
1540            // only add to services to close if its a singleton
1541            // otherwise we could for example end up with a lot of prototype scope endpoints
1542            boolean singleton = true; // assume singleton by default
1543            if (object instanceof IsSingleton) {
1544                singleton = ((IsSingleton) service).isSingleton();
1545            }
1546            // do not add endpoints as they have their own list
1547            if (singleton && !(service instanceof Endpoint)) {
1548                // only add to list of services to stop if its not already there
1549                if (stopOnShutdown && !hasService(service)) {
1550                    servicesToStop.add(service);
1551                }
1552            }
1553            // are we already started?
1554            if (isStarted()) {
1555                ServiceHelper.startService(service);
1556            } else {
1557                deferStartupListener.addService(service);
1558            }
1559        }
1560    }
1561
1562    public void addStartupListener(StartupListener listener) throws Exception {
1563        // either add to listener so we can invoke then later when CamelContext has been started
1564        // or invoke the callback right now
1565        if (isStarted()) {
1566            listener.onCamelContextStarted(this, true);
1567        } else {
1568            startupListeners.add(listener);
1569        }
1570    }
1571
1572    public String resolveComponentDefaultName(String javaType) {
1573        // special for some components
1574        // TODO: ActiveMQ 5.11 will include this out of the box, so not needed when its released
1575        if ("org.apache.activemq.camel.component.ActiveMQComponent".equals(javaType)) {
1576            return "jms";
1577        }
1578
1579        // try to find the component by its java type from the in-use components
1580        if (javaType != null) {
1581            // find all the components which will include the default component name
1582            try {
1583                Map<String, Properties> all = CamelContextHelper.findComponents(this);
1584                for (Map.Entry<String, Properties> entry : all.entrySet()) {
1585                    String fqn = (String) entry.getValue().get("class");
1586                    if (javaType.equals(fqn)) {
1587                        // is there component docs for that name?
1588                        String name = entry.getKey();
1589                        String json = getComponentParameterJsonSchema(name);
1590                        if (json != null) {
1591                            return name;
1592                        }
1593                    }
1594                }
1595            } catch (Exception e) {
1596                // ignore
1597                return null;
1598            }
1599        }
1600
1601        // could not find a component with that name
1602        return null;
1603    }
1604
1605    public Map<String, Properties> findComponents() throws LoadPropertiesException, IOException {
1606        return CamelContextHelper.findComponents(this);
1607    }
1608
1609    public Map<String, Properties> findEips() throws LoadPropertiesException, IOException {
1610        return CamelContextHelper.findEips(this);
1611    }
1612
1613    public String getComponentDocumentation(String componentName) throws IOException {
1614        return null;
1615    }
1616
1617    public String getComponentParameterJsonSchema(String componentName) throws IOException {
1618        // use the component factory finder to find the package name of the component class, which is the location
1619        // where the documentation exists as well
1620        FactoryFinder finder = getFactoryFinder(DefaultComponentResolver.RESOURCE_PATH);
1621        try {
1622            Class<?> clazz = null;
1623            try {
1624                clazz = finder.findClass(componentName);
1625            } catch (NoFactoryAvailableException e) {
1626                // ignore, i.e. if a component is an auto-configured spring-boot
1627                // component
1628            }
1629
1630            if (clazz == null) {
1631                // fallback and find existing component
1632                Component existing = hasComponent(componentName);
1633                if (existing != null) {
1634                    clazz = existing.getClass();
1635                } else {
1636                    return null;
1637                }
1638            }
1639
1640            String packageName = clazz.getPackage().getName();
1641            packageName = packageName.replace('.', '/');
1642            String path = packageName + "/" + componentName + ".json";
1643
1644            ClassResolver resolver = getClassResolver();
1645            InputStream inputStream = resolver.loadResourceAsStream(path);
1646            log.debug("Loading component JSON Schema for: {} using class resolver: {} -> {}", componentName, resolver, inputStream);
1647            if (inputStream != null) {
1648                try {
1649                    return IOHelper.loadText(inputStream);
1650                } finally {
1651                    IOHelper.close(inputStream);
1652                }
1653            }
1654            // special for ActiveMQ as it is really just JMS
1655            if ("ActiveMQComponent".equals(clazz.getSimpleName())) {
1656                return getComponentParameterJsonSchema("jms");
1657            } else {
1658                return null;
1659            }
1660        } catch (ClassNotFoundException e) {
1661            return null;
1662        }
1663    }
1664
1665    public String getDataFormatParameterJsonSchema(String dataFormatName) throws IOException {
1666        // use the dataformat factory finder to find the package name of the dataformat class, which is the location
1667        // where the documentation exists as well
1668        FactoryFinder finder = getFactoryFinder(DefaultDataFormatResolver.DATAFORMAT_RESOURCE_PATH);
1669        try {
1670            Class<?> clazz = null;
1671            try {
1672                clazz = finder.findClass(dataFormatName);
1673            } catch (NoFactoryAvailableException e) {
1674                // ignore, i.e. if a component is an auto-configured spring-boot
1675                // data-formats
1676            }
1677
1678            if (clazz == null) {
1679                return null;
1680            }
1681
1682            String packageName = clazz.getPackage().getName();
1683            packageName = packageName.replace('.', '/');
1684            String path = packageName + "/" + dataFormatName + ".json";
1685
1686            ClassResolver resolver = getClassResolver();
1687            InputStream inputStream = resolver.loadResourceAsStream(path);
1688            log.debug("Loading dataformat JSON Schema for: {} using class resolver: {} -> {}", dataFormatName, resolver, inputStream);
1689            if (inputStream != null) {
1690                try {
1691                    return IOHelper.loadText(inputStream);
1692                } finally {
1693                    IOHelper.close(inputStream);
1694                }
1695            }
1696            return null;
1697
1698        } catch (ClassNotFoundException e) {
1699            return null;
1700        }
1701    }
1702
1703    public String getLanguageParameterJsonSchema(String languageName) throws IOException {
1704        // use the language factory finder to find the package name of the language class, which is the location
1705        // where the documentation exists as well
1706        FactoryFinder finder = getFactoryFinder(DefaultLanguageResolver.LANGUAGE_RESOURCE_PATH);
1707        try {
1708            Class<?> clazz = null;
1709            try {
1710                clazz = finder.findClass(languageName);
1711            } catch (NoFactoryAvailableException e) {
1712                // ignore, i.e. if a component is an auto-configured spring-boot
1713                // languages
1714            }
1715
1716            if (clazz == null) {
1717                return null;
1718            }
1719
1720            String packageName = clazz.getPackage().getName();
1721            packageName = packageName.replace('.', '/');
1722            String path = packageName + "/" + languageName + ".json";
1723
1724            ClassResolver resolver = getClassResolver();
1725            InputStream inputStream = resolver.loadResourceAsStream(path);
1726            log.debug("Loading language JSON Schema for: {} using class resolver: {} -> {}", languageName, resolver, inputStream);
1727            if (inputStream != null) {
1728                try {
1729                    return IOHelper.loadText(inputStream);
1730                } finally {
1731                    IOHelper.close(inputStream);
1732                }
1733            }
1734            return null;
1735
1736        } catch (ClassNotFoundException e) {
1737            return null;
1738        }
1739    }
1740
1741    public String getEipParameterJsonSchema(String eipName) throws IOException {
1742        // the eip json schema may be in some of the sub-packages so look until we find it
1743        String[] subPackages = new String[]{"", "/config", "/dataformat", "/language", "/loadbalancer", "/rest"};
1744        for (String sub : subPackages) {
1745            String path = CamelContextHelper.MODEL_DOCUMENTATION_PREFIX + sub + "/" + eipName + ".json";
1746            ClassResolver resolver = getClassResolver();
1747            InputStream inputStream = resolver.loadResourceAsStream(path);
1748            if (inputStream != null) {
1749                log.debug("Loading eip JSON Schema for: {} using class resolver: {} -> {}", eipName, resolver, inputStream);
1750                try {
1751                    return IOHelper.loadText(inputStream);
1752                } finally {
1753                    IOHelper.close(inputStream);
1754                }
1755            }
1756        }
1757        return null;
1758    }
1759
1760    public String explainEipJson(String nameOrId, boolean includeAllOptions) {
1761        try {
1762            // try to find the id within all known routes and their eips
1763            String eipName = nameOrId;
1764            NamedNode target = null;
1765            for (RouteDefinition route : getRouteDefinitions()) {
1766                if (route.getId().equals(nameOrId)) {
1767                    target = route;
1768                    break;
1769                }
1770                for (FromDefinition from : route.getInputs()) {
1771                    if (nameOrId.equals(from.getId())) {
1772                        target = route;
1773                        break;
1774                    }
1775                }
1776                Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
1777                while (it.hasNext()) {
1778                    ProcessorDefinition def = it.next();
1779                    if (nameOrId.equals(def.getId())) {
1780                        target = def;
1781                        break;
1782                    }
1783                }
1784                if (target != null) {
1785                    break;
1786                }
1787            }
1788
1789            if (target != null) {
1790                eipName = target.getShortName();
1791            }
1792
1793            String json = getEipParameterJsonSchema(eipName);
1794            if (json == null) {
1795                return null;
1796            }
1797
1798            // overlay with runtime parameters that id uses at runtime
1799            if (target != null) {
1800                List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
1801
1802                // selected rows to use for answer
1803                Map<String, String[]> selected = new LinkedHashMap<>();
1804
1805                // extract options from the node
1806                Map<String, Object> options = new LinkedHashMap<>();
1807                IntrospectionSupport.getProperties(target, options, "", false);
1808                // remove outputs which we do not want to include
1809                options.remove("outputs");
1810
1811                // include other rows
1812                for (Map<String, String> row : rows) {
1813                    String name = row.get("name");
1814                    String kind = row.get("kind");
1815                    String label = row.get("label");
1816                    String required = row.get("required");
1817                    String value = row.get("value");
1818                    String defaultValue = row.get("defaultValue");
1819                    String type = row.get("type");
1820                    String javaType = row.get("javaType");
1821                    String deprecated = row.get("deprecated");
1822                    String description = row.get("description");
1823
1824                    // find the configured option
1825                    Object o = options.get(name);
1826                    if (o != null) {
1827                        value = o.toString();
1828                    }
1829
1830                    value = URISupport.sanitizePath(value);
1831
1832                    if (includeAllOptions || o != null) {
1833                        // add as selected row
1834                        if (!selected.containsKey(name)) {
1835                            selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, value, defaultValue, description});
1836                        }
1837                    }
1838                }
1839
1840                json = StringHelper.before(json, "  \"properties\": {");
1841
1842                StringBuilder buffer = new StringBuilder("  \"properties\": {");
1843
1844                boolean first = true;
1845                for (String[] row : selected.values()) {
1846                    if (first) {
1847                        first = false;
1848                    } else {
1849                        buffer.append(",");
1850                    }
1851                    buffer.append("\n    ");
1852
1853                    String name = row[0];
1854                    String kind = row[1];
1855                    String label = row[2];
1856                    String required = row[3];
1857                    String type = row[4];
1858                    String javaType = row[5];
1859                    String deprecated = row[6];
1860                    String value = row[7];
1861                    String defaultValue = row[8];
1862                    String description = row[9];
1863
1864                    // add json of the option
1865                    buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
1866                    CollectionStringBuffer csb = new CollectionStringBuffer();
1867                    if (kind != null) {
1868                        csb.append("\"kind\": \"" + kind + "\"");
1869                    }
1870                    if (label != null) {
1871                        csb.append("\"label\": \"" + label + "\"");
1872                    }
1873                    if (required != null) {
1874                        csb.append("\"required\": \"" + required + "\"");
1875                    }
1876                    if (type != null) {
1877                        csb.append("\"type\": \"" + type + "\"");
1878                    }
1879                    if (javaType != null) {
1880                        csb.append("\"javaType\": \"" + javaType + "\"");
1881                    }
1882                    if (deprecated != null) {
1883                        csb.append("\"deprecated\": \"" + deprecated + "\"");
1884                    }
1885                    if (value != null) {
1886                        csb.append("\"value\": \"" + value + "\"");
1887                    }
1888                    if (defaultValue != null) {
1889                        csb.append("\"defaultValue\": \"" + defaultValue + "\"");
1890                    }
1891                    if (description != null) {
1892                        csb.append("\"description\": \"" + description + "\"");
1893                    }
1894                    if (!csb.isEmpty()) {
1895                        buffer.append(csb.toString());
1896                    }
1897                    buffer.append(" }");
1898                }
1899
1900                buffer.append("\n  }\n}\n");
1901
1902                // insert the original first part of the json into the start of the buffer
1903                buffer.insert(0, json);
1904                return buffer.toString();
1905            }
1906
1907            return json;
1908        } catch (Exception e) {
1909            // ignore and return empty response
1910            return null;
1911        }
1912    }
1913
1914    public String explainDataFormatJson(String dataFormatName, DataFormat dataFormat, boolean includeAllOptions) {
1915        try {
1916            String json = getDataFormatParameterJsonSchema(dataFormatName);
1917            if (json == null) {
1918                // the model may be shared for multiple data formats such as bindy, json (xstream, jackson, gson)
1919                if (dataFormatName.contains("-")) {
1920                    dataFormatName = StringHelper.before(dataFormatName, "-");
1921                    json = getDataFormatParameterJsonSchema(dataFormatName);
1922                }
1923                if (json == null) {
1924                    return null;
1925                }
1926            }
1927
1928            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
1929
1930            // selected rows to use for answer
1931            Map<String, String[]> selected = new LinkedHashMap<>();
1932            Map<String, String[]> dataFormatOptions = new LinkedHashMap<>();
1933
1934            // extract options from the data format
1935            Map<String, Object> options = new LinkedHashMap<>();
1936            IntrospectionSupport.getProperties(dataFormat, options, "", false);
1937
1938            for (Map.Entry<String, Object> entry : options.entrySet()) {
1939                String name = entry.getKey();
1940                String value = "";
1941                if (entry.getValue() != null) {
1942                    value = entry.getValue().toString();
1943                }
1944                value = URISupport.sanitizePath(value);
1945
1946                // find type and description from the json schema
1947                String type = null;
1948                String kind = null;
1949                String label = null;
1950                String required = null;
1951                String javaType = null;
1952                String deprecated = null;
1953                String secret = null;
1954                String defaultValue = null;
1955                String description = null;
1956                for (Map<String, String> row : rows) {
1957                    if (name.equals(row.get("name"))) {
1958                        type = row.get("type");
1959                        kind = row.get("kind");
1960                        label = row.get("label");
1961                        required = row.get("required");
1962                        javaType = row.get("javaType");
1963                        deprecated = row.get("deprecated");
1964                        secret = row.get("secret");
1965                        defaultValue = row.get("defaultValue");
1966                        description = row.get("description");
1967                        break;
1968                    }
1969                }
1970
1971                // remember this option from the uri
1972                dataFormatOptions.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, secret, value, defaultValue, description});
1973            }
1974
1975            // include other rows
1976            for (Map<String, String> row : rows) {
1977                String name = row.get("name");
1978                String kind = row.get("kind");
1979                String label = row.get("label");
1980                String required = row.get("required");
1981                String value = row.get("value");
1982                String defaultValue = row.get("defaultValue");
1983                String type = row.get("type");
1984                String javaType = row.get("javaType");
1985                String deprecated = row.get("deprecated");
1986                String secret = row.get("secret");
1987                value = URISupport.sanitizePath(value);
1988                String description = row.get("description");
1989
1990                boolean isDataFormatOption = dataFormatOptions.containsKey(name);
1991
1992                // always include from uri or path options
1993                if (includeAllOptions || isDataFormatOption) {
1994                    if (!selected.containsKey(name)) {
1995                        // add as selected row, but take the value from uri options if it was from there
1996                        if (isDataFormatOption) {
1997                            selected.put(name, dataFormatOptions.get(name));
1998                        } else {
1999                            selected.put(name, new String[]{name, kind, label, required, type, javaType, deprecated, secret, value, defaultValue, description});
2000                        }
2001                    }
2002                }
2003            }
2004
2005            json = StringHelper.before(json, "  \"properties\": {");
2006
2007            StringBuilder buffer = new StringBuilder("  \"properties\": {");
2008
2009            boolean first = true;
2010            for (String[] row : selected.values()) {
2011                if (first) {
2012                    first = false;
2013                } else {
2014                    buffer.append(",");
2015                }
2016                buffer.append("\n    ");
2017
2018                String name = row[0];
2019                String kind = row[1];
2020                String label = row[2];
2021                String required = row[3];
2022                String type = row[4];
2023                String javaType = row[5];
2024                String deprecated = row[6];
2025                String secret = row[7];
2026                String value = row[8];
2027                String defaultValue = row[9];
2028                String description = row[10];
2029
2030                // add json of the option
2031                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
2032                CollectionStringBuffer csb = new CollectionStringBuffer();
2033                if (kind != null) {
2034                    csb.append("\"kind\": \"" + kind + "\"");
2035                }
2036                if (label != null) {
2037                    csb.append("\"label\": \"" + label + "\"");
2038                }
2039                if (required != null) {
2040                    csb.append("\"required\": \"" + required + "\"");
2041                }
2042                if (type != null) {
2043                    csb.append("\"type\": \"" + type + "\"");
2044                }
2045                if (javaType != null) {
2046                    csb.append("\"javaType\": \"" + javaType + "\"");
2047                }
2048                if (deprecated != null) {
2049                    csb.append("\"deprecated\": \"" + deprecated + "\"");
2050                }
2051                if (secret != null) {
2052                    csb.append("\"secret\": \"" + secret + "\"");
2053                }
2054                if (value != null) {
2055                    csb.append("\"value\": \"" + value + "\"");
2056                }
2057                if (defaultValue != null) {
2058                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
2059                }
2060                if (description != null) {
2061                    csb.append("\"description\": \"" + description + "\"");
2062                }
2063                if (!csb.isEmpty()) {
2064                    buffer.append(csb.toString());
2065                }
2066                buffer.append(" }");
2067            }
2068
2069            buffer.append("\n  }\n}\n");
2070
2071            // insert the original first part of the json into the start of the buffer
2072            buffer.insert(0, json);
2073            return buffer.toString();
2074
2075        } catch (Exception e) {
2076            // ignore and return empty response
2077            return null;
2078        }
2079    }
2080
2081    public String explainComponentJson(String componentName, boolean includeAllOptions) {
2082        try {
2083            String json = getComponentParameterJsonSchema(componentName);
2084            if (json == null) {
2085                return null;
2086            }
2087            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("componentProperties", json, true);
2088
2089            // selected rows to use for answer
2090            Map<String, String[]> selected = new LinkedHashMap<>();
2091
2092            // insert values from component
2093            Component component = getComponent(componentName);
2094            Map<String, Object> options = new HashMap<>();
2095            IntrospectionSupport.getProperties(component, options, null);
2096
2097            for (Map.Entry<String, Object> entry : options.entrySet()) {
2098                String name = entry.getKey();
2099                // skip unwanted options which is default inherited from DefaultComponent
2100                if ("camelContext".equals(name) || "endpointClass".equals(name)) {
2101                    continue;
2102                }
2103                String value = "";
2104                if (entry.getValue() != null) {
2105                    value = entry.getValue().toString();
2106                }
2107                value = URISupport.sanitizePath(value);
2108
2109                // find type and description from the json schema
2110                String type = null;
2111                String kind = null;
2112                String group = null;
2113                String label = null;
2114                String required = null;
2115                String javaType = null;
2116                String deprecated = null;
2117                String secret = null;
2118                String defaultValue = null;
2119                String description = null;
2120                for (Map<String, String> row : rows) {
2121                    if (name.equals(row.get("name"))) {
2122                        type = row.get("type");
2123                        kind = row.get("kind");
2124                        group = row.get("group");
2125                        label = row.get("label");
2126                        required = row.get("required");
2127                        javaType = row.get("javaType");
2128                        deprecated = row.get("deprecated");
2129                        secret = row.get("secret");
2130                        defaultValue = row.get("defaultValue");
2131                        description = row.get("description");
2132                        break;
2133                    }
2134                }
2135                // add as selected row
2136                selected.put(name, new String[]{name, kind, group, label, required, type, javaType, deprecated, secret, value, defaultValue, description});
2137            }
2138
2139            // include other rows
2140            for (Map<String, String> row : rows) {
2141                String name = row.get("name");
2142                String kind = row.get("kind");
2143                String group = row.get("group");
2144                String label = row.get("label");
2145                String required = row.get("required");
2146                String value = row.get("value");
2147                String defaultValue = row.get("defaultValue");
2148                String type = row.get("type");
2149                String javaType = row.get("javaType");
2150                String deprecated = row.get("deprecated");
2151                String secret = row.get("secret");
2152                value = URISupport.sanitizePath(value);
2153                String description = row.get("description");
2154                // always include path options
2155                if (includeAllOptions) {
2156                    // add as selected row
2157                    if (!selected.containsKey(name)) {
2158                        selected.put(name, new String[]{name, kind, group, label, required, type, javaType, deprecated, secret, value, defaultValue, description});
2159                    }
2160                }
2161            }
2162
2163            json = StringHelper.before(json, "  \"componentProperties\": {");
2164            StringBuilder buffer = new StringBuilder("  \"componentProperties\": {");
2165
2166            boolean first = true;
2167            for (String[] row : selected.values()) {
2168                if (first) {
2169                    first = false;
2170                } else {
2171                    buffer.append(",");
2172                }
2173                buffer.append("\n    ");
2174
2175                String name = row[0];
2176                String kind = row[1];
2177                String group = row[2];
2178                String label = row[3];
2179                String required = row[4];
2180                String type = row[5];
2181                String javaType = row[6];
2182                String deprecated = row[7];
2183                String secret = row[8];
2184                String value = row[9];
2185                String defaultValue = row[10];
2186                String description = row[11];
2187
2188                // add json of the option
2189                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
2190                CollectionStringBuffer csb = new CollectionStringBuffer();
2191                if (kind != null) {
2192                    csb.append("\"kind\": \"" + kind + "\"");
2193                }
2194                if (group != null) {
2195                    csb.append("\"group\": \"" + group + "\"");
2196                }
2197                if (label != null) {
2198                    csb.append("\"label\": \"" + label + "\"");
2199                }
2200                if (required != null) {
2201                    csb.append("\"required\": \"" + required + "\"");
2202                }
2203                if (type != null) {
2204                    csb.append("\"type\": \"" + type + "\"");
2205                }
2206                if (javaType != null) {
2207                    csb.append("\"javaType\": \"" + javaType + "\"");
2208                }
2209                if (deprecated != null) {
2210                    csb.append("\"deprecated\": \"" + deprecated + "\"");
2211                }
2212                if (secret != null) {
2213                    csb.append("\"secret\": \"" + secret + "\"");
2214                }
2215                if (value != null) {
2216                    csb.append("\"value\": \"" + value + "\"");
2217                }
2218                if (defaultValue != null) {
2219                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
2220                }
2221                if (description != null) {
2222                    csb.append("\"description\": \"" + description + "\"");
2223                }
2224                if (!csb.isEmpty()) {
2225                    buffer.append(csb.toString());
2226                }
2227                buffer.append(" }");
2228            }
2229            buffer.append("\n  }\n}\n");
2230            // insert the original first part of the json into the start of the buffer
2231            buffer.insert(0, json);
2232            return buffer.toString();
2233        } catch (Exception e) {
2234            // ignore and return empty response
2235            return null;
2236        }
2237    }
2238
2239    // CHECKSTYLE:OFF
2240    public String explainEndpointJson(String uri, boolean includeAllOptions) {
2241        try {
2242            URI u = new URI(uri);
2243            String json = getComponentParameterJsonSchema(u.getScheme());
2244            if (json == null) {
2245                return null;
2246            }
2247            List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json, true);
2248
2249            // selected rows to use for answer
2250            Map<String, String[]> selected = new LinkedHashMap<>();
2251            Map<String, String[]> uriOptions = new LinkedHashMap<>();
2252
2253            // insert values from uri
2254            Map<String, Object> options = EndpointHelper.endpointProperties(this, uri);
2255
2256            // extract consumer. prefix options
2257            Map<String, Object> consumerOptions = IntrospectionSupport.extractProperties(options, "consumer.");
2258            // and add back again without the consumer. prefix as that json schema omits that
2259            options.putAll(consumerOptions);
2260
2261            for (Map.Entry<String, Object> entry : options.entrySet()) {
2262                String name = entry.getKey();
2263                String value = "";
2264                if (entry.getValue() != null) {
2265                    value = entry.getValue().toString();
2266                }
2267                value = URISupport.sanitizePath(value);
2268                // find type and description from the json schema
2269                String type = null;
2270                String kind = null;
2271                String group = null;
2272                String label = null;
2273                String required = null;
2274                String javaType = null;
2275                String deprecated = null;
2276                String secret = null;
2277                String defaultValue = null;
2278                String description = null;
2279                for (Map<String, String> row : rows) {
2280                    if (name.equals(row.get("name"))) {
2281                        type = row.get("type");
2282                        kind = row.get("kind");
2283                        group = row.get("group");
2284                        label = row.get("label");
2285                        required = row.get("required");
2286                        javaType = row.get("javaType");
2287                        deprecated = row.get("deprecated");
2288                        secret = row.get("secret");
2289                        defaultValue = row.get("defaultValue");
2290                        description = row.get("description");
2291                        break;
2292                    }
2293                }
2294                // remember this option from the uri
2295                uriOptions.put(name, new String[]{name, kind, group, label, required, type, javaType, deprecated, secret, value, defaultValue, description});
2296            }
2297
2298            // include other rows
2299            for (Map<String, String> row : rows) {
2300                String name = row.get("name");
2301                String kind = row.get("kind");
2302                String group = row.get("group");
2303                String label = row.get("label");
2304                String required = row.get("required");
2305                String value = row.get("value");
2306                String defaultValue = row.get("defaultValue");
2307                String type = row.get("type");
2308                String javaType = row.get("javaType");
2309                String deprecated = row.get("deprecated");
2310                String secret = row.get("secret");
2311                value = URISupport.sanitizePath(value);
2312                String description = row.get("description");
2313                boolean isUriOption = uriOptions.containsKey(name);
2314                // always include from uri or path options
2315                if (includeAllOptions || isUriOption || "path".equals(kind)) {
2316                    if (!selected.containsKey(name)) {
2317                        // add as selected row, but take the value from uri options if it was from there
2318                        if (isUriOption) {
2319                            selected.put(name, uriOptions.get(name));
2320                        } else {
2321                            selected.put(name, new String[]{name, kind, group, label, required, type, javaType, deprecated, secret, value, defaultValue, description});
2322                        }
2323                    }
2324                }
2325            }
2326
2327            // skip component properties
2328            json = StringHelper.before(json, "  \"componentProperties\": {");
2329            // and rewrite properties
2330            StringBuilder buffer = new StringBuilder("  \"properties\": {");
2331
2332            boolean first = true;
2333            for (String[] row : selected.values()) {
2334                if (first) {
2335                    first = false;
2336                } else {
2337                    buffer.append(",");
2338                }
2339                buffer.append("\n    ");
2340
2341                String name = row[0];
2342                String kind = row[1];
2343                String group = row[2];
2344                String label = row[3];
2345                String required = row[4];
2346                String type = row[5];
2347                String javaType = row[6];
2348                String deprecated = row[7];
2349                String secret = row[8];
2350                String value = row[9];
2351                String defaultValue = row[10];
2352                String description = row[11];
2353
2354                // add json of the option
2355                buffer.append(StringQuoteHelper.doubleQuote(name)).append(": { ");
2356                CollectionStringBuffer csb = new CollectionStringBuffer();
2357                if (kind != null) {
2358                    csb.append("\"kind\": \"" + kind + "\"");
2359                }
2360                if (group != null) {
2361                    csb.append("\"group\": \"" + group + "\"");
2362                }
2363                if (label != null) {
2364                    csb.append("\"label\": \"" + label + "\"");
2365                }
2366                if (required != null) {
2367                    csb.append("\"required\": \"" + required + "\"");
2368                }
2369                if (type != null) {
2370                    csb.append("\"type\": \"" + type + "\"");
2371                }
2372                if (javaType != null) {
2373                    csb.append("\"javaType\": \"" + javaType + "\"");
2374                }
2375                if (deprecated != null) {
2376                    csb.append("\"deprecated\": \"" + deprecated + "\"");
2377                }
2378                if (secret != null) {
2379                    csb.append("\"secret\": \"" + secret + "\"");
2380                }
2381                if (value != null) {
2382                    csb.append("\"value\": \"" + value + "\"");
2383                }
2384                if (defaultValue != null) {
2385                    csb.append("\"defaultValue\": \"" + defaultValue + "\"");
2386                }
2387                if (description != null) {
2388                    csb.append("\"description\": \"" + description + "\"");
2389                }
2390                if (!csb.isEmpty()) {
2391                    buffer.append(csb.toString());
2392                }
2393                buffer.append(" }");
2394            }
2395            buffer.append("\n  }\n}\n");
2396            // insert the original first part of the json into the start of the buffer
2397            buffer.insert(0, json);
2398            return buffer.toString();
2399        } catch (Exception e) {
2400            // ignore and return empty response
2401            return null;
2402        }
2403    }
2404    // CHECKSTYLE:ON
2405
2406    public String createRouteStaticEndpointJson(String routeId) {
2407        // lets include dynamic as well as we want as much data as possible
2408        return createRouteStaticEndpointJson(routeId, true);
2409    }
2410
2411    public String createRouteStaticEndpointJson(String routeId, boolean includeDynamic) {
2412        List<RouteDefinition> routes = new ArrayList<>();
2413        if (routeId != null) {
2414            RouteDefinition route = getRouteDefinition(routeId);
2415            if (route == null) {
2416                throw new IllegalArgumentException("Route with id " + routeId + " does not exist");
2417            }
2418            routes.add(route);
2419        } else {
2420            routes.addAll(getRouteDefinitions());
2421        }
2422
2423        StringBuilder buffer = new StringBuilder("{\n  \"routes\": {");
2424        boolean firstRoute = true;
2425        for (RouteDefinition route : routes) {
2426            if (!firstRoute) {
2427                buffer.append("\n    },");
2428            } else {
2429                firstRoute = false;
2430            }
2431
2432            String id = route.getId();
2433            buffer.append("\n    \"").append(id).append("\": {");
2434            buffer.append("\n      \"inputs\": [");
2435            // for inputs we do not need to check dynamic as we have the data from the route definition
2436            Set<String> inputs = RouteDefinitionHelper.gatherAllStaticEndpointUris(this, route, true, false);
2437            boolean first = true;
2438            for (String input : inputs) {
2439                if (!first) {
2440                    buffer.append(",");
2441                } else {
2442                    first = false;
2443                }
2444                buffer.append("\n        ");
2445                buffer.append(StringHelper.toJson("uri", input, true));
2446            }
2447            buffer.append("\n      ]");
2448
2449            buffer.append(",");
2450            buffer.append("\n      \"outputs\": [");
2451            Set<String> outputs = RouteDefinitionHelper.gatherAllEndpointUris(this, route, false, true, includeDynamic);
2452            first = true;
2453            for (String output : outputs) {
2454                if (!first) {
2455                    buffer.append(",");
2456                } else {
2457                    first = false;
2458                }
2459                buffer.append("\n        ");
2460                buffer.append(StringHelper.toJson("uri", output, true));
2461            }
2462            buffer.append("\n      ]");
2463        }
2464        if (!firstRoute) {
2465            buffer.append("\n    }");
2466        }
2467        buffer.append("\n  }\n}\n");
2468
2469        return buffer.toString();
2470    }
2471
2472    // Helper methods
2473    // -----------------------------------------------------------------------
2474
2475    public Language resolveLanguage(String language) {
2476        Language answer;
2477        synchronized (languages) {
2478            answer = languages.get(language);
2479
2480            // check if the language is singleton, if so return the shared instance
2481            if (answer instanceof IsSingleton) {
2482                boolean singleton = ((IsSingleton) answer).isSingleton();
2483                if (singleton) {
2484                    return answer;
2485                }
2486            }
2487
2488            // language not known or not singleton, then use resolver
2489            answer = getLanguageResolver().resolveLanguage(language, this);
2490
2491            // inject CamelContext if aware
2492            if (answer != null) {
2493                if (answer instanceof CamelContextAware) {
2494                    ((CamelContextAware) answer).setCamelContext(this);
2495                }
2496                if (answer instanceof Service) {
2497                    try {
2498                        startService((Service) answer);
2499                    } catch (Exception e) {
2500                        throw ObjectHelper.wrapRuntimeCamelException(e);
2501                    }
2502                }
2503
2504                languages.put(language, answer);
2505            }
2506        }
2507
2508        return answer;
2509    }
2510
2511    public String getPropertyPrefixToken() {
2512        PropertiesComponent pc = getPropertiesComponent();
2513
2514        if (pc != null) {
2515            return pc.getPrefixToken();
2516        } else {
2517            return null;
2518        }
2519    }
2520
2521    public String getPropertySuffixToken() {
2522        PropertiesComponent pc = getPropertiesComponent();
2523
2524        if (pc != null) {
2525            return pc.getSuffixToken();
2526        } else {
2527            return null;
2528        }
2529    }
2530
2531    public String resolvePropertyPlaceholders(String text) throws Exception {
2532        // While it is more efficient to only do the lookup if we are sure we need the component,
2533        // with custom tokens, we cannot know if the URI contains a property or not without having
2534        // the component.  We also lose fail-fast behavior for the missing component with this change.
2535        PropertiesComponent pc = getPropertiesComponent();
2536
2537        // Do not parse uris that are designated for the properties component as it will handle that itself
2538        if (text != null && !text.startsWith("properties:")) {
2539            // No component, assume default tokens.
2540            if (pc == null && text.contains(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
2541                // lookup existing properties component, or force create a new default component
2542                pc = (PropertiesComponent) CamelContextHelper.lookupPropertiesComponent(this, true);
2543            }
2544
2545            if (pc != null && text.contains(pc.getPrefixToken())) {
2546                // the parser will throw exception if property key was not found
2547                String answer = pc.parseUri(text);
2548                log.debug("Resolved text: {} -> {}", text, answer);
2549                return answer;
2550            }
2551        }
2552
2553        // return original text as is
2554        return text;
2555    }
2556
2557    // Properties
2558    // -----------------------------------------------------------------------
2559
2560    public TypeConverter getTypeConverter() {
2561        if (typeConverter == null) {
2562            synchronized (this) {
2563                // we can synchronize on this as there is only one instance
2564                // of the camel context (its the container)
2565                typeConverter = createTypeConverter();
2566                try {
2567                    // must add service eager and force start it
2568                    addService(typeConverter, true, true);
2569                } catch (Exception e) {
2570                    throw ObjectHelper.wrapRuntimeCamelException(e);
2571                }
2572            }
2573        }
2574        return typeConverter;
2575    }
2576
2577    public void setTypeConverter(TypeConverter typeConverter) {
2578        this.typeConverter = typeConverter;
2579        try {
2580            // must add service eager and force start it
2581            addService(typeConverter, true, true);
2582        } catch (Exception e) {
2583            throw ObjectHelper.wrapRuntimeCamelException(e);
2584        }
2585    }
2586
2587    public TypeConverterRegistry getTypeConverterRegistry() {
2588        if (typeConverterRegistry == null) {
2589            // init type converter as its lazy
2590            if (typeConverter == null) {
2591                getTypeConverter();
2592            }
2593            if (typeConverter instanceof TypeConverterRegistry) {
2594                typeConverterRegistry = (TypeConverterRegistry) typeConverter;
2595            }
2596        }
2597        return typeConverterRegistry;
2598    }
2599
2600    public void setTypeConverterRegistry(TypeConverterRegistry typeConverterRegistry) {
2601        this.typeConverterRegistry = typeConverterRegistry;
2602    }
2603
2604    public Injector getInjector() {
2605        if (injector == null) {
2606            injector = createInjector();
2607        }
2608        return injector;
2609    }
2610
2611    public void setInjector(Injector injector) {
2612        this.injector = injector;
2613    }
2614
2615    public ManagementMBeanAssembler getManagementMBeanAssembler() {
2616        return managementMBeanAssembler;
2617    }
2618
2619    public void setManagementMBeanAssembler(ManagementMBeanAssembler managementMBeanAssembler) {
2620        this.managementMBeanAssembler = managementMBeanAssembler;
2621    }
2622
2623    public ComponentResolver getComponentResolver() {
2624        if (componentResolver == null) {
2625            componentResolver = createComponentResolver();
2626        }
2627        return componentResolver;
2628    }
2629
2630    public void setComponentResolver(ComponentResolver componentResolver) {
2631        this.componentResolver = componentResolver;
2632    }
2633
2634    public LanguageResolver getLanguageResolver() {
2635        if (languageResolver == null) {
2636            languageResolver = createLanguageResolver();
2637        }
2638        return languageResolver;
2639    }
2640
2641    public void setLanguageResolver(LanguageResolver languageResolver) {
2642        this.languageResolver = languageResolver;
2643    }
2644
2645    public boolean isAutoCreateComponents() {
2646        return autoCreateComponents;
2647    }
2648
2649    public void setAutoCreateComponents(boolean autoCreateComponents) {
2650        this.autoCreateComponents = autoCreateComponents;
2651    }
2652
2653    public Registry getRegistry() {
2654        if (registry == null) {
2655            registry = createRegistry();
2656            setRegistry(registry);
2657        }
2658        return registry;
2659    }
2660
2661    public <T> T getRegistry(Class<T> type) {
2662        Registry reg = getRegistry();
2663
2664        // unwrap the property placeholder delegate
2665        if (reg instanceof PropertyPlaceholderDelegateRegistry) {
2666            reg = ((PropertyPlaceholderDelegateRegistry) reg).getRegistry();
2667        }
2668
2669        if (type.isAssignableFrom(reg.getClass())) {
2670            return type.cast(reg);
2671        } else if (reg instanceof CompositeRegistry) {
2672            List<Registry> list = ((CompositeRegistry) reg).getRegistryList();
2673            for (Registry r : list) {
2674                if (type.isAssignableFrom(r.getClass())) {
2675                    return type.cast(r);
2676                }
2677            }
2678        }
2679        return null;
2680    }
2681
2682    /**
2683     * Sets the registry to the given JNDI context
2684     *
2685     * @param jndiContext is the JNDI context to use as the registry
2686     * @see #setRegistry(org.apache.camel.spi.Registry)
2687     */
2688    public void setJndiContext(Context jndiContext) {
2689        setRegistry(new JndiRegistry(jndiContext));
2690    }
2691
2692    public void setRegistry(Registry registry) {
2693        // wrap the registry so we always do property placeholder lookups
2694        if (!(registry instanceof PropertyPlaceholderDelegateRegistry)) {
2695            registry = new PropertyPlaceholderDelegateRegistry(this, registry);
2696        }
2697        this.registry = registry;
2698    }
2699
2700    public List<LifecycleStrategy> getLifecycleStrategies() {
2701        return lifecycleStrategies;
2702    }
2703
2704    public void setLifecycleStrategies(List<LifecycleStrategy> lifecycleStrategies) {
2705        this.lifecycleStrategies = lifecycleStrategies;
2706    }
2707
2708    public void addLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
2709        // avoid adding double which can happen with spring xml on spring boot
2710        if (!getLifecycleStrategies().contains(lifecycleStrategy)) {
2711            getLifecycleStrategies().add(lifecycleStrategy);
2712        }
2713    }
2714
2715    public void setupRoutes(boolean done) {
2716        if (done) {
2717            isSetupRoutes.remove();
2718        } else {
2719            isSetupRoutes.set(true);
2720        }
2721    }
2722
2723    public synchronized List<RouteDefinition> getRouteDefinitions() {
2724        return routeDefinitions;
2725    }
2726
2727    public synchronized RouteDefinition getRouteDefinition(String id) {
2728        for (RouteDefinition route : routeDefinitions) {
2729            if (route.idOrCreate(nodeIdFactory).equals(id)) {
2730                return route;
2731            }
2732        }
2733        return null;
2734    }
2735
2736    public synchronized List<RestDefinition> getRestDefinitions() {
2737        return restDefinitions;
2738    }
2739
2740    public void addRestDefinitions(Collection<RestDefinition> restDefinitions) throws Exception {
2741        if (restDefinitions == null || restDefinitions.isEmpty()) {
2742            return;
2743        }
2744
2745        this.restDefinitions.addAll(restDefinitions);
2746    }
2747
2748    public RestConfiguration getRestConfiguration() {
2749        return restConfigurations.get("");
2750    }
2751
2752    public void setRestConfiguration(RestConfiguration restConfiguration) {
2753        restConfigurations.put("", restConfiguration);
2754    }
2755
2756    public Collection<RestConfiguration> getRestConfigurations() {
2757        return restConfigurations.values();
2758    }
2759
2760    public void addRestConfiguration(RestConfiguration restConfiguration) {
2761        restConfigurations.put(restConfiguration.getComponent(), restConfiguration);
2762    }
2763
2764    public RestConfiguration getRestConfiguration(String component, boolean defaultIfNotExist) {
2765        if (component == null) {
2766            component = "";
2767        }
2768        RestConfiguration config = restConfigurations.get(component);
2769        if (config == null && defaultIfNotExist) {
2770            // grab the default configuration
2771            config = getRestConfiguration();
2772            if (config == null || (config.getComponent() != null && !config.getComponent().equals(component))) {
2773                config = new RestConfiguration();
2774                restConfigurations.put(component, config);
2775            }
2776        }
2777        return config;
2778    }
2779
2780    @Override
2781    public ServiceCallConfigurationDefinition getServiceCallConfiguration(String serviceName) {
2782        if (serviceName == null) {
2783            serviceName = "";
2784        }
2785
2786        return serviceCallConfigurations.get(serviceName);
2787    }
2788
2789    @Override
2790    public void setServiceCallConfiguration(ServiceCallConfigurationDefinition configuration) {
2791        serviceCallConfigurations.put("", configuration);
2792    }
2793
2794    @Override
2795    public void setServiceCallConfigurations(List<ServiceCallConfigurationDefinition> configurations) {
2796        if (configurations != null) {
2797            for (ServiceCallConfigurationDefinition configuration : configurations) {
2798                serviceCallConfigurations.put(configuration.getId(), configuration);
2799            }
2800        }
2801    }
2802
2803    @Override
2804    public void addServiceCallConfiguration(String serviceName, ServiceCallConfigurationDefinition configuration) {
2805        serviceCallConfigurations.put(serviceName, configuration);
2806    }
2807
2808    @Override
2809    public HystrixConfigurationDefinition getHystrixConfiguration(String id) {
2810        if (id == null) {
2811            id = "";
2812        }
2813
2814        return hystrixConfigurations.get(id);
2815    }
2816
2817    @Override
2818    public void setHystrixConfiguration(HystrixConfigurationDefinition configuration) {
2819        hystrixConfigurations.put("", configuration);
2820    }
2821
2822    @Override
2823    public void setHystrixConfigurations(List<HystrixConfigurationDefinition> configurations) {
2824        if (configurations != null) {
2825            for (HystrixConfigurationDefinition configuration : configurations) {
2826                hystrixConfigurations.put(configuration.getId(), configuration);
2827            }
2828        }
2829    }
2830
2831    @Override
2832    public void addHystrixConfiguration(String id, HystrixConfigurationDefinition configuration) {
2833        hystrixConfigurations.put(id, configuration);
2834    }
2835
2836    public List<InterceptStrategy> getInterceptStrategies() {
2837        return interceptStrategies;
2838    }
2839
2840    public void setInterceptStrategies(List<InterceptStrategy> interceptStrategies) {
2841        this.interceptStrategies = interceptStrategies;
2842    }
2843
2844    public void addInterceptStrategy(InterceptStrategy interceptStrategy) {
2845        // avoid adding double which can happen with spring xml on spring boot
2846        if (!getInterceptStrategies().contains(interceptStrategy)) {
2847            getInterceptStrategies().add(interceptStrategy);
2848
2849            // for backwards compatible or if user add them here instead of the setXXX methods
2850
2851            if (interceptStrategy instanceof Tracer) {
2852                setTracing(true);
2853            } else if (interceptStrategy instanceof HandleFault) {
2854                setHandleFault(true);
2855            } else if (interceptStrategy instanceof StreamCaching) {
2856                setStreamCaching(true);
2857            } else if (interceptStrategy instanceof Delayer) {
2858                setDelayer(((Delayer) interceptStrategy).getDelay());
2859            }
2860        }
2861    }
2862
2863    public List<RoutePolicyFactory> getRoutePolicyFactories() {
2864        return routePolicyFactories;
2865    }
2866
2867    public void setRoutePolicyFactories(List<RoutePolicyFactory> routePolicyFactories) {
2868        this.routePolicyFactories = routePolicyFactories;
2869    }
2870
2871    public void addRoutePolicyFactory(RoutePolicyFactory routePolicyFactory) {
2872        // avoid adding double which can happen with spring xml on spring boot
2873        if (!getRoutePolicyFactories().contains(routePolicyFactory)) {
2874            getRoutePolicyFactories().add(routePolicyFactory);
2875        }
2876    }
2877
2878    public Set<LogListener> getLogListeners() {
2879        return logListeners;
2880    }
2881
2882    public void addLogListener(LogListener listener) {
2883        // avoid adding double which can happen with spring xml on spring boot
2884        if (!logListeners.contains(listener)) {
2885            logListeners.add(listener);
2886        }
2887    }
2888
2889    public void setStreamCaching(Boolean cache) {
2890        this.streamCache = cache;
2891    }
2892
2893    public Boolean isStreamCaching() {
2894        return streamCache;
2895    }
2896
2897    public void setTracing(Boolean tracing) {
2898        this.trace = tracing;
2899    }
2900
2901    public Boolean isTracing() {
2902        return trace;
2903    }
2904
2905    public Boolean isMessageHistory() {
2906        return messageHistory;
2907    }
2908
2909    public void setMessageHistory(Boolean messageHistory) {
2910        this.messageHistory = messageHistory;
2911    }
2912
2913    public void setLogMask(Boolean logMask) {
2914        this.logMask = logMask;
2915    }
2916
2917    public Boolean isLogMask() {
2918        return logMask != null && logMask;
2919    }
2920
2921    public Boolean isLogExhaustedMessageBody() {
2922        return logExhaustedMessageBody;
2923    }
2924
2925    public void setLogExhaustedMessageBody(Boolean logExhaustedMessageBody) {
2926        this.logExhaustedMessageBody = logExhaustedMessageBody;
2927    }
2928
2929    public Boolean isHandleFault() {
2930        return handleFault;
2931    }
2932
2933    public void setHandleFault(Boolean handleFault) {
2934        this.handleFault = handleFault;
2935    }
2936
2937    public Long getDelayer() {
2938        return delay;
2939    }
2940
2941    public void setDelayer(Long delay) {
2942        this.delay = delay;
2943    }
2944
2945    public ProducerTemplate createProducerTemplate() {
2946        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2947        return createProducerTemplate(size);
2948    }
2949
2950    public ProducerTemplate createProducerTemplate(int maximumCacheSize) {
2951        DefaultProducerTemplate answer = new DefaultProducerTemplate(this);
2952        answer.setMaximumCacheSize(maximumCacheSize);
2953        // start it so its ready to use
2954        try {
2955            startService(answer);
2956        } catch (Exception e) {
2957            throw ObjectHelper.wrapRuntimeCamelException(e);
2958        }
2959        return answer;
2960    }
2961
2962    public FluentProducerTemplate createFluentProducerTemplate() {
2963        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2964        return createFluentProducerTemplate(size);
2965    }
2966
2967    public FluentProducerTemplate createFluentProducerTemplate(int maximumCacheSize) {
2968        DefaultFluentProducerTemplate answer = new DefaultFluentProducerTemplate(this);
2969        answer.setMaximumCacheSize(maximumCacheSize);
2970        // start it so its ready to use
2971        try {
2972            startService(answer);
2973        } catch (Exception e) {
2974            throw ObjectHelper.wrapRuntimeCamelException(e);
2975        }
2976        return answer;
2977    }
2978
2979    public ConsumerTemplate createConsumerTemplate() {
2980        int size = CamelContextHelper.getMaximumCachePoolSize(this);
2981        return createConsumerTemplate(size);
2982    }
2983
2984    public ConsumerTemplate createConsumerTemplate(int maximumCacheSize) {
2985        DefaultConsumerTemplate answer = new DefaultConsumerTemplate(this);
2986        answer.setMaximumCacheSize(maximumCacheSize);
2987        // start it so its ready to use
2988        try {
2989            startService(answer);
2990        } catch (Exception e) {
2991            throw ObjectHelper.wrapRuntimeCamelException(e);
2992        }
2993        return answer;
2994    }
2995
2996    public ErrorHandlerBuilder getErrorHandlerBuilder() {
2997        return (ErrorHandlerBuilder)errorHandlerBuilder;
2998    }
2999
3000    public void setErrorHandlerBuilder(ErrorHandlerFactory errorHandlerBuilder) {
3001        this.errorHandlerBuilder = errorHandlerBuilder;
3002    }
3003
3004    public ScheduledExecutorService getErrorHandlerExecutorService() {
3005        synchronized (errorHandlerExecutorServiceLock) {
3006            if (errorHandlerExecutorService == null) {
3007                // setup default thread pool for error handler
3008                errorHandlerExecutorService = getExecutorServiceManager().newDefaultScheduledThreadPool("ErrorHandlerRedeliveryThreadPool", "ErrorHandlerRedeliveryTask");
3009            }
3010        }
3011        return errorHandlerExecutorService;
3012    }
3013
3014    public void setProducerServicePool(ServicePool<Endpoint, Producer> producerServicePool) {
3015        this.producerServicePool = producerServicePool;
3016    }
3017
3018    public ServicePool<Endpoint, Producer> getProducerServicePool() {
3019        return producerServicePool;
3020    }
3021
3022    public ServicePool<Endpoint, PollingConsumer> getPollingConsumerServicePool() {
3023        return pollingConsumerServicePool;
3024    }
3025
3026    public void setPollingConsumerServicePool(ServicePool<Endpoint, PollingConsumer> pollingConsumerServicePool) {
3027        this.pollingConsumerServicePool = pollingConsumerServicePool;
3028    }
3029
3030    public UnitOfWorkFactory getUnitOfWorkFactory() {
3031        return unitOfWorkFactory;
3032    }
3033
3034    public void setUnitOfWorkFactory(UnitOfWorkFactory unitOfWorkFactory) {
3035        this.unitOfWorkFactory = unitOfWorkFactory;
3036    }
3037
3038    public RuntimeEndpointRegistry getRuntimeEndpointRegistry() {
3039        return runtimeEndpointRegistry;
3040    }
3041
3042    public void setRuntimeEndpointRegistry(RuntimeEndpointRegistry runtimeEndpointRegistry) {
3043        this.runtimeEndpointRegistry = runtimeEndpointRegistry;
3044    }
3045
3046    public String getUptime() {
3047        long delta = getUptimeMillis();
3048        if (delta == 0) {
3049            return "";
3050        }
3051        return TimeUtils.printDuration(delta);
3052    }
3053
3054    public long getUptimeMillis() {
3055        if (startDate == null) {
3056            return 0;
3057        }
3058        return new Date().getTime() - startDate.getTime();
3059    }
3060
3061    @Override
3062    protected void doSuspend() throws Exception {
3063        EventHelper.notifyCamelContextSuspending(this);
3064
3065        log.info("Apache Camel {} (CamelContext: {}) is suspending", getVersion(), getName());
3066        StopWatch watch = new StopWatch();
3067
3068        // update list of started routes to be suspended
3069        // because we only want to suspend started routes
3070        // (so when we resume we only resume the routes which actually was suspended)
3071        for (Map.Entry<String, RouteService> entry : getRouteServices().entrySet()) {
3072            if (entry.getValue().getStatus().isStarted()) {
3073                suspendedRouteServices.put(entry.getKey(), entry.getValue());
3074            }
3075        }
3076
3077        // assemble list of startup ordering so routes can be shutdown accordingly
3078        List<RouteStartupOrder> orders = new ArrayList<>();
3079        for (Map.Entry<String, RouteService> entry : suspendedRouteServices.entrySet()) {
3080            Route route = entry.getValue().getRoutes().iterator().next();
3081            Integer order = entry.getValue().getRouteDefinition().getStartupOrder();
3082            if (order == null) {
3083                order = defaultRouteStartupOrder++;
3084            }
3085            orders.add(new DefaultRouteStartupOrder(order, route, entry.getValue()));
3086        }
3087
3088        // suspend routes using the shutdown strategy so it can shutdown in correct order
3089        // routes which doesn't support suspension will be stopped instead
3090        getShutdownStrategy().suspend(this, orders);
3091
3092        // mark the route services as suspended or stopped
3093        for (RouteService service : suspendedRouteServices.values()) {
3094            if (routeSupportsSuspension(service.getId())) {
3095                service.suspend();
3096            } else {
3097                service.stop();
3098            }
3099        }
3100
3101        watch.stop();
3102        if (log.isInfoEnabled()) {
3103            log.info("Apache Camel {} (CamelContext: {}) is suspended in {}", getVersion(), getName(), TimeUtils.printDuration(watch.taken()));
3104        }
3105
3106        EventHelper.notifyCamelContextSuspended(this);
3107    }
3108
3109    @Override
3110    protected void doResume() throws Exception {
3111        try {
3112            EventHelper.notifyCamelContextResuming(this);
3113
3114            log.info("Apache Camel {} (CamelContext: {}) is resuming", getVersion(), getName());
3115            StopWatch watch = new StopWatch();
3116
3117            // start the suspended routes (do not check for route clashes, and indicate)
3118            doStartOrResumeRoutes(suspendedRouteServices, false, true, true, false);
3119
3120            // mark the route services as resumed (will be marked as started) as well
3121            for (RouteService service : suspendedRouteServices.values()) {
3122                if (routeSupportsSuspension(service.getId())) {
3123                    service.resume();
3124                } else {
3125                    service.start();
3126                }
3127            }
3128
3129            if (log.isInfoEnabled()) {
3130                log.info("Resumed {} routes", suspendedRouteServices.size());
3131                log.info("Apache Camel {} (CamelContext: {}) resumed in {}", getVersion(), getName(), TimeUtils.printDuration(watch.taken()));
3132            }
3133
3134            // and clear the list as they have been resumed
3135            suspendedRouteServices.clear();
3136
3137            EventHelper.notifyCamelContextResumed(this);
3138        } catch (Exception e) {
3139            EventHelper.notifyCamelContextResumeFailed(this, e);
3140            throw e;
3141        }
3142    }
3143
3144    @Override
3145    public void start() throws Exception {
3146        try (MDCHelper mdcHelper = new MDCHelper()) {
3147            vetoStated.set(false);
3148            startDate = new Date();
3149            stopWatch.restart();
3150            log.info("Apache Camel {} (CamelContext: {}) is starting", getVersion(), getName());
3151
3152            // Note: This is done on context start as we want to avoid doing it during object construction
3153            // where we could be dealing with CDI proxied camel contexts which may never be started (CAMEL-9657)
3154            // [TODO] Remove in 3.0
3155            Container.Instance.manage(this);
3156
3157            // Start the route controller
3158            ServiceHelper.startServices(this.routeController);
3159
3160            doNotStartRoutesOnFirstStart = !firstStartDone && !isAutoStartup();
3161
3162            // if the context was configured with auto startup = false, and we are already started,
3163            // then we may need to start the routes on the 2nd start call
3164            if (firstStartDone && !isAutoStartup() && isStarted()) {
3165                // invoke this logic to warm up the routes and if possible also start the routes
3166                doStartOrResumeRoutes(routeServices, true, true, false, true);
3167            }
3168
3169            // super will invoke doStart which will prepare internal services and start routes etc.
3170            try {
3171                firstStartDone = true;
3172                super.start();
3173            } catch (VetoCamelContextStartException e) {
3174                // mark we veto against starting Camel
3175                vetoStated.set(true);
3176                if (e.isRethrowException()) {
3177                    throw e;
3178                } else {
3179                    log.info("CamelContext ({}) vetoed to not start due {}", getName(), e.getMessage());
3180                    // swallow exception and change state of this camel context to stopped
3181                    stop();
3182                    return;
3183                }
3184            }
3185
3186            if (log.isInfoEnabled()) {
3187                // count how many routes are actually started
3188                int started = 0;
3189                for (Route route : getRoutes()) {
3190                    ServiceStatus status = getRouteStatus(route.getId());
3191                    if (status != null && status.isStarted()) {
3192                        started++;
3193                    }
3194                }
3195
3196                final Collection<Route> controlledRoutes = getRouteController().getControlledRoutes();
3197                if (controlledRoutes.isEmpty()) {
3198                    log.info("Total {} routes, of which {} are started",
3199                        getRoutes().size(),
3200                        started);
3201                } else {
3202                    log.info("Total {} routes, of which {} are started, and {} are managed by RouteController: {}",
3203                        getRoutes().size(),
3204                        started,
3205                        controlledRoutes.size(),
3206                        getRouteController().getClass().getName()
3207                    );
3208                }
3209                log.info("Apache Camel {} (CamelContext: {}) started in {}", getVersion(), getName(), TimeUtils.printDuration(stopWatch.taken()));
3210            }
3211
3212            // okay the routes has been started so emit event that CamelContext has started (here at the end)
3213            EventHelper.notifyCamelContextStarted(this);
3214
3215            // now call the startup listeners where the routes has been started
3216            for (StartupListener startup : startupListeners) {
3217                if (startup instanceof ExtendedStartupListener) {
3218                    ((ExtendedStartupListener) startup).onCamelContextFullyStarted(this, isStarted());
3219                }
3220            }
3221        }
3222    }
3223
3224    @Override
3225    public void stop() throws Exception {
3226        try (MDCHelper mdcHelper = new MDCHelper()) {
3227            super.stop();
3228        }
3229    }
3230
3231    @Override
3232    public void suspend() throws Exception {
3233        try (MDCHelper mdcHelper = new MDCHelper()) {
3234            super.suspend();
3235        }
3236    }
3237
3238    @Override
3239    public void resume() throws Exception {
3240        try (MDCHelper mdcHelper = new MDCHelper()) {
3241            super.resume();
3242        }
3243    }
3244
3245    @Override
3246    public void shutdown() throws Exception {
3247        try (MDCHelper mdcHelper = new MDCHelper()) {
3248            super.shutdown();
3249        }
3250    }
3251
3252    // Implementation methods
3253    // -----------------------------------------------------------------------
3254
3255    protected synchronized void doStart() throws Exception {
3256        doWithDefinedClassLoader(new Callable<Void>() {
3257            @Override
3258            public Void call() throws Exception {
3259                try {
3260                    doStartCamel();
3261                    return null;
3262                } catch (Exception e) {
3263                    // fire event that we failed to start
3264                    EventHelper.notifyCamelContextStartupFailed(DefaultCamelContext.this, e);
3265                    // rethrow cause
3266                    throw e;
3267                }
3268            }
3269        });
3270    }
3271
3272    private <T> T doWithDefinedClassLoader(Callable<T> callable) throws Exception {
3273        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
3274        try {
3275            // Using the ApplicationClassLoader as the default for TCCL
3276            if (applicationContextClassLoader != null) {
3277                Thread.currentThread().setContextClassLoader(applicationContextClassLoader);
3278            }
3279            return callable.call();
3280        } finally {
3281            Thread.currentThread().setContextClassLoader(tccl);
3282        }
3283    }
3284
3285    protected void doStartCamel() throws Exception {
3286
3287        // custom properties may use property placeholders so resolve those early on
3288        if (globalOptions != null && !globalOptions.isEmpty()) {
3289            for (Map.Entry<String, String> entry : globalOptions.entrySet()) {
3290                String key = entry.getKey();
3291                String value = entry.getValue();
3292                if (value != null) {
3293                    String replaced = resolvePropertyPlaceholders(value);
3294                    if (!value.equals(replaced)) {
3295                        if (log.isDebugEnabled()) {
3296                            log.debug("Camel property with key {} replaced value from {} -> {}", key, value, replaced);
3297                        }
3298                        entry.setValue(replaced);
3299                    }
3300                }
3301            }
3302        }
3303
3304        if (classResolver instanceof CamelContextAware) {
3305            ((CamelContextAware) classResolver).setCamelContext(this);
3306        }
3307
3308        if (log.isDebugEnabled()) {
3309            log.debug("Using ClassResolver={}, PackageScanClassResolver={}, ApplicationContextClassLoader={}, RouteController={}",
3310                getClassResolver(), getPackageScanClassResolver(), getApplicationContextClassLoader(), getRouteController());
3311        }
3312
3313        if (isStreamCaching()) {
3314            log.info("StreamCaching is enabled on CamelContext: {}", getName());
3315        }
3316
3317        if (isTracing()) {
3318            // tracing is added in the DefaultChannel so we can enable it on the fly
3319            log.info("Tracing is enabled on CamelContext: {}", getName());
3320        }
3321
3322        if (isUseMDCLogging()) {
3323            // log if MDC has been enabled
3324            log.info("MDC logging is enabled on CamelContext: {}", getName());
3325        }
3326
3327        if (isHandleFault()) {
3328            // only add a new handle fault if not already configured
3329            if (HandleFault.getHandleFault(this) == null) {
3330                log.info("HandleFault is enabled on CamelContext: {}", getName());
3331                addInterceptStrategy(new HandleFault());
3332            }
3333        }
3334
3335        if (getDelayer() != null && getDelayer() > 0) {
3336            log.info("Delayer is enabled with: {} ms. on CamelContext: {}", getDelayer(), getName());
3337        }
3338
3339        // register debugger
3340        if (getDebugger() != null) {
3341            log.info("Debugger: {} is enabled on CamelContext: {}", getDebugger(), getName());
3342            // register this camel context on the debugger
3343            getDebugger().setCamelContext(this);
3344            startService(getDebugger());
3345            addInterceptStrategy(new Debug(getDebugger()));
3346        }
3347
3348        // start management strategy before lifecycles are started
3349        ManagementStrategy managementStrategy = getManagementStrategy();
3350        // inject CamelContext if aware
3351        if (managementStrategy instanceof CamelContextAware) {
3352            ((CamelContextAware) managementStrategy).setCamelContext(this);
3353        }
3354        ServiceHelper.startService(managementStrategy);
3355
3356        // start lifecycle strategies
3357        ServiceHelper.startServices(lifecycleStrategies);
3358        Iterator<LifecycleStrategy> it = lifecycleStrategies.iterator();
3359        while (it.hasNext()) {
3360            LifecycleStrategy strategy = it.next();
3361            try {
3362                strategy.onContextStart(this);
3363            } catch (VetoCamelContextStartException e) {
3364                // okay we should not start Camel since it was vetoed
3365                log.warn("Lifecycle strategy vetoed starting CamelContext ({}) due: {}", getName(), e.getMessage());
3366                throw e;
3367            } catch (Exception e) {
3368                log.warn("Lifecycle strategy " + strategy + " failed starting CamelContext ({}) due: {}", getName(), e.getMessage());
3369                throw e;
3370            }
3371        }
3372
3373        // start notifiers as services
3374        for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
3375            if (notifier instanceof Service) {
3376                Service service = (Service) notifier;
3377                for (LifecycleStrategy strategy : lifecycleStrategies) {
3378                    strategy.onServiceAdd(this, service, null);
3379                }
3380            }
3381            if (notifier instanceof Service) {
3382                startService((Service)notifier);
3383            }
3384        }
3385
3386        // must let some bootstrap service be started before we can notify the starting event
3387        EventHelper.notifyCamelContextStarting(this);
3388
3389        forceLazyInitialization();
3390
3391        // re-create endpoint registry as the cache size limit may be set after the constructor of this instance was called.
3392        // and we needed to create endpoints up-front as it may be accessed before this context is started
3393        endpoints = createEndpointRegistry(endpoints);
3394        // add this as service and force pre-start them
3395        addService(endpoints, true, true);
3396        // special for executorServiceManager as want to stop it manually so false in stopOnShutdown
3397        addService(executorServiceManager, false, true);
3398        addService(producerServicePool, true, true);
3399        addService(pollingConsumerServicePool, true, true);
3400        addService(inflightRepository, true, true);
3401        addService(asyncProcessorAwaitManager, true, true);
3402        addService(shutdownStrategy, true, true);
3403        addService(packageScanClassResolver, true, true);
3404        addService(restRegistry, true, true);
3405        addService(messageHistoryFactory, true, true);
3406        addService(runtimeCamelCatalog, true, true);
3407        if (reloadStrategy != null) {
3408            log.info("Using ReloadStrategy: {}", reloadStrategy);
3409            addService(reloadStrategy, true, true);
3410        }
3411
3412        // Initialize declarative transformer/validator registry
3413        transformerRegistry = createTransformerRegistry(transformers);
3414        addService(transformerRegistry, true, true);
3415        validatorRegistry = createValidatorRegistry(validators);
3416        addService(validatorRegistry, true, true);
3417
3418        // optimised to not include runtimeEndpointRegistry unlesstartServices its enabled or JMX statistics is in extended mode
3419        if (runtimeEndpointRegistry == null && getManagementStrategy() != null && getManagementStrategy().getManagementAgent() != null) {
3420            Boolean isEnabled = getManagementStrategy().getManagementAgent().getEndpointRuntimeStatisticsEnabled();
3421            boolean isExtended = getManagementStrategy().getManagementAgent().getStatisticsLevel().isExtended();
3422            // extended mode is either if we use Extended statistics level or the option is explicit enabled
3423            boolean extended = isExtended || isEnabled != null && isEnabled;
3424            if (extended) {
3425                runtimeEndpointRegistry = new DefaultRuntimeEndpointRegistry();
3426            }
3427        }
3428        if (runtimeEndpointRegistry != null) {
3429            if (runtimeEndpointRegistry instanceof EventNotifier) {
3430                getManagementStrategy().addEventNotifier((EventNotifier) runtimeEndpointRegistry);
3431            }
3432            addService(runtimeEndpointRegistry, true, true);
3433        }
3434
3435        // eager lookup any configured properties component to avoid subsequent lookup attempts which may impact performance
3436        // due we use properties component for property placeholder resolution at runtime
3437        Component existing = CamelContextHelper.lookupPropertiesComponent(this, false);
3438        if (existing != null) {
3439            // store reference to the existing properties component
3440            if (existing instanceof PropertiesComponent) {
3441                propertiesComponent = (PropertiesComponent) existing;
3442            } else {
3443                // properties component must be expected type
3444                throw new IllegalArgumentException("Found properties component of type: " + existing.getClass() + " instead of expected: " + PropertiesComponent.class);
3445            }
3446        }
3447
3448        // start components
3449        startServices(components.values());
3450
3451        // start the route definitions before the routes is started
3452        startRouteDefinitions(routeDefinitions);
3453
3454        // 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
3455        boolean streamCachingInUse = isStreamCaching();
3456        if (!streamCachingInUse) {
3457            for (RouteDefinition route : routeDefinitions) {
3458                Boolean routeCache = CamelContextHelper.parseBoolean(this, route.getStreamCache());
3459                if (routeCache != null && routeCache) {
3460                    streamCachingInUse = true;
3461                    break;
3462                }
3463            }
3464        }
3465
3466        if (isUseDataType()) {
3467            // log if DataType has been enabled
3468            log.info("Message DataType is enabled on CamelContext: {}", getName());
3469        }
3470
3471        if (streamCachingInUse) {
3472            // stream caching is in use so enable the strategy
3473            getStreamCachingStrategy().setEnabled(true);
3474            addService(getStreamCachingStrategy(), true, true);
3475        } else {
3476            // log if stream caching is not in use as this can help people to enable it if they use streams
3477            log.info("StreamCaching is not in use. If using streams then its recommended to enable stream caching."
3478                    + " See more details at http://camel.apache.org/stream-caching.html");
3479        }
3480
3481        if (isAllowUseOriginalMessage()) {
3482            log.debug("AllowUseOriginalMessage enabled because UseOriginalMessage is in use");
3483        }
3484
3485        // use resolver to find the headers map factory to be used, if we are using the default
3486        if (headersMapFactory instanceof DefaultHeadersMapFactory) {
3487            headersMapFactory = new HeadersMapFactoryResolver().resolve(this);
3488        }
3489
3490        log.debug("Using HeadersMapFactory: {}", headersMapFactory);
3491        if (!headersMapFactory.isCaseInsensitive()) {
3492            log.info("HeadersMapFactory: {} is case-sensitive which can cause problems for protocols such as HTTP based, which rely on case-insensitive headers.", getHeadersMapFactory());
3493        }
3494
3495        // start routes
3496        if (doNotStartRoutesOnFirstStart) {
3497            log.debug("Skip starting routes as CamelContext has been configured with autoStartup=false");
3498        }
3499
3500        // invoke this logic to warmup the routes and if possible also start the routes
3501        doStartOrResumeRoutes(routeServices, true, !doNotStartRoutesOnFirstStart, false, true);
3502
3503        // starting will continue in the start method
3504    }
3505
3506    protected synchronized void doStop() throws Exception {
3507        stopWatch.restart();
3508        log.info("Apache Camel {} (CamelContext: {}) is shutting down", getVersion(), getName());
3509        EventHelper.notifyCamelContextStopping(this);
3510        
3511        // Stop the route controller
3512        ServiceHelper.stopAndShutdownService(this.routeController);
3513
3514        // stop route inputs in the same order as they was started so we stop the very first inputs first
3515        try {
3516            // force shutting down routes as they may otherwise cause shutdown to hang
3517            shutdownStrategy.shutdownForced(this, getRouteStartupOrder());
3518        } catch (Throwable e) {
3519            log.warn("Error occurred while shutting down routes. This exception will be ignored.", e);
3520        }
3521
3522        // shutdown await manager to trigger interrupt of blocked threads to attempt to free these threads graceful
3523        shutdownServices(asyncProcessorAwaitManager);
3524
3525        routeStartupOrder.sort(new Comparator<RouteStartupOrder>() {
3526            @Override
3527            public int compare(RouteStartupOrder o1, RouteStartupOrder o2) {
3528                // Reversed order
3529                return Integer.compare(o2.getStartupOrder(), o1.getStartupOrder());
3530            }
3531        });
3532        List<RouteService> list = new ArrayList<>();
3533        for (RouteStartupOrder startupOrder : routeStartupOrder) {
3534            DefaultRouteStartupOrder order = (DefaultRouteStartupOrder) startupOrder;
3535            RouteService routeService = order.getRouteService();
3536            list.add(routeService);
3537        }
3538        shutdownServices(list, false);
3539        // do not clear route services or startup listeners as we can start Camel again and get the route back as before
3540        routeStartupOrder.clear();
3541
3542        // but clear any suspend routes
3543        suspendedRouteServices.clear();
3544
3545        // stop consumers from the services to close first, such as POJO consumer (eg @Consumer)
3546        // which we need to stop after the routes, as a POJO consumer is essentially a route also
3547        for (Service service : servicesToStop) {
3548            if (service instanceof Consumer) {
3549                shutdownServices(service);
3550            }
3551        }
3552
3553        // the stop order is important
3554
3555        // shutdown default error handler thread pool
3556        if (errorHandlerExecutorService != null) {
3557            // force shutting down the thread pool
3558            getExecutorServiceManager().shutdownNow(errorHandlerExecutorService);
3559            errorHandlerExecutorService = null;
3560        }
3561
3562        // shutdown debugger
3563        ServiceHelper.stopAndShutdownService(getDebugger());
3564
3565        shutdownServices(endpoints.values());
3566        endpoints.clear();
3567
3568        shutdownServices(components.values());
3569        components.clear();
3570
3571        shutdownServices(languages.values());
3572        languages.clear();
3573
3574        try {
3575            for (LifecycleStrategy strategy : lifecycleStrategies) {
3576                strategy.onContextStop(this);
3577            }
3578        } catch (Throwable e) {
3579            log.warn("Error occurred while stopping lifecycle strategies. This exception will be ignored.", e);
3580        }
3581
3582        // shutdown services as late as possible
3583        shutdownServices(servicesToStop);
3584        servicesToStop.clear();
3585
3586        // must notify that we are stopped before stopping the management strategy
3587        EventHelper.notifyCamelContextStopped(this);
3588
3589        // stop the notifier service
3590        for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
3591            shutdownServices(notifier);
3592        }
3593
3594        // shutdown executor service and management as the last one
3595        shutdownServices(executorServiceManager);
3596        shutdownServices(managementStrategy);
3597        shutdownServices(managementMBeanAssembler);
3598        shutdownServices(lifecycleStrategies);
3599        // do not clear lifecycleStrategies as we can start Camel again and get the route back as before
3600
3601        // stop the lazy created so they can be re-created on restart
3602        forceStopLazyInitialization();
3603
3604        // stop to clear introspection cache
3605        IntrospectionSupport.stop();
3606
3607        if (log.isInfoEnabled()) {
3608            log.info("Apache Camel " + getVersion() + " (CamelContext: " + getName() + ") uptime {}", getUptime());
3609            log.info("Apache Camel {} (CamelContext: {}) is shutdown in {}", getVersion(), getName(), TimeUtils.printDuration(stopWatch.taken()));
3610        }
3611
3612        // and clear start date
3613        startDate = null;
3614
3615        // [TODO] Remove in 3.0
3616        Container.Instance.unmanage(this);
3617    }
3618
3619    /**
3620     * Starts or resumes the routes
3621     *
3622     * @param routeServices  the routes to start (will only start a route if its not already started)
3623     * @param checkClash     whether to check for startup ordering clash
3624     * @param startConsumer  whether the route consumer should be started. Can be used to warmup the route without starting the consumer.
3625     * @param resumeConsumer whether the route consumer should be resumed.
3626     * @param addingRoutes   whether we are adding new routes
3627     * @throws Exception is thrown if error starting routes
3628     */
3629    protected void doStartOrResumeRoutes(Map<String, RouteService> routeServices, boolean checkClash,
3630                                         boolean startConsumer, boolean resumeConsumer, boolean addingRoutes) throws Exception {
3631        isStartingRoutes.set(true);
3632        try {
3633            // filter out already started routes
3634            Map<String, RouteService> filtered = new LinkedHashMap<>();
3635            for (Map.Entry<String, RouteService> entry : routeServices.entrySet()) {
3636                boolean startable = false;
3637
3638                Consumer consumer = entry.getValue().getRoutes().iterator().next().getConsumer();
3639                if (consumer instanceof SuspendableService) {
3640                    // consumer could be suspended, which is not reflected in the RouteService status
3641                    startable = ((SuspendableService) consumer).isSuspended();
3642                }
3643
3644                if (!startable && consumer instanceof StatefulService) {
3645                    // consumer could be stopped, which is not reflected in the RouteService status
3646                    startable = ((StatefulService) consumer).getStatus().isStartable();
3647                } else if (!startable) {
3648                    // no consumer so use state from route service
3649                    startable = entry.getValue().getStatus().isStartable();
3650                }
3651
3652                if (startable) {
3653                    filtered.put(entry.getKey(), entry.getValue());
3654                }
3655            }
3656
3657            // the context is in last phase of staring, so lets start the routes
3658            safelyStartRouteServices(checkClash, startConsumer, resumeConsumer, addingRoutes, filtered.values());
3659
3660        } finally {
3661            isStartingRoutes.remove();
3662        }
3663    }
3664
3665    protected boolean routeSupportsSuspension(String routeId) {
3666        RouteService routeService = routeServices.get(routeId);
3667        if (routeService != null) {
3668            return routeService.getRoutes().iterator().next().supportsSuspension();
3669        }
3670        return false;
3671    }
3672
3673    private void shutdownServices(Object service) {
3674        // do not rethrow exception as we want to keep shutting down in case of problems
3675
3676        // allow us to do custom work before delegating to service helper
3677        try {
3678            if (service instanceof Service) {
3679                ServiceHelper.stopAndShutdownService(service);
3680            } else if (service instanceof Collection) {
3681                ServiceHelper.stopAndShutdownServices((Collection<?>)service);
3682            }
3683        } catch (Throwable e) {
3684            log.warn("Error occurred while shutting down service: " + service + ". This exception will be ignored.", e);
3685            // fire event
3686            EventHelper.notifyServiceStopFailure(this, service, e);
3687        }
3688    }
3689
3690    private void shutdownServices(Collection<?> services) {
3691        // reverse stopping by default
3692        shutdownServices(services, true);
3693    }
3694
3695    private void shutdownServices(Collection<?> services, boolean reverse) {
3696        Collection<?> list = services;
3697        if (reverse) {
3698            List<Object> reverseList = new ArrayList<>(services);
3699            Collections.reverse(reverseList);
3700            list = reverseList;
3701        }
3702
3703        for (Object service : list) {
3704            shutdownServices(service);
3705        }
3706    }
3707
3708    private void startService(Service service) throws Exception {
3709        // and register startup aware so they can be notified when
3710        // camel context has been started
3711        if (service instanceof StartupListener) {
3712            StartupListener listener = (StartupListener) service;
3713            addStartupListener(listener);
3714        }
3715
3716        if (service instanceof CamelContextAware) {
3717            CamelContextAware aware = (CamelContextAware) service;
3718            aware.setCamelContext(this);
3719        }
3720
3721        service.start();
3722    }
3723
3724    private void startServices(Collection<?> services) throws Exception {
3725        for (Object element : services) {
3726            if (element instanceof Service) {
3727                startService((Service)element);
3728            }
3729        }
3730    }
3731
3732    private void stopServices(Object service) throws Exception {
3733        // allow us to do custom work before delegating to service helper
3734        try {
3735            ServiceHelper.stopService(service);
3736        } catch (Exception e) {
3737            // fire event
3738            EventHelper.notifyServiceStopFailure(this, service, e);
3739            // rethrow to signal error with stopping
3740            throw e;
3741        }
3742    }
3743
3744    protected void startRouteDefinitions(Collection<RouteDefinition> list) throws Exception {
3745        if (list != null) {
3746            for (RouteDefinition route : list) {
3747                startRoute(route);
3748            }
3749        }
3750    }
3751
3752    /**
3753     * Starts the given route service
3754     */
3755    protected synchronized void startRouteService(RouteService routeService, boolean addingRoutes) throws Exception {
3756        // we may already be starting routes so remember this, so we can unset accordingly in finally block
3757        boolean alreadyStartingRoutes = isStartingRoutes();
3758        if (!alreadyStartingRoutes) {
3759            isStartingRoutes.set(true);
3760        }
3761
3762        try {
3763            // the route service could have been suspended, and if so then resume it instead
3764            if (routeService.getStatus().isSuspended()) {
3765                resumeRouteService(routeService);
3766            } else {
3767                // start the route service
3768                routeServices.put(routeService.getId(), routeService);
3769                if (shouldStartRoutes()) {
3770                    // this method will log the routes being started
3771                    safelyStartRouteServices(true, true, true, false, addingRoutes, routeService);
3772                    // start route services if it was configured to auto startup and we are not adding routes
3773                    boolean autoStartup = routeService.getRouteDefinition().isAutoStartup(this) && this.isAutoStartup();
3774                    if (!addingRoutes || autoStartup) {
3775                        // start the route since auto start is enabled or we are starting a route (not adding new routes)
3776                        routeService.start();
3777                    }
3778                }
3779            }
3780        } finally {
3781            if (!alreadyStartingRoutes) {
3782                isStartingRoutes.remove();
3783            }
3784        }
3785    }
3786
3787    /**
3788     * Resumes the given route service
3789     */
3790    protected synchronized void resumeRouteService(RouteService routeService) throws Exception {
3791        // the route service could have been stopped, and if so then start it instead
3792        if (!routeService.getStatus().isSuspended()) {
3793            startRouteService(routeService, false);
3794        } else {
3795            // resume the route service
3796            if (shouldStartRoutes()) {
3797                // this method will log the routes being started
3798                safelyStartRouteServices(true, false, true, true, false, routeService);
3799                // must resume route service as well
3800                routeService.resume();
3801            }
3802        }
3803    }
3804
3805    protected synchronized void stopRouteService(RouteService routeService, boolean removingRoutes) throws Exception {
3806        routeService.setRemovingRoutes(removingRoutes);
3807        stopRouteService(routeService);
3808    }
3809
3810    protected void logRouteState(Route route, String state) {
3811        if (log.isInfoEnabled()) {
3812            if (route.getConsumer() != null) {
3813                log.info("Route: {} is {}, was consuming from: {}", route.getId(), state, route.getConsumer().getEndpoint());
3814            } else {
3815                log.info("Route: {} is {}.", route.getId(), state);
3816            }
3817        }
3818    }
3819
3820    protected synchronized void stopRouteService(RouteService routeService) throws Exception {
3821        routeService.stop();
3822        for (Route route : routeService.getRoutes()) {
3823            logRouteState(route, "stopped");
3824        }
3825    }
3826
3827    protected synchronized void shutdownRouteService(RouteService routeService) throws Exception {
3828        routeService.shutdown();
3829        for (Route route : routeService.getRoutes()) {
3830            logRouteState(route, "shutdown and removed");
3831        }
3832    }
3833
3834    protected synchronized void suspendRouteService(RouteService routeService) throws Exception {
3835        routeService.setRemovingRoutes(false);
3836        routeService.suspend();
3837        for (Route route : routeService.getRoutes()) {
3838            logRouteState(route, "suspended");
3839        }
3840    }
3841
3842    /**
3843     * Starts the routes services in a proper manner which ensures the routes will be started in correct order,
3844     * check for clash and that the routes will also be shutdown in correct order as well.
3845     * <p/>
3846     * This method <b>must</b> be used to start routes in a safe manner.
3847     *
3848     * @param checkClash     whether to check for startup order clash
3849     * @param startConsumer  whether the route consumer should be started. Can be used to warmup the route without starting the consumer.
3850     * @param resumeConsumer whether the route consumer should be resumed.
3851     * @param addingRoutes   whether we are adding new routes
3852     * @param routeServices  the routes
3853     * @throws Exception is thrown if error starting the routes
3854     */
3855    protected synchronized void safelyStartRouteServices(boolean checkClash, boolean startConsumer, boolean resumeConsumer,
3856                                                         boolean addingRoutes, Collection<RouteService> routeServices) throws Exception {
3857        // list of inputs to start when all the routes have been prepared for starting
3858        // we use a tree map so the routes will be ordered according to startup order defined on the route
3859        Map<Integer, DefaultRouteStartupOrder> inputs = new TreeMap<>();
3860
3861        // figure out the order in which the routes should be started
3862        for (RouteService routeService : routeServices) {
3863            DefaultRouteStartupOrder order = doPrepareRouteToBeStarted(routeService);
3864            // check for clash before we add it as input
3865            if (checkClash) {
3866                doCheckStartupOrderClash(order, inputs);
3867            }
3868            inputs.put(order.getStartupOrder(), order);
3869        }
3870
3871        // warm up routes before we start them
3872        doWarmUpRoutes(inputs, startConsumer);
3873
3874        // sort the startup listeners so they are started in the right order
3875        startupListeners.sort(OrderedComparator.get());
3876        // now call the startup listeners where the routes has been warmed up
3877        // (only the actual route consumer has not yet been started)
3878        for (StartupListener startup : startupListeners) {
3879            startup.onCamelContextStarted(this, isStarted());
3880        }
3881        // because the consumers may also register startup listeners we need to reset
3882        // the already started listeners
3883        List<StartupListener> backup = new ArrayList<>(startupListeners);
3884        startupListeners.clear();
3885
3886        // now start the consumers
3887        if (startConsumer) {
3888            if (resumeConsumer) {
3889                // and now resume the routes
3890                doResumeRouteConsumers(inputs, addingRoutes);
3891            } else {
3892                // and now start the routes
3893                // and check for clash with multiple consumers of the same endpoints which is not allowed
3894                doStartRouteConsumers(inputs, addingRoutes);
3895            }
3896        }
3897
3898        // sort the startup listeners so they are started in the right order
3899        startupListeners.sort(OrderedComparator.get());
3900        // now the consumers that was just started may also add new StartupListeners (such as timer)
3901        // so we need to ensure they get started as well
3902        for (StartupListener startup : startupListeners) {
3903            startup.onCamelContextStarted(this, isStarted());
3904        }
3905        // and add the previous started startup listeners to the list so we have them all
3906        startupListeners.addAll(0, backup);
3907
3908        // inputs no longer needed
3909        inputs.clear();
3910    }
3911
3912    /**
3913     * @see #safelyStartRouteServices(boolean,boolean,boolean,boolean,java.util.Collection)
3914     */
3915    protected synchronized void safelyStartRouteServices(boolean forceAutoStart, boolean checkClash, boolean startConsumer,
3916                                                         boolean resumeConsumer, boolean addingRoutes, RouteService... routeServices) throws Exception {
3917        safelyStartRouteServices(checkClash, startConsumer, resumeConsumer, addingRoutes, Arrays.asList(routeServices));
3918    }
3919
3920    private DefaultRouteStartupOrder doPrepareRouteToBeStarted(RouteService routeService) {
3921        // add the inputs from this route service to the list to start afterwards
3922        // should be ordered according to the startup number
3923        Integer startupOrder = routeService.getRouteDefinition().getStartupOrder();
3924        if (startupOrder == null) {
3925            // auto assign a default startup order
3926            startupOrder = defaultRouteStartupOrder++;
3927        }
3928
3929        // create holder object that contains information about this route to be started
3930        Route route = routeService.getRoutes().iterator().next();
3931        return new DefaultRouteStartupOrder(startupOrder, route, routeService);
3932    }
3933
3934    private boolean doCheckStartupOrderClash(DefaultRouteStartupOrder answer, Map<Integer, DefaultRouteStartupOrder> inputs) throws FailedToStartRouteException {
3935        // check for clash by startupOrder id
3936        DefaultRouteStartupOrder other = inputs.get(answer.getStartupOrder());
3937        if (other != null && answer != other) {
3938            String otherId = other.getRoute().getId();
3939            throw new FailedToStartRouteException(answer.getRoute().getId(), "startupOrder clash. Route " + otherId + " already has startupOrder "
3940                + answer.getStartupOrder() + " configured which this route have as well. Please correct startupOrder to be unique among all your routes.");
3941        }
3942        // check in existing already started as well
3943        for (RouteStartupOrder order : routeStartupOrder) {
3944            String otherId = order.getRoute().getId();
3945            if (answer.getRoute().getId().equals(otherId)) {
3946                // its the same route id so skip clash check as its the same route (can happen when using suspend/resume)
3947            } else if (answer.getStartupOrder() == order.getStartupOrder()) {
3948                throw new FailedToStartRouteException(answer.getRoute().getId(), "startupOrder clash. Route " + otherId + " already has startupOrder "
3949                    + answer.getStartupOrder() + " configured which this route have as well. Please correct startupOrder to be unique among all your routes.");
3950            }
3951        }
3952        return true;
3953    }
3954
3955    private void doWarmUpRoutes(Map<Integer, DefaultRouteStartupOrder> inputs, boolean autoStartup) throws Exception {
3956        // now prepare the routes by starting its services before we start the input
3957        for (Map.Entry<Integer, DefaultRouteStartupOrder> entry : inputs.entrySet()) {
3958            // defer starting inputs till later as we want to prepare the routes by starting
3959            // all their processors and child services etc.
3960            // then later we open the floods to Camel by starting the inputs
3961            // what this does is to ensure Camel is more robust on starting routes as all routes
3962            // will then be prepared in time before we start inputs which will consume messages to be routed
3963            RouteService routeService = entry.getValue().getRouteService();
3964            log.debug("Warming up route id: {} having autoStartup={}", routeService.getId(), autoStartup);
3965            routeService.warmUp();
3966        }
3967    }
3968
3969    private void doResumeRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean addingRoutes) throws Exception {
3970        doStartOrResumeRouteConsumers(inputs, true, addingRoutes);
3971    }
3972
3973    private void doStartRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean addingRoutes) throws Exception {
3974        doStartOrResumeRouteConsumers(inputs, false, addingRoutes);
3975    }
3976
3977    private void doStartOrResumeRouteConsumers(Map<Integer, DefaultRouteStartupOrder> inputs, boolean resumeOnly, boolean addingRoute) throws Exception {
3978        List<Endpoint> routeInputs = new ArrayList<>();
3979
3980        for (Map.Entry<Integer, DefaultRouteStartupOrder> entry : inputs.entrySet()) {
3981            Integer order = entry.getKey();
3982            Route route = entry.getValue().getRoute();
3983            RouteService routeService = entry.getValue().getRouteService();
3984
3985            // if we are starting camel, then skip routes which are configured to not be auto started
3986            boolean autoStartup = routeService.getRouteDefinition().isAutoStartup(this) && this.isAutoStartup();
3987            if (addingRoute && !autoStartup) {
3988                log.info("Skipping starting of route {} as its configured with autoStartup=false", routeService.getId());
3989                continue;
3990            }
3991
3992            // start the service
3993            for (Consumer consumer : routeService.getInputs().values()) {
3994                Endpoint endpoint = consumer.getEndpoint();
3995
3996                // check multiple consumer violation, with the other routes to be started
3997                if (!doCheckMultipleConsumerSupportClash(endpoint, routeInputs)) {
3998                    throw new FailedToStartRouteException(routeService.getId(),
3999                        "Multiple consumers for the same endpoint is not allowed: " + endpoint);
4000                }
4001
4002                // check for multiple consumer violations with existing routes which
4003                // have already been started, or is currently starting
4004                List<Endpoint> existingEndpoints = new ArrayList<>();
4005                for (Route existingRoute : getRoutes()) {
4006                    if (route.getId().equals(existingRoute.getId())) {
4007                        // skip ourselves
4008                        continue;
4009                    }
4010                    Endpoint existing = existingRoute.getEndpoint();
4011                    ServiceStatus status = getRouteStatus(existingRoute.getId());
4012                    if (status != null && (status.isStarted() || status.isStarting())) {
4013                        existingEndpoints.add(existing);
4014                    }
4015                }
4016                if (!doCheckMultipleConsumerSupportClash(endpoint, existingEndpoints)) {
4017                    throw new FailedToStartRouteException(routeService.getId(),
4018                            "Multiple consumers for the same endpoint is not allowed: " + endpoint);
4019                }
4020
4021                // start the consumer on the route
4022                log.debug("Route: {} >>> {}", route.getId(), route);
4023                if (resumeOnly) {
4024                    log.debug("Resuming consumer (order: {}) on route: {}", order, route.getId());
4025                } else {
4026                    log.debug("Starting consumer (order: {}) on route: {}", order, route.getId());
4027                }
4028
4029                if (resumeOnly && route.supportsSuspension()) {
4030                    // if we are resuming and the route can be resumed
4031                    ServiceHelper.resumeService(consumer);
4032                    log.info("Route: {} resumed and consuming from: {}", route.getId(), endpoint);
4033                } else {
4034                    // when starting we should invoke the lifecycle strategies
4035                    for (LifecycleStrategy strategy : lifecycleStrategies) {
4036                        strategy.onServiceAdd(this, consumer, route);
4037                    }
4038                    try {
4039                        startService(consumer);
4040                        route.getProperties().remove("route.start.exception");
4041                    } catch (Exception e) {
4042                        route.getProperties().put("route.start.exception", e);
4043                        throw e;
4044                    }
4045
4046                    log.info("Route: {} started and consuming from: {}", route.getId(), endpoint);
4047                }
4048
4049                routeInputs.add(endpoint);
4050
4051                // add to the order which they was started, so we know how to stop them in reverse order
4052                // but only add if we haven't already registered it before (we dont want to double add when restarting)
4053                boolean found = false;
4054                for (RouteStartupOrder other : routeStartupOrder) {
4055                    if (other.getRoute().getId().equals(route.getId())) {
4056                        found = true;
4057                        break;
4058                    }
4059                }
4060                if (!found) {
4061                    routeStartupOrder.add(entry.getValue());
4062                }
4063            }
4064
4065            if (resumeOnly) {
4066                routeService.resume();
4067            } else {
4068                // and start the route service (no need to start children as they are already warmed up)
4069                try {
4070                    routeService.start(false);
4071                    route.getProperties().remove("route.start.exception");
4072                } catch (Exception e) {
4073                    route.getProperties().put("route.start.exception", e);
4074                    throw e;
4075                }
4076            }
4077        }
4078    }
4079
4080    private boolean doCheckMultipleConsumerSupportClash(Endpoint endpoint, List<Endpoint> routeInputs) {
4081        // is multiple consumers supported
4082        boolean multipleConsumersSupported = false;
4083        if (endpoint instanceof MultipleConsumersSupport) {
4084            multipleConsumersSupported = ((MultipleConsumersSupport) endpoint).isMultipleConsumersSupported();
4085        }
4086
4087        if (multipleConsumersSupported) {
4088            // multiple consumer allowed, so return true
4089            return true;
4090        }
4091
4092        // check in progress list
4093        if (routeInputs.contains(endpoint)) {
4094            return false;
4095        }
4096
4097        return true;
4098    }
4099
4100    /**
4101     * Force some lazy initialization to occur upfront before we start any
4102     * components and create routes
4103     */
4104    protected void forceLazyInitialization() {
4105        getRegistry();
4106        getInjector();
4107        getLanguageResolver();
4108        getTypeConverterRegistry();
4109        getTypeConverter();
4110
4111        if (isTypeConverterStatisticsEnabled() != null) {
4112            getTypeConverterRegistry().getStatistics().setStatisticsEnabled(isTypeConverterStatisticsEnabled());
4113        }
4114
4115        // resolve simple language to initialize it
4116        resolveLanguage("simple");
4117    }
4118
4119    /**
4120     * Force clear lazy initialization so they can be re-created on restart
4121     */
4122    protected void forceStopLazyInitialization() {
4123        injector = null;
4124        languageResolver = null;
4125        typeConverterRegistry = null;
4126        typeConverter = null;
4127    }
4128
4129    /**
4130     * Lazily create a default implementation
4131     */
4132    protected TypeConverter createTypeConverter() {
4133        BaseTypeConverterRegistry answer;
4134        if (isLazyLoadTypeConverters()) {
4135            answer = new LazyLoadingTypeConverter(packageScanClassResolver, getInjector(), getDefaultFactoryFinder());
4136        } else {
4137            answer = new DefaultTypeConverter(packageScanClassResolver, getInjector(), getDefaultFactoryFinder(), isLoadTypeConverters());
4138        }
4139        answer.setCamelContext(this);
4140        setTypeConverterRegistry(answer);
4141        return answer;
4142    }
4143
4144    /**
4145     * Lazily create a default implementation
4146     */
4147    protected Injector createInjector() {
4148        FactoryFinder finder = getDefaultFactoryFinder();
4149        try {
4150            return (Injector) finder.newInstance("Injector");
4151        } catch (NoFactoryAvailableException e) {
4152            // lets use the default injector
4153            return new DefaultInjector(this);
4154        }
4155    }
4156
4157    /**
4158     * Lazily create a default implementation
4159     */
4160    protected ManagementMBeanAssembler createManagementMBeanAssembler() {
4161        return new DefaultManagementMBeanAssembler(this);
4162    }
4163
4164    /**
4165     * Lazily create a default implementation
4166     */
4167    protected ComponentResolver createComponentResolver() {
4168        return new DefaultComponentResolver();
4169    }
4170
4171    /**
4172     * Lazily create a default implementation
4173     */
4174    protected Registry createRegistry() {
4175        JndiRegistry jndi = new JndiRegistry();
4176        try {
4177            // getContext() will force setting up JNDI
4178            jndi.getContext();
4179            return jndi;
4180        } catch (Throwable e) {
4181            log.debug("Cannot create javax.naming.InitialContext due " + e.getMessage() + ". Will fallback and use SimpleRegistry instead. This exception is ignored.", e);
4182            return new SimpleRegistry();
4183        }
4184    }
4185
4186    /**
4187     * A pluggable strategy to allow an endpoint to be created without requiring
4188     * a component to be its factory, such as for looking up the URI inside some
4189     * {@link Registry}
4190     *
4191     * @param uri the uri for the endpoint to be created
4192     * @return the newly created endpoint or null if it could not be resolved
4193     */
4194    protected Endpoint createEndpoint(String uri) {
4195        Object value = getRegistry().lookupByName(uri);
4196        if (value instanceof Endpoint) {
4197            return (Endpoint) value;
4198        } else if (value instanceof Processor) {
4199            return new ProcessorEndpoint(uri, this, (Processor) value);
4200        } else if (value != null) {
4201            return convertBeanToEndpoint(uri, value);
4202        }
4203        return null;
4204    }
4205
4206    /**
4207     * Strategy method for attempting to convert the bean from a {@link Registry} to an endpoint using
4208     * some kind of transformation or wrapper
4209     *
4210     * @param uri  the uri for the endpoint (and name in the registry)
4211     * @param bean the bean to be converted to an endpoint, which will be not null
4212     * @return a new endpoint
4213     */
4214    protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
4215        throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
4216                + " could not be converted to an Endpoint");
4217    }
4218
4219    /**
4220     * Should we start newly added routes?
4221     */
4222    protected boolean shouldStartRoutes() {
4223        return isStarted() && !isStarting();
4224    }
4225
4226    /**
4227     * Gets the properties component in use.
4228     * Returns {@code null} if no properties component is in use.
4229     */
4230    protected PropertiesComponent getPropertiesComponent() {
4231        return propertiesComponent;
4232    }
4233
4234    public void setDataFormats(Map<String, DataFormatDefinition> dataFormats) {
4235        this.dataFormats = dataFormats;
4236    }
4237
4238    public Map<String, DataFormatDefinition> getDataFormats() {
4239        return dataFormats;
4240    }
4241
4242    @Deprecated
4243    public Map<String, String> getProperties() {
4244        return getGlobalOptions();
4245    }
4246
4247    @Override
4248    public Map<String, String> getGlobalOptions() {
4249        return globalOptions;
4250    }
4251
4252    @Deprecated
4253    public void setProperties(Map<String, String> properties) {
4254        this.setGlobalOptions(properties);
4255    }
4256
4257    @Override
4258    public void setGlobalOptions(Map<String, String> globalOptions) {
4259        this.globalOptions = globalOptions;
4260    }
4261
4262    public FactoryFinder getDefaultFactoryFinder() {
4263        if (defaultFactoryFinder == null) {
4264            defaultFactoryFinder = getFactoryFinderResolver().resolveDefaultFactoryFinder(getClassResolver());
4265        }
4266        return defaultFactoryFinder;
4267    }
4268
4269    public FactoryFinderResolver getFactoryFinderResolver() {
4270        if (factoryFinderResolver == null) {
4271            factoryFinderResolver = createFactoryFinderResolver();
4272        }
4273        return factoryFinderResolver;
4274    }
4275
4276    public void setFactoryFinderResolver(FactoryFinderResolver resolver) {
4277        this.factoryFinderResolver = resolver;
4278    }
4279
4280    public FactoryFinder getFactoryFinder(String path) throws NoFactoryAvailableException {
4281        synchronized (factories) {
4282            FactoryFinder answer = factories.get(path);
4283            if (answer == null) {
4284                answer = getFactoryFinderResolver().resolveFactoryFinder(getClassResolver(), path);
4285                factories.put(path, answer);
4286            }
4287            return answer;
4288        }
4289    }
4290
4291    public ClassResolver getClassResolver() {
4292        return classResolver;
4293    }
4294
4295    public void setClassResolver(ClassResolver classResolver) {
4296        this.classResolver = classResolver;
4297    }
4298
4299    public PackageScanClassResolver getPackageScanClassResolver() {
4300        return packageScanClassResolver;
4301    }
4302
4303    public void setPackageScanClassResolver(PackageScanClassResolver packageScanClassResolver) {
4304        this.packageScanClassResolver = packageScanClassResolver;
4305    }
4306
4307    public List<String> getComponentNames() {
4308        List<String> answer = new ArrayList<>();
4309        for (String name : components.keySet()) {
4310            answer.add(name);
4311        }
4312        return answer;
4313    }
4314
4315    public List<String> getLanguageNames() {
4316        synchronized (languages) {
4317            List<String> answer = new ArrayList<>();
4318            for (String name : languages.keySet()) {
4319                answer.add(name);
4320            }
4321            return answer;
4322        }
4323    }
4324
4325    public ModelJAXBContextFactory getModelJAXBContextFactory() {
4326        if (modelJAXBContextFactory == null) {
4327            modelJAXBContextFactory = createModelJAXBContextFactory();
4328        }
4329        return modelJAXBContextFactory;
4330    }
4331
4332    public void setModelJAXBContextFactory(final ModelJAXBContextFactory modelJAXBContextFactory) {
4333        this.modelJAXBContextFactory = modelJAXBContextFactory;
4334    }
4335
4336    public NodeIdFactory getNodeIdFactory() {
4337        return nodeIdFactory;
4338    }
4339
4340    public void setNodeIdFactory(NodeIdFactory idFactory) {
4341        this.nodeIdFactory = idFactory;
4342    }
4343
4344    public ManagementStrategy getManagementStrategy() {
4345        return managementStrategy;
4346    }
4347
4348    public void setManagementStrategy(ManagementStrategy managementStrategy) {
4349        this.managementStrategy = managementStrategy;
4350    }
4351
4352    public InterceptStrategy getDefaultTracer() {
4353        if (defaultTracer == null) {
4354            defaultTracer = new Tracer();
4355        }
4356        return defaultTracer;
4357    }
4358
4359    public void setDefaultTracer(InterceptStrategy tracer) {
4360        this.defaultTracer = tracer;
4361    }
4362
4363    public InterceptStrategy getDefaultBacklogTracer() {
4364        if (defaultBacklogTracer == null) {
4365            defaultBacklogTracer = BacklogTracer.createTracer(this);
4366        }
4367        return defaultBacklogTracer;
4368    }
4369
4370    public void setDefaultBacklogTracer(InterceptStrategy backlogTracer) {
4371        this.defaultBacklogTracer = backlogTracer;
4372    }
4373
4374    public InterceptStrategy getDefaultBacklogDebugger() {
4375        if (defaultBacklogDebugger == null) {
4376            defaultBacklogDebugger = new BacklogDebugger(this);
4377        }
4378        return defaultBacklogDebugger;
4379    }
4380
4381    public void setDefaultBacklogDebugger(InterceptStrategy defaultBacklogDebugger) {
4382        this.defaultBacklogDebugger = defaultBacklogDebugger;
4383    }
4384
4385    public void disableJMX() {
4386        if (isStarting() || isStarted()) {
4387            throw new IllegalStateException("Disabling JMX can only be done when CamelContext has not been started");
4388        }
4389        managementStrategy = new DefaultManagementStrategy(this);
4390        // must clear lifecycle strategies as we add DefaultManagementLifecycleStrategy by default for JMX support
4391        lifecycleStrategies.clear();
4392    }
4393
4394    public InflightRepository getInflightRepository() {
4395        return inflightRepository;
4396    }
4397
4398    public void setInflightRepository(InflightRepository repository) {
4399        this.inflightRepository = repository;
4400    }
4401
4402    public AsyncProcessorAwaitManager getAsyncProcessorAwaitManager() {
4403        return asyncProcessorAwaitManager;
4404    }
4405
4406    public void setAsyncProcessorAwaitManager(AsyncProcessorAwaitManager asyncProcessorAwaitManager) {
4407        this.asyncProcessorAwaitManager = asyncProcessorAwaitManager;
4408    }
4409
4410    public void setAutoStartup(Boolean autoStartup) {
4411        this.autoStartup = autoStartup;
4412    }
4413
4414    public Boolean isAutoStartup() {
4415        return autoStartup != null && autoStartup;
4416    }
4417
4418    @Deprecated
4419    public Boolean isLazyLoadTypeConverters() {
4420        return lazyLoadTypeConverters != null && lazyLoadTypeConverters;
4421    }
4422
4423    @Deprecated
4424    public void setLazyLoadTypeConverters(Boolean lazyLoadTypeConverters) {
4425        this.lazyLoadTypeConverters = lazyLoadTypeConverters;
4426    }
4427
4428    public Boolean isLoadTypeConverters() {
4429        return loadTypeConverters != null && loadTypeConverters;
4430    }
4431
4432    public void setLoadTypeConverters(Boolean loadTypeConverters) {
4433        this.loadTypeConverters = loadTypeConverters;
4434    }
4435
4436    public Boolean isTypeConverterStatisticsEnabled() {
4437        return typeConverterStatisticsEnabled != null && typeConverterStatisticsEnabled;
4438    }
4439
4440    public void setTypeConverterStatisticsEnabled(Boolean typeConverterStatisticsEnabled) {
4441        this.typeConverterStatisticsEnabled = typeConverterStatisticsEnabled;
4442    }
4443
4444    public Boolean isUseMDCLogging() {
4445        return useMDCLogging != null && useMDCLogging;
4446    }
4447
4448    public void setUseMDCLogging(Boolean useMDCLogging) {
4449        this.useMDCLogging = useMDCLogging;
4450    }
4451
4452    public Boolean isUseDataType() {
4453        return useDataType;
4454    }
4455
4456    @Override
4457    public void setUseDataType(Boolean useDataType) {
4458        this.useDataType = useDataType;
4459    }
4460
4461    public Boolean isUseBreadcrumb() {
4462        return useBreadcrumb != null && useBreadcrumb;
4463    }
4464
4465    public void setUseBreadcrumb(Boolean useBreadcrumb) {
4466        this.useBreadcrumb = useBreadcrumb;
4467    }
4468
4469    public ClassLoader getApplicationContextClassLoader() {
4470        return applicationContextClassLoader;
4471    }
4472
4473    public void setApplicationContextClassLoader(ClassLoader classLoader) {
4474        applicationContextClassLoader = classLoader;
4475    }
4476
4477    public DataFormatResolver getDataFormatResolver() {
4478        if (dataFormatResolver == null) {
4479            dataFormatResolver = createDataFormatResolver();
4480        }
4481        return dataFormatResolver;
4482    }
4483
4484    public void setDataFormatResolver(DataFormatResolver dataFormatResolver) {
4485        this.dataFormatResolver = dataFormatResolver;
4486    }
4487
4488    public DataFormat resolveDataFormat(String name) {
4489        DataFormat answer = getDataFormatResolver().resolveDataFormat(name, this);
4490
4491        // inject CamelContext if aware
4492        if (answer instanceof CamelContextAware) {
4493            ((CamelContextAware) answer).setCamelContext(this);
4494        }
4495
4496        return answer;
4497    }
4498
4499    public DataFormat createDataFormat(String name) {
4500        DataFormat answer = getDataFormatResolver().createDataFormat(name, this);
4501
4502        // inject CamelContext if aware
4503        if (answer instanceof CamelContextAware) {
4504            ((CamelContextAware) answer).setCamelContext(this);
4505        }
4506
4507        return answer;
4508    }
4509
4510    public DataFormatDefinition resolveDataFormatDefinition(String name) {
4511        // lookup type and create the data format from it
4512        DataFormatDefinition type = lookup(this, name, DataFormatDefinition.class);
4513        if (type == null && getDataFormats() != null) {
4514            type = getDataFormats().get(name);
4515        }
4516        return type;
4517    }
4518
4519    private static <T> T lookup(CamelContext context, String ref, Class<T> type) {
4520        try {
4521            return context.getRegistry().lookupByNameAndType(ref, type);
4522        } catch (Exception e) {
4523            // need to ignore not same type and return it as null
4524            return null;
4525        }
4526    }
4527
4528    /**
4529     * @deprecated use {@link org.apache.camel.util.CamelContextHelper#lookupPropertiesComponent(org.apache.camel.CamelContext, boolean)}
4530     */
4531    @Deprecated
4532    protected Component lookupPropertiesComponent() {
4533        return CamelContextHelper.lookupPropertiesComponent(this, false);
4534    }
4535
4536    public ShutdownStrategy getShutdownStrategy() {
4537        return shutdownStrategy;
4538    }
4539
4540    public void setShutdownStrategy(ShutdownStrategy shutdownStrategy) {
4541        this.shutdownStrategy = shutdownStrategy;
4542    }
4543
4544    public ShutdownRoute getShutdownRoute() {
4545        return shutdownRoute;
4546    }
4547
4548    public void setShutdownRoute(ShutdownRoute shutdownRoute) {
4549        this.shutdownRoute = shutdownRoute;
4550    }
4551
4552    public ShutdownRunningTask getShutdownRunningTask() {
4553        return shutdownRunningTask;
4554    }
4555
4556    public void setShutdownRunningTask(ShutdownRunningTask shutdownRunningTask) {
4557        this.shutdownRunningTask = shutdownRunningTask;
4558    }
4559
4560    public void setAllowUseOriginalMessage(Boolean allowUseOriginalMessage) {
4561        this.allowUseOriginalMessage = allowUseOriginalMessage;
4562    }
4563
4564    public Boolean isAllowUseOriginalMessage() {
4565        return allowUseOriginalMessage != null && allowUseOriginalMessage;
4566    }
4567
4568    public ExecutorServiceManager getExecutorServiceManager() {
4569        return this.executorServiceManager;
4570    }
4571
4572    @Deprecated
4573    public org.apache.camel.spi.ExecutorServiceStrategy getExecutorServiceStrategy() {
4574        // its okay to create a new instance as its stateless, and just delegate
4575        // ExecutorServiceManager which is the new API
4576        return new DefaultExecutorServiceStrategy(this);
4577    }
4578
4579    public void setExecutorServiceManager(ExecutorServiceManager executorServiceManager) {
4580        this.executorServiceManager = executorServiceManager;
4581    }
4582
4583    public ProcessorFactory getProcessorFactory() {
4584        return processorFactory;
4585    }
4586
4587    public void setProcessorFactory(ProcessorFactory processorFactory) {
4588        this.processorFactory = processorFactory;
4589    }
4590
4591    public MessageHistoryFactory getMessageHistoryFactory() {
4592        return messageHistoryFactory;
4593    }
4594
4595    public void setMessageHistoryFactory(MessageHistoryFactory messageHistoryFactory) {
4596        this.messageHistoryFactory = messageHistoryFactory;
4597    }
4598
4599    public Debugger getDebugger() {
4600        return debugger;
4601    }
4602
4603    public void setDebugger(Debugger debugger) {
4604        this.debugger = debugger;
4605    }
4606
4607    public UuidGenerator getUuidGenerator() {
4608        return uuidGenerator;
4609    }
4610
4611    public void setUuidGenerator(UuidGenerator uuidGenerator) {
4612        this.uuidGenerator = uuidGenerator;
4613    }
4614
4615    public StreamCachingStrategy getStreamCachingStrategy() {
4616        if (streamCachingStrategy == null) {
4617            streamCachingStrategy = new DefaultStreamCachingStrategy();
4618        }
4619        return streamCachingStrategy;
4620    }
4621
4622    public void setStreamCachingStrategy(StreamCachingStrategy streamCachingStrategy) {
4623        this.streamCachingStrategy = streamCachingStrategy;
4624    }
4625
4626    public RestRegistry getRestRegistry() {
4627        return restRegistry;
4628    }
4629
4630    public void setRestRegistry(RestRegistry restRegistry) {
4631        this.restRegistry = restRegistry;
4632    }
4633
4634    @Deprecated
4635    @Override
4636    public String getProperty(String key) {
4637        return getGlobalOption(key);
4638    }
4639
4640    @Override
4641    public String getGlobalOption(String key) {
4642        String value = getGlobalOptions().get(key);
4643        if (ObjectHelper.isNotEmpty(value)) {
4644            try {
4645                value = resolvePropertyPlaceholders(value);
4646            } catch (Exception e) {
4647                throw new RuntimeCamelException("Error getting global option: " + key, e);
4648            }
4649        }
4650        return value;
4651    }
4652
4653    @Override
4654    public ReloadStrategy getReloadStrategy() {
4655        return reloadStrategy;
4656    }
4657
4658    @Override
4659    public void setReloadStrategy(ReloadStrategy reloadStrategy) {
4660        this.reloadStrategy = reloadStrategy;
4661    }
4662
4663    @Override
4664    public void setTransformers(List<TransformerDefinition> transformers) {
4665        this.transformers = transformers;
4666    }
4667
4668    @Override
4669    public List<TransformerDefinition> getTransformers() {
4670        return transformers;
4671    }
4672
4673    @Override
4674    public Transformer resolveTransformer(String scheme) {
4675        return transformerRegistry.resolveTransformer(new TransformerKey(scheme));
4676    }
4677
4678    @Override
4679    public Transformer resolveTransformer(DataType from, DataType to) {
4680        return transformerRegistry.resolveTransformer(new TransformerKey(from, to));
4681    }
4682
4683    @Override
4684    public TransformerRegistry<TransformerKey> getTransformerRegistry() {
4685        return transformerRegistry;
4686    }
4687
4688    @Override
4689    public void setValidators(List<ValidatorDefinition> validators) {
4690        this.validators = validators;
4691    }
4692
4693    @Override
4694    public List<ValidatorDefinition> getValidators() {
4695        return validators;
4696    }
4697
4698    @Override
4699    public Validator resolveValidator(DataType type) {
4700        return validatorRegistry.resolveValidator(new ValidatorKey(type));
4701    }
4702
4703    @Override
4704    public ValidatorRegistry<ValidatorKey> getValidatorRegistry() {
4705        return validatorRegistry;
4706    }
4707
4708    @Override
4709    public RuntimeCamelCatalog getRuntimeCamelCatalog() {
4710        return runtimeCamelCatalog;
4711    }
4712
4713    @Override
4714    public void setSSLContextParameters(SSLContextParameters sslContextParameters) {
4715        this.sslContextParameters = sslContextParameters;
4716    }
4717
4718    @Override
4719    public SSLContextParameters getSSLContextParameters() {
4720        return this.sslContextParameters;
4721    }
4722
4723    @Override
4724    public HeadersMapFactory getHeadersMapFactory() {
4725        return headersMapFactory;
4726    }
4727
4728    @Override
4729    public void setHeadersMapFactory(HeadersMapFactory headersMapFactory) {
4730        this.headersMapFactory = headersMapFactory;
4731    }
4732
4733    protected Map<String, RouteService> getRouteServices() {
4734        return routeServices;
4735    }
4736
4737    protected ManagementStrategy createManagementStrategy() {
4738        return new ManagementStrategyFactory().create(this, disableJMX || Boolean.getBoolean(JmxSystemPropertyKeys.DISABLED));
4739    }
4740
4741    /**
4742     * Reset context counter to a preset value. Mostly used for tests to ensure a predictable getName()
4743     *
4744     * @param value new value for the context counter
4745     */
4746    public static void setContextCounter(int value) {
4747        DefaultCamelContextNameStrategy.setCounter(value);
4748        DefaultManagementNameStrategy.setCounter(value);
4749    }
4750
4751    protected UuidGenerator createDefaultUuidGenerator() {
4752        if (System.getProperty("com.google.appengine.runtime.environment") != null) {
4753            // either "Production" or "Development"
4754            return new JavaUuidGenerator();
4755        } else {
4756            return new DefaultUuidGenerator();
4757        }
4758    }
4759
4760    protected ModelJAXBContextFactory createModelJAXBContextFactory() {
4761        return new DefaultModelJAXBContextFactory();
4762    }
4763
4764    @Override
4765    public String toString() {
4766        return "CamelContext(" + getName() + ")";
4767    }
4768
4769    class MDCHelper implements AutoCloseable {
4770        final Map<String, String> originalContextMap;
4771
4772        MDCHelper() {
4773            if (isUseMDCLogging()) {
4774                originalContextMap = MDC.getCopyOfContextMap();
4775                MDC.put(MDC_CAMEL_CONTEXT_ID, getName());
4776            } else {
4777                originalContextMap = null;
4778            }
4779        }
4780
4781        @Override
4782        public void close() {
4783            if (isUseMDCLogging()) {
4784                if (originalContextMap != null) {
4785                    MDC.setContextMap(originalContextMap);
4786                } else {
4787                    MDC.clear();
4788                }
4789            }
4790        }
4791    }
4792
4793    @Override
4794    public HealthCheckRegistry getHealthCheckRegistry() {
4795        return healthCheckRegistry;
4796    }
4797
4798    /**
4799     * Sets a {@link HealthCheckRegistry}.
4800     */
4801    public void setHealthCheckRegistry(HealthCheckRegistry healthCheckRegistry) {
4802        this.healthCheckRegistry = ObjectHelper.notNull(healthCheckRegistry, "HealthCheckRegistry");
4803    }
4804
4805    protected NodeIdFactory createNodeIdFactory() {
4806        return new DefaultNodeIdFactory();
4807    }
4808
4809    protected FactoryFinderResolver createFactoryFinderResolver() {
4810        return new DefaultFactoryFinderResolver();
4811    }
4812
4813    protected ClassResolver createClassResolver() {
4814        return new DefaultClassResolver(this);
4815    }
4816
4817    protected ProcessorFactory createProcessorFactory() {
4818        return new DefaultProcessorFactory();
4819    }
4820
4821    protected DataFormatResolver createDataFormatResolver() {
4822        return new DefaultDataFormatResolver();
4823    }
4824
4825    protected MessageHistoryFactory createMessageHistoryFactory() {
4826        return new DefaultMessageHistoryFactory();
4827    }
4828
4829    protected InflightRepository createInflightRepository() {
4830        return new DefaultInflightRepository();
4831    }
4832
4833    protected AsyncProcessorAwaitManager createAsyncProcessorAwaitManager() {
4834        return new DefaultAsyncProcessorAwaitManager();
4835    }
4836
4837    protected RouteController createRouteController() {
4838        return new DefaultRouteController(this);
4839    }
4840
4841    protected HealthCheckRegistry createHealthCheckRegistry() {
4842        return new DefaultHealthCheckRegistry(this);
4843    }
4844
4845
4846    protected ShutdownStrategy createShutdownStrategy() {
4847        return new DefaultShutdownStrategy(this);
4848    }
4849
4850    protected PackageScanClassResolver createPackageScanClassResolver() {
4851        PackageScanClassResolver packageScanClassResolver;
4852        // use WebSphere specific resolver if running on WebSphere
4853        if (WebSpherePackageScanClassResolver.isWebSphereClassLoader(this.getClass().getClassLoader())) {
4854            log.info("Using WebSphere specific PackageScanClassResolver");
4855            packageScanClassResolver = new WebSpherePackageScanClassResolver("META-INF/services/org/apache/camel/TypeConverter");
4856        } else {
4857            packageScanClassResolver = new DefaultPackageScanClassResolver();
4858        }
4859        return packageScanClassResolver;
4860    }
4861
4862    protected ExecutorServiceManager createExecutorServiceManager() {
4863        return new DefaultExecutorServiceManager(this);
4864    }
4865
4866    protected ServicePool<Endpoint, Producer> createProducerServicePool() {
4867        return new SharedProducerServicePool(100);
4868    }
4869
4870    protected ServicePool<Endpoint, PollingConsumer> createPollingConsumerServicePool() {
4871        return new SharedPollingConsumerServicePool(100);
4872    }
4873
4874    protected UnitOfWorkFactory createUnitOfWorkFactory() {
4875        return new DefaultUnitOfWorkFactory();
4876    }
4877
4878    protected RuntimeCamelCatalog createRuntimeCamelCatalog() {
4879        return new DefaultRuntimeCamelCatalog(this, true);
4880    }
4881
4882    protected CamelContextNameStrategy createCamelContextNameStrategy() {
4883        return new DefaultCamelContextNameStrategy();
4884    }
4885
4886    protected ManagementNameStrategy createManagementNameStrategy() {
4887        return new DefaultManagementNameStrategy(this);
4888    }
4889
4890    protected HeadersMapFactory createHeadersMapFactory() {
4891        return new DefaultHeadersMapFactory();
4892    }
4893
4894    protected LanguageResolver createLanguageResolver() {
4895        return new DefaultLanguageResolver();
4896    }
4897
4898    protected EndpointRegistry<EndpointKey> createEndpointRegistry(Map<EndpointKey, Endpoint> endpoints) {
4899        return new DefaultEndpointRegistry(this, endpoints);
4900    }
4901
4902    protected ValidatorRegistry<ValidatorKey> createValidatorRegistry(List<ValidatorDefinition> validators) throws Exception {
4903        return new DefaultValidatorRegistry(this, validators);
4904    }
4905
4906    protected TransformerRegistry<TransformerKey> createTransformerRegistry(List<TransformerDefinition> transformers) throws Exception {
4907        return new DefaultTransformerRegistry(this, transformers);
4908    }
4909
4910}