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