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