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