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