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