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