You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by or...@apache.org on 2023/03/24 11:56:03 UTC

[camel] branch main updated: CAMEL-15105: decouple handling of internal services

This is an automated email from the ASF dual-hosted git repository.

orpiske pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 7d455ef7aec CAMEL-15105: decouple handling of internal services
7d455ef7aec is described below

commit 7d455ef7aec2391e6c00549c3ca6e4e2eb4963fa
Author: Otavio Rodolfo Piske <an...@gmail.com>
AuthorDate: Fri Mar 24 10:53:36 2023 +0100

    CAMEL-15105: decouple handling of internal services
    
    Implement a handler for managing the internal services and their lifecycle.
---
 .../camel/impl/engine/AbstractCamelContext.java    | 220 ++++------------
 .../impl/engine/DefaultCamelContextExtension.java  |  65 ++---
 .../camel/impl/engine/InternalServiceHelper.java   | 142 -----------
 .../camel/impl/engine/InternalServiceManager.java  | 278 +++++++++++++++++++++
 4 files changed, 366 insertions(+), 339 deletions(-)

diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index 9d3c13ffff2..d93243898b8 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -47,14 +47,12 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.CatalogCamelContext;
 import org.apache.camel.Component;
-import org.apache.camel.Consumer;
 import org.apache.camel.ConsumerTemplate;
 import org.apache.camel.Endpoint;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.FailedToStartComponentException;
 import org.apache.camel.FluentProducerTemplate;
 import org.apache.camel.GlobalEndpointConfiguration;
-import org.apache.camel.IsSingleton;
 import org.apache.camel.LoggingLevel;
 import org.apache.camel.NoSuchEndpointException;
 import org.apache.camel.Processor;
@@ -201,8 +199,9 @@ public abstract class AbstractCamelContext extends BaseService
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractCamelContext.class);
 
+    protected final InternalServiceManager internalServiceManager;
+
     // start auto assigning route ids using numbering 1000 and upwards
-    final List<Service> servicesToStop = new CopyOnWriteArrayList<>();
     final List<BootstrapCloseable> bootstraps = new CopyOnWriteArrayList<>();
     final Map<String, FactoryFinder> bootstrapFactories = new ConcurrentHashMap<>();
     volatile FactoryFinder bootstrapFactoryFinder;
@@ -264,7 +263,6 @@ public abstract class AbstractCamelContext extends BaseService
     private final Map<String, Component> components = new ConcurrentHashMap<>();
     private final Set<Route> routes = new LinkedHashSet<>();
     private final List<StartupListener> startupListeners = new CopyOnWriteArrayList<>();
-    private final DeferServiceStartupListener deferStartupListener = new DeferServiceStartupListener();
     private final Map<String, Language> languages = new ConcurrentHashMap<>();
     private final Map<String, DataFormat> dataformats = new ConcurrentHashMap<>();
     private final List<LifecycleStrategy> lifecycleStrategies = new CopyOnWriteArrayList<>();
@@ -375,9 +373,6 @@ public abstract class AbstractCamelContext extends BaseService
         // DefaultEndpointRegistry later, but we do this to startup Camel faster.
         this.endpoints = new ProvisionalEndpointRegistry();
 
-        // add the defer service startup listener
-        this.startupListeners.add(deferStartupListener);
-
         // add a default LifecycleStrategy that discover strategies on the registry and invoke them
         this.lifecycleStrategies.add(new OnCamelContextLifecycleStrategy());
 
@@ -398,6 +393,8 @@ public abstract class AbstractCamelContext extends BaseService
             }
         });
 
+        this.internalServiceManager = new InternalServiceManager(this, internalRouteStartupManager, startupListeners);
+
         if (build) {
             try {
                 build();
@@ -475,7 +472,7 @@ public abstract class AbstractCamelContext extends BaseService
     public <T> void setExtension(Class<T> type, T module) {
         if (module != null) {
             try {
-                extensions.put(type, doAddService(module));
+                extensions.put(type, internalServiceManager.addService(module));
             } catch (Exception e) {
                 throw RuntimeCamelException.wrapRuntimeCamelException(e);
             }
@@ -507,7 +504,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setNameStrategy(CamelContextNameStrategy nameStrategy) {
-        this.nameStrategy = doAddService(nameStrategy);
+        this.nameStrategy = internalServiceManager.addService(nameStrategy);
     }
 
     @Override
@@ -524,7 +521,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setManagementNameStrategy(ManagementNameStrategy managementNameStrategy) {
-        this.managementNameStrategy = doAddService(managementNameStrategy);
+        this.managementNameStrategy = internalServiceManager.addService(managementNameStrategy);
     }
 
     @Override
@@ -1003,7 +1000,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setRouteController(RouteController routeController) {
-        this.routeController = doAddService(routeController);
+        this.routeController = internalServiceManager.addService(routeController);
     }
 
     @Override
@@ -1402,31 +1399,12 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void addService(Object object, boolean stopOnShutdown, boolean forceStart) throws Exception {
-        InternalServiceHelper.internalAddService(object, stopOnShutdown, forceStart, true, this,
-                internalRouteStartupManager, servicesToStop, deferStartupListener);
+        internalServiceManager.doAddService(object, stopOnShutdown, forceStart, true);
     }
 
     @Override
     public void addPrototypeService(Object object) throws Exception {
-        doAddService(object, false, true, false);
-    }
-
-    protected <T> T doAddService(T object) {
-        return doAddService(object, true);
-    }
-
-    protected <T> T doAddService(T object, boolean stopOnShutdown) {
-        return doAddService(object, stopOnShutdown, true, true);
-    }
-
-    protected <T> T doAddService(T object, boolean stopOnShutdown, boolean forceStart, boolean useLifecycleStrategies) {
-        try {
-            InternalServiceHelper.internalAddService(object, stopOnShutdown, forceStart, useLifecycleStrategies, this,
-                    internalRouteStartupManager, servicesToStop, deferStartupListener);
-        } catch (Exception e) {
-            throw RuntimeCamelException.wrapRuntimeCamelException(e);
-        }
-        return object;
+        internalServiceManager.addService(object, false, true, false);
     }
 
     @Override
@@ -1440,81 +1418,30 @@ public abstract class AbstractCamelContext extends BaseService
             for (LifecycleStrategy strategy : lifecycleStrategies) {
                 strategy.onServiceRemove(this, service, null);
             }
-            return servicesToStop.remove(service);
+
+            return internalServiceManager.removeService(service);
         }
         return false;
     }
 
     @Override
     public boolean hasService(Object object) {
-        if (servicesToStop.isEmpty()) {
-            return false;
-        }
-        if (object instanceof Service) {
-            Service service = (Service) object;
-            return servicesToStop.contains(service);
-        }
-        return false;
+        return internalServiceManager.hasService(object);
     }
 
     @Override
     public <T> T hasService(Class<T> type) {
-        if (servicesToStop.isEmpty()) {
-            return null;
-        }
-        for (Service service : servicesToStop) {
-            if (type.isInstance(service)) {
-                return type.cast(service);
-            }
-        }
-        return null;
+        return internalServiceManager.hasService(type);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public <T> Set<T> hasServices(Class<T> type) {
-        if (servicesToStop.isEmpty()) {
-            return Collections.emptySet();
-        }
-        Set<T> set = new HashSet<>();
-        for (Service service : servicesToStop) {
-            if (type.isInstance(service)) {
-                set.add((T) service);
-            }
-        }
-        return set;
+        return internalServiceManager.hasServices(type);
     }
 
     @Override
     public void deferStartService(Object object, boolean stopOnShutdown) throws Exception {
-        deferStartService(object, stopOnShutdown, false);
-    }
-
-    public void deferStartService(Object object, boolean stopOnShutdown, boolean startEarly) throws Exception {
-        if (object instanceof Service) {
-            Service service = (Service) object;
-
-            // only add to services to close if its a singleton
-            // otherwise we could for example end up with a lot of prototype
-            // scope endpoints
-            boolean singleton = true; // assume singleton by default
-            if (object instanceof IsSingleton) {
-                singleton = ((IsSingleton) service).isSingleton();
-            }
-            // do not add endpoints as they have their own list
-            if (singleton && !(service instanceof Endpoint)) {
-                // only add to list of services to stop if its not already there
-                if (stopOnShutdown && !hasService(service)) {
-                    servicesToStop.add(service);
-                }
-            }
-            // are we already started?
-            if (isStarted()) {
-                ServiceHelper.startService(service);
-            } else {
-                deferStartupListener.addService(service, startEarly);
-            }
-        }
+        internalServiceManager.deferStartService(object, stopOnShutdown, false);
     }
 
     protected List<StartupListener> getStartupListeners() {
@@ -1712,7 +1639,7 @@ public abstract class AbstractCamelContext extends BaseService
     }
 
     public void setTypeConverter(TypeConverter typeConverter) {
-        this.typeConverter = doAddService(typeConverter);
+        this.typeConverter = internalServiceManager.addService(typeConverter);
     }
 
     protected TypeConverter getOrCreateTypeConverter() {
@@ -1740,7 +1667,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setTypeConverterRegistry(TypeConverterRegistry typeConverterRegistry) {
-        this.typeConverterRegistry = doAddService(typeConverterRegistry);
+        this.typeConverterRegistry = internalServiceManager.addService(typeConverterRegistry);
         // some registries are also a type converter implementation
         if (typeConverterRegistry instanceof TypeConverter) {
             this.typeConverter = (TypeConverter) typeConverterRegistry;
@@ -1761,7 +1688,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setInjector(Injector injector) {
-        this.injector = doAddService(injector);
+        this.injector = internalServiceManager.addService(injector);
     }
 
     @Override
@@ -1778,11 +1705,11 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setPropertiesComponent(PropertiesComponent propertiesComponent) {
-        this.propertiesComponent = doAddService(propertiesComponent);
+        this.propertiesComponent = internalServiceManager.addService(propertiesComponent);
     }
 
     public void setManagementMBeanAssembler(ManagementMBeanAssembler managementMBeanAssembler) {
-        this.managementMBeanAssembler = doAddService(managementMBeanAssembler, false);
+        this.managementMBeanAssembler = internalServiceManager.addService(managementMBeanAssembler, false);
     }
 
     public void setAutoCreateComponents(boolean autoCreateComponents) {
@@ -2018,7 +1945,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setRuntimeEndpointRegistry(RuntimeEndpointRegistry runtimeEndpointRegistry) {
-        this.runtimeEndpointRegistry = doAddService(runtimeEndpointRegistry);
+        this.runtimeEndpointRegistry = internalServiceManager.addService(runtimeEndpointRegistry);
     }
 
     @Override
@@ -2480,7 +2407,7 @@ public abstract class AbstractCamelContext extends BaseService
 
         // re-create endpoint registry as the cache size limit may be set after the constructor of this instance was called.
         // and we needed to create endpoints up-front as it may be accessed before this context is started
-        endpoints = doAddService(createEndpointRegistry(endpoints));
+        endpoints = internalServiceManager.addService(createEndpointRegistry(endpoints));
 
         // optimised to not include runtimeEndpointRegistry unless startServices
         // is enabled or JMX statistics is in extended mode
@@ -3024,7 +2951,7 @@ public abstract class AbstractCamelContext extends BaseService
 
         // shutdown await manager to trigger interrupt of blocked threads to
         // attempt to free these threads graceful
-        shutdownServices(asyncProcessorAwaitManager);
+        InternalServiceManager.shutdownServices(this, asyncProcessorAwaitManager);
 
         // we need also to include routes which failed to start to ensure all resources get stopped when stopping Camel
         for (RouteService routeService : routeServices.values()) {
@@ -3042,7 +2969,7 @@ public abstract class AbstractCamelContext extends BaseService
             RouteService routeService = order.getRouteService();
             list.add(routeService);
         }
-        shutdownServices(list, false);
+        InternalServiceManager.shutdownServices(this, list, false);
 
         if (startupSummaryLevel != StartupSummaryLevel.Oneline
                 && startupSummaryLevel != StartupSummaryLevel.Off) {
@@ -3062,11 +2989,7 @@ public abstract class AbstractCamelContext extends BaseService
         // consumer (eg @Consumer)
         // which we need to stop after the routes, as a POJO consumer is
         // essentially a route also
-        for (Service service : servicesToStop) {
-            if (service instanceof Consumer) {
-                shutdownServices(service);
-            }
-        }
+        internalServiceManager.stopConsumers();
 
         // the stop order is important
 
@@ -3080,18 +3003,17 @@ public abstract class AbstractCamelContext extends BaseService
         // shutdown debugger
         ServiceHelper.stopAndShutdownService(getDebugger());
 
-        shutdownServices(endpoints.values());
+        InternalServiceManager.shutdownServices(this, endpoints.values());
         endpoints.clear();
 
-        shutdownServices(components.values());
+        InternalServiceManager.shutdownServices(this, components.values());
         components.clear();
 
-        shutdownServices(languages.values());
+        InternalServiceManager.shutdownServices(this, languages.values());
         languages.clear();
 
         // shutdown services as late as possible (except type converters as they may be needed during the remainder of the stopping)
-        shutdownServices(servicesToStop);
-        servicesToStop.clear();
+        internalServiceManager.shutdownServices();
 
         try {
             for (LifecycleStrategy strategy : lifecycleStrategies) {
@@ -3107,20 +3029,20 @@ public abstract class AbstractCamelContext extends BaseService
         // stop the notifier service
         if (getManagementStrategy() != null) {
             for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) {
-                shutdownServices(notifier);
+                InternalServiceManager.shutdownServices(this, notifier);
             }
         }
 
         // shutdown management and lifecycle after all other services
-        shutdownServices(managementStrategy);
-        shutdownServices(managementMBeanAssembler);
-        shutdownServices(lifecycleStrategies);
+        InternalServiceManager.shutdownServices(this, managementStrategy);
+        InternalServiceManager.shutdownServices(this, managementMBeanAssembler);
+        InternalServiceManager.shutdownServices(this, lifecycleStrategies);
         // do not clear lifecycleStrategies as we can start Camel again and get
         // the route back as before
 
         // shutdown executor service, reactive executor last
-        shutdownServices(executorServiceManager);
-        shutdownServices(reactiveExecutor);
+        InternalServiceManager.shutdownServices(this, executorServiceManager);
+        InternalServiceManager.shutdownServices(this, reactiveExecutor);
 
         // shutdown type converter and registry as late as possible
         ServiceHelper.stopService(typeConverter);
@@ -3235,42 +3157,6 @@ public abstract class AbstractCamelContext extends BaseService
         return false;
     }
 
-    private void shutdownServices(Object service) {
-        // do not rethrow exception as we want to keep shutting down in case of
-        // problems
-
-        // allow us to do custom work before delegating to service helper
-        try {
-            if (service instanceof Service) {
-                ServiceHelper.stopAndShutdownService(service);
-            } else if (service instanceof Collection) {
-                ServiceHelper.stopAndShutdownServices((Collection<?>) service);
-            }
-        } catch (Throwable e) {
-            LOG.warn("Error occurred while shutting down service: " + service + ". This exception will be ignored.", e);
-            // fire event
-            EventHelper.notifyServiceStopFailure(this, service, e);
-        }
-    }
-
-    private void shutdownServices(Collection<?> services) {
-        // reverse stopping by default
-        shutdownServices(services, true);
-    }
-
-    private void shutdownServices(Collection<?> services, boolean reverse) {
-        Collection<?> list = services;
-        if (reverse) {
-            List<Object> reverseList = new ArrayList<>(services);
-            Collections.reverse(reverseList);
-            list = reverseList;
-        }
-
-        for (Object service : list) {
-            shutdownServices(service);
-        }
-    }
-
     void startService(Service service) throws Exception {
         // and register startup aware so they can be notified when
         // camel context has been started
@@ -3562,7 +3448,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setClassResolver(ClassResolver classResolver) {
-        this.classResolver = doAddService(classResolver);
+        this.classResolver = internalServiceManager.addService(classResolver);
     }
 
     @Override
@@ -3639,7 +3525,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setInflightRepository(InflightRepository repository) {
-        this.inflightRepository = doAddService(repository);
+        this.inflightRepository = internalServiceManager.addService(repository);
     }
 
     @Override
@@ -3846,7 +3732,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setShutdownStrategy(ShutdownStrategy shutdownStrategy) {
-        this.shutdownStrategy = doAddService(shutdownStrategy);
+        this.shutdownStrategy = internalServiceManager.addService(shutdownStrategy);
     }
 
     @Override
@@ -3915,7 +3801,7 @@ public abstract class AbstractCamelContext extends BaseService
     public void setExecutorServiceManager(ExecutorServiceManager executorServiceManager) {
         // special for executorServiceManager as want to stop it manually so
         // false in stopOnShutdown
-        this.executorServiceManager = doAddService(executorServiceManager, false);
+        this.executorServiceManager = internalServiceManager.addService(executorServiceManager, false);
     }
 
     @Override
@@ -3932,7 +3818,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setMessageHistoryFactory(MessageHistoryFactory messageHistoryFactory) {
-        this.messageHistoryFactory = doAddService(messageHistoryFactory);
+        this.messageHistoryFactory = internalServiceManager.addService(messageHistoryFactory);
         // enable message history if we set a custom factory
         setMessageHistory(true);
     }
@@ -3949,7 +3835,7 @@ public abstract class AbstractCamelContext extends BaseService
         if (isStartingOrStarted()) {
             throw new IllegalStateException("Cannot set debugger on a started CamelContext");
         }
-        this.debugger = doAddService(debugger, true, false, true);
+        this.debugger = internalServiceManager.addService(debugger, true, false, true);
     }
 
     @Override
@@ -3970,7 +3856,7 @@ public abstract class AbstractCamelContext extends BaseService
         if (!isTracingStandby() && isStartingOrStarted()) {
             throw new IllegalStateException("Cannot set tracer on a started CamelContext");
         }
-        this.tracer = doAddService(tracer, true, false, true);
+        this.tracer = internalServiceManager.addService(tracer, true, false, true);
     }
 
     @Override
@@ -4007,7 +3893,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setUuidGenerator(UuidGenerator uuidGenerator) {
-        this.uuidGenerator = doAddService(uuidGenerator);
+        this.uuidGenerator = internalServiceManager.addService(uuidGenerator);
     }
 
     @Override
@@ -4024,7 +3910,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setStreamCachingStrategy(StreamCachingStrategy streamCachingStrategy) {
-        this.streamCachingStrategy = doAddService(streamCachingStrategy, true, false, true);
+        this.streamCachingStrategy = internalServiceManager.addService(streamCachingStrategy, true, false, true);
     }
 
     @Override
@@ -4041,7 +3927,7 @@ public abstract class AbstractCamelContext extends BaseService
 
     @Override
     public void setRestRegistry(RestRegistry restRegistry) {
-        this.restRegistry = doAddService(restRegistry);
+        this.restRegistry = internalServiceManager.addService(restRegistry);
     }
 
     protected RestRegistry createRestRegistry() {
@@ -4061,7 +3947,7 @@ public abstract class AbstractCamelContext extends BaseService
     }
 
     public void setRestRegistryFactory(RestRegistryFactory restRegistryFactory) {
-        this.restRegistryFactory = doAddService(restRegistryFactory);
+        this.restRegistryFactory = internalServiceManager.addService(restRegistryFactory);
     }
 
     @Override
@@ -4100,7 +3986,7 @@ public abstract class AbstractCamelContext extends BaseService
     }
 
     public void setTransformerRegistry(TransformerRegistry transformerRegistry) {
-        this.transformerRegistry = doAddService(transformerRegistry);
+        this.transformerRegistry = internalServiceManager.addService(transformerRegistry);
     }
 
     @Override
@@ -4121,7 +4007,7 @@ public abstract class AbstractCamelContext extends BaseService
     }
 
     public void setValidatorRegistry(ValidatorRegistry validatorRegistry) {
-        this.validatorRegistry = doAddService(validatorRegistry);
+        this.validatorRegistry = internalServiceManager.addService(validatorRegistry);
     }
 
     @Override
@@ -4145,11 +4031,11 @@ public abstract class AbstractCamelContext extends BaseService
     }
 
     public void setBeanProxyFactory(BeanProxyFactory beanProxyFactory) {
-        this.beanProxyFactory = doAddService(beanProxyFactory);
+        this.beanProxyFactory = internalServiceManager.addService(beanProxyFactory);
     }
 
     public void setBeanProcessorFactory(BeanProcessorFactory beanProcessorFactory) {
-        this.beanProcessorFactory = doAddService(beanProcessorFactory);
+        this.beanProcessorFactory = internalServiceManager.addService(beanProcessorFactory);
     }
 
     public boolean isLogJvmUptime() {
@@ -4533,4 +4419,8 @@ public abstract class AbstractCamelContext extends BaseService
     List<RouteStartupOrder> getRouteStartupOrder() {
         return routeStartupOrder;
     }
+
+    InternalServiceManager getInternalServiceManager() {
+        return internalServiceManager;
+    }
 }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelContextExtension.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelContextExtension.java
index f7e988cf761..736b8d39dff 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelContextExtension.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelContextExtension.java
@@ -18,7 +18,6 @@
 package org.apache.camel.impl.engine;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -219,7 +218,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public List<Service> getServices() {
-        return Collections.unmodifiableList(camelContext.servicesToStop);
+        return camelContext.getInternalServiceManager().getServices();
     }
 
     @Override
@@ -261,7 +260,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setBeanPostProcessor(CamelBeanPostProcessor beanPostProcessor) {
-        camelContext.beanPostProcessor = camelContext.doAddService(beanPostProcessor);
+        camelContext.beanPostProcessor = camelContext.getInternalServiceManager().addService(beanPostProcessor);
     }
 
     @Override
@@ -299,7 +298,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setComponentResolver(ComponentResolver componentResolver) {
-        camelContext.componentResolver = camelContext.doAddService(componentResolver);
+        camelContext.componentResolver = camelContext.getInternalServiceManager().addService(componentResolver);
     }
 
     public ComponentNameResolver getComponentNameResolver() {
@@ -314,7 +313,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setComponentNameResolver(ComponentNameResolver componentNameResolver) {
-        camelContext.componentNameResolver = camelContext.doAddService(componentNameResolver);
+        camelContext.componentNameResolver = camelContext.getInternalServiceManager().addService(componentNameResolver);
     }
 
     public LanguageResolver getLanguageResolver() {
@@ -329,7 +328,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setLanguageResolver(LanguageResolver languageResolver) {
-        camelContext.languageResolver = camelContext.doAddService(languageResolver);
+        camelContext.languageResolver = camelContext.getInternalServiceManager().addService(languageResolver);
     }
 
     public ConfigurerResolver getConfigurerResolver() {
@@ -344,7 +343,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setConfigurerResolver(ConfigurerResolver configurerResolver) {
-        camelContext.configurerResolver = camelContext.doAddService(configurerResolver);
+        camelContext.configurerResolver = camelContext.getInternalServiceManager().addService(configurerResolver);
     }
 
     public UriFactoryResolver getUriFactoryResolver() {
@@ -359,7 +358,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setUriFactoryResolver(UriFactoryResolver uriFactoryResolver) {
-        camelContext.uriFactoryResolver = camelContext.doAddService(uriFactoryResolver);
+        camelContext.uriFactoryResolver = camelContext.getInternalServiceManager().addService(uriFactoryResolver);
     }
 
     @Override
@@ -454,7 +453,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setUnitOfWorkFactory(UnitOfWorkFactory unitOfWorkFactory) {
-        camelContext.unitOfWorkFactory = camelContext.doAddService(unitOfWorkFactory);
+        camelContext.unitOfWorkFactory = camelContext.getInternalServiceManager().addService(unitOfWorkFactory);
     }
 
     @Override
@@ -528,7 +527,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setFactoryFinderResolver(FactoryFinderResolver factoryFinderResolver) {
-        camelContext.factoryFinderResolver = camelContext.doAddService(factoryFinderResolver);
+        camelContext.factoryFinderResolver = camelContext.getInternalServiceManager().addService(factoryFinderResolver);
     }
 
     @Override
@@ -550,7 +549,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setPackageScanClassResolver(PackageScanClassResolver packageScanClassResolver) {
-        camelContext.packageScanClassResolver = camelContext.doAddService(packageScanClassResolver);
+        camelContext.packageScanClassResolver = camelContext.getInternalServiceManager().addService(packageScanClassResolver);
     }
 
     @Override
@@ -567,7 +566,8 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setPackageScanResourceResolver(PackageScanResourceResolver packageScanResourceResolver) {
-        camelContext.packageScanResourceResolver = camelContext.doAddService(packageScanResourceResolver);
+        camelContext.packageScanResourceResolver
+                = camelContext.getInternalServiceManager().addService(packageScanResourceResolver);
     }
 
     @Override
@@ -584,7 +584,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setModelJAXBContextFactory(final ModelJAXBContextFactory modelJAXBContextFactory) {
-        camelContext.modelJAXBContextFactory = camelContext.doAddService(modelJAXBContextFactory);
+        camelContext.modelJAXBContextFactory = camelContext.getInternalServiceManager().addService(modelJAXBContextFactory);
     }
 
     @Override
@@ -601,7 +601,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setNodeIdFactory(NodeIdFactory idFactory) {
-        camelContext.nodeIdFactory = camelContext.doAddService(idFactory);
+        camelContext.nodeIdFactory = camelContext.getInternalServiceManager().addService(idFactory);
     }
 
     @Override
@@ -618,7 +618,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setModelineFactory(ModelineFactory modelineFactory) {
-        camelContext.modelineFactory = camelContext.doAddService(modelineFactory);
+        camelContext.modelineFactory = camelContext.getInternalServiceManager().addService(modelineFactory);
     }
 
     @Override
@@ -635,7 +635,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setPeriodTaskResolver(PeriodTaskResolver periodTaskResolver) {
-        camelContext.periodTaskResolver = camelContext.doAddService(periodTaskResolver);
+        camelContext.periodTaskResolver = camelContext.getInternalServiceManager().addService(periodTaskResolver);
     }
 
     public PeriodTaskScheduler getPeriodTaskScheduler() {
@@ -650,7 +650,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setPeriodTaskScheduler(PeriodTaskScheduler periodTaskScheduler) {
-        camelContext.periodTaskScheduler = camelContext.doAddService(periodTaskScheduler);
+        camelContext.periodTaskScheduler = camelContext.getInternalServiceManager().addService(periodTaskScheduler);
     }
 
     @Override
@@ -717,7 +717,8 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setAsyncProcessorAwaitManager(AsyncProcessorAwaitManager asyncProcessorAwaitManager) {
-        camelContext.asyncProcessorAwaitManager = camelContext.doAddService(asyncProcessorAwaitManager);
+        camelContext.asyncProcessorAwaitManager
+                = camelContext.getInternalServiceManager().addService(asyncProcessorAwaitManager);
     }
 
     @Override
@@ -734,7 +735,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setBeanIntrospection(BeanIntrospection beanIntrospection) {
-        camelContext.beanIntrospection = camelContext.doAddService(beanIntrospection);
+        camelContext.beanIntrospection = camelContext.getInternalServiceManager().addService(beanIntrospection);
     }
 
     @Override
@@ -761,7 +762,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setDataFormatResolver(DataFormatResolver dataFormatResolver) {
-        camelContext.dataFormatResolver = camelContext.doAddService(dataFormatResolver);
+        camelContext.dataFormatResolver = camelContext.getInternalServiceManager().addService(dataFormatResolver);
     }
 
     @Override
@@ -778,7 +779,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setHealthCheckResolver(HealthCheckResolver healthCheckResolver) {
-        camelContext.healthCheckResolver = camelContext.doAddService(healthCheckResolver);
+        camelContext.healthCheckResolver = camelContext.getInternalServiceManager().addService(healthCheckResolver);
     }
 
     public DevConsoleResolver getDevConsoleResolver() {
@@ -793,7 +794,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setDevConsoleResolver(DevConsoleResolver devConsoleResolver) {
-        camelContext.devConsoleResolver = camelContext.doAddService(devConsoleResolver);
+        camelContext.devConsoleResolver = camelContext.getInternalServiceManager().addService(devConsoleResolver);
     }
 
     @Override
@@ -820,7 +821,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setProcessorFactory(ProcessorFactory processorFactory) {
-        camelContext.processorFactory = camelContext.doAddService(processorFactory);
+        camelContext.processorFactory = camelContext.getInternalServiceManager().addService(processorFactory);
     }
 
     @Override
@@ -837,7 +838,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setInternalProcessorFactory(InternalProcessorFactory internalProcessorFactory) {
-        camelContext.internalProcessorFactory = camelContext.doAddService(internalProcessorFactory);
+        camelContext.internalProcessorFactory = camelContext.getInternalServiceManager().addService(internalProcessorFactory);
     }
 
     @Override
@@ -854,7 +855,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setInterceptEndpointFactory(InterceptEndpointFactory interceptEndpointFactory) {
-        camelContext.interceptEndpointFactory = camelContext.doAddService(interceptEndpointFactory);
+        camelContext.interceptEndpointFactory = camelContext.getInternalServiceManager().addService(interceptEndpointFactory);
     }
 
     @Override
@@ -881,7 +882,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setHeadersMapFactory(HeadersMapFactory headersMapFactory) {
-        camelContext.headersMapFactory = camelContext.doAddService(headersMapFactory);
+        camelContext.headersMapFactory = camelContext.getInternalServiceManager().addService(headersMapFactory);
     }
 
     @Override
@@ -898,7 +899,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setRoutesLoader(RoutesLoader routesLoader) {
-        camelContext.routesLoader = camelContext.doAddService(routesLoader);
+        camelContext.routesLoader = camelContext.getInternalServiceManager().addService(routesLoader);
     }
 
     @Override
@@ -915,7 +916,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setResourceLoader(ResourceLoader resourceLoader) {
-        camelContext.resourceLoader = camelContext.doAddService(resourceLoader);
+        camelContext.resourceLoader = camelContext.getInternalServiceManager().addService(resourceLoader);
     }
 
     public ModelToXMLDumper getModelToXMLDumper() {
@@ -930,7 +931,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     }
 
     public void setModelToXMLDumper(ModelToXMLDumper modelToXMLDumper) {
-        camelContext.modelToXMLDumper = camelContext.doAddService(modelToXMLDumper);
+        camelContext.modelToXMLDumper = camelContext.getInternalServiceManager().addService(modelToXMLDumper);
     }
 
     public RestBindingJaxbDataFormatFactory getRestBindingJaxbDataFormatFactory() {
@@ -962,7 +963,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setRuntimeCamelCatalog(RuntimeCamelCatalog runtimeCamelCatalog) {
-        camelContext.runtimeCamelCatalog = camelContext.doAddService(runtimeCamelCatalog);
+        camelContext.runtimeCamelCatalog = camelContext.getInternalServiceManager().addService(runtimeCamelCatalog);
     }
 
     @Override
@@ -998,7 +999,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
 
     @Override
     public void setExchangeFactoryManager(ExchangeFactoryManager exchangeFactoryManager) {
-        camelContext.exchangeFactoryManager = camelContext.doAddService(exchangeFactoryManager);
+        camelContext.exchangeFactoryManager = camelContext.getInternalServiceManager().addService(exchangeFactoryManager);
     }
 
     @Override
@@ -1036,7 +1037,7 @@ class DefaultCamelContextExtension implements ExtendedCamelContext {
     public void setReactiveExecutor(ReactiveExecutor reactiveExecutor) {
         // special for executorServiceManager as want to stop it manually so
         // false in stopOnShutdown
-        camelContext.reactiveExecutor = camelContext.doAddService(reactiveExecutor, false);
+        camelContext.reactiveExecutor = camelContext.getInternalServiceManager().addService(reactiveExecutor, false);
     }
 
     @Override
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalServiceHelper.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalServiceHelper.java
deleted file mode 100644
index 9bc9c96c435..00000000000
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalServiceHelper.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.camel.impl.engine;
-
-import java.util.List;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelContextAware;
-import org.apache.camel.Endpoint;
-import org.apache.camel.IsSingleton;
-import org.apache.camel.Route;
-import org.apache.camel.RouteAware;
-import org.apache.camel.Service;
-import org.apache.camel.TypeConverter;
-import org.apache.camel.spi.LifecycleStrategy;
-import org.apache.camel.spi.TypeConverterRegistry;
-import org.apache.camel.support.service.BaseService;
-import org.apache.camel.support.service.ServiceHelper;
-
-final class InternalServiceHelper {
-
-    private InternalServiceHelper() {
-
-    }
-
-    public static void internalAddService(
-            Object object, boolean stopOnShutdown,
-            boolean forceStart, boolean useLifecycleStrategies, CamelContext camelContext,
-            InternalRouteStartupManager internalRouteStartupManager,
-            List<Service> servicesToStop, DeferServiceStartupListener deferStartupListener)
-            throws Exception {
-
-        if (object == null) {
-            return;
-        }
-
-        // inject CamelContext
-        CamelContextAware.trySetCamelContext(object, camelContext);
-
-        if (object instanceof Service) {
-            Service service = (Service) object;
-
-            if (useLifecycleStrategies) {
-                for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
-                    if (service instanceof Endpoint) {
-                        // use specialized endpoint add
-                        strategy.onEndpointAdd((Endpoint) service);
-                    } else {
-                        Route route;
-                        if (service instanceof RouteAware) {
-                            route = ((RouteAware) service).getRoute();
-                        } else {
-                            // if the service is added while creating a new route then grab the route from the startup manager
-                            route = internalRouteStartupManager.getSetupRoute();
-                        }
-                        strategy.onServiceAdd(camelContext, service, route);
-                    }
-                }
-            }
-
-            if (!forceStart) {
-                ServiceHelper.initService(service);
-                // now start the service (and defer starting if CamelContext is
-                // starting up itself)
-                camelContext.deferStartService(object, stopOnShutdown);
-            } else {
-                // only add to services to close if its a singleton
-                // otherwise we could for example end up with a lot of prototype
-                // scope endpoints
-                boolean singleton = true; // assume singleton by default
-                if (object instanceof IsSingleton) {
-                    singleton = ((IsSingleton) service).isSingleton();
-                }
-                // do not add endpoints as they have their own list
-                if (singleton && !(service instanceof Endpoint)) {
-                    // only add to list of services to stop if its not already there
-                    if (stopOnShutdown && !camelContext.hasService(service)) {
-                        // special for type converter / type converter registry which is stopped manual later
-                        boolean tc = service instanceof TypeConverter || service instanceof TypeConverterRegistry;
-                        if (!tc) {
-                            servicesToStop.add(service);
-                        }
-                    }
-                }
-
-                if (camelContext instanceof BaseService baseService) {
-                    if (baseService.isStartingOrStarted()) {
-                        ServiceHelper.startService(service);
-                    } else {
-                        ServiceHelper.initService(service);
-                        deferStartService(object, stopOnShutdown, true, camelContext, servicesToStop, deferStartupListener);
-                    }
-                }
-            }
-        }
-    }
-
-    private static void deferStartService(
-            Object object, boolean stopOnShutdown, boolean startEarly, CamelContext camelContext,
-            List<Service> servicesToStop, DeferServiceStartupListener deferStartupListener)
-            throws Exception {
-        if (object instanceof Service) {
-            Service service = (Service) object;
-
-            // only add to services to close if its a singleton
-            // otherwise we could for example end up with a lot of prototype
-            // scope endpoints
-            boolean singleton = true; // assume singleton by default
-            if (object instanceof IsSingleton) {
-                singleton = ((IsSingleton) service).isSingleton();
-            }
-            // do not add endpoints as they have their own list
-            if (singleton && !(service instanceof Endpoint)) {
-                // only add to list of services to stop if its not already there
-                if (stopOnShutdown && !camelContext.hasService(service)) {
-                    servicesToStop.add(service);
-                }
-            }
-            // are we already started?
-            if (camelContext.isStarted()) {
-                ServiceHelper.startService(service);
-            } else {
-                deferStartupListener.addService(service, startEarly);
-            }
-        }
-    }
-}
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalServiceManager.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalServiceManager.java
new file mode 100644
index 00000000000..e2502da0e2f
--- /dev/null
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalServiceManager.java
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.impl.engine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Consumer;
+import org.apache.camel.Endpoint;
+import org.apache.camel.IsSingleton;
+import org.apache.camel.Route;
+import org.apache.camel.RouteAware;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.Service;
+import org.apache.camel.StartupListener;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.spi.LifecycleStrategy;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.support.EventHelper;
+import org.apache.camel.support.service.BaseService;
+import org.apache.camel.support.service.ServiceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InternalServiceManager {
+    private static final Logger LOG = LoggerFactory.getLogger(InternalServiceManager.class);
+
+    private final CamelContext camelContext;
+    private final InternalRouteStartupManager internalRouteStartupManager;
+
+    private final DeferServiceStartupListener deferStartupListener = new DeferServiceStartupListener();
+    private final List<Service> services = new CopyOnWriteArrayList<>();
+
+    InternalServiceManager(CamelContext camelContext, InternalRouteStartupManager internalRouteStartupManager,
+                           List<StartupListener> startupListeners) {
+        /*
+         Note: this is an internal API and not meant to be public, so it uses assertion for lightweight nullability
+         checking for extremely unlikely scenarios that should be found during development time.
+         */
+        assert camelContext != null : "the Camel context cannot be null";
+        assert internalRouteStartupManager != null : "the internalRouteStartupManager cannot be null";
+        assert startupListeners != null : "the startupListeners cannot be null";
+
+        this.camelContext = camelContext;
+        this.internalRouteStartupManager = internalRouteStartupManager;
+
+        startupListeners.add(deferStartupListener);
+    }
+
+    public <T> T addService(T object) {
+        return addService(object, true);
+    }
+
+    public <T> T addService(T object, boolean stopOnShutdown) {
+        return addService(object, stopOnShutdown, true, true);
+    }
+
+    public <T> T addService(T object, boolean stopOnShutdown, boolean forceStart, boolean useLifecycleStrategies) {
+        try {
+            doAddService(object, stopOnShutdown, forceStart, useLifecycleStrategies);
+        } catch (Exception e) {
+            throw RuntimeCamelException.wrapRuntimeCamelException(e);
+        }
+        return object;
+    }
+
+    public void doAddService(Object object, boolean stopOnShutdown, boolean forceStart, boolean useLifecycleStrategies)
+            throws Exception {
+
+        if (object == null) {
+            return;
+        }
+
+        // inject CamelContext
+        CamelContextAware.trySetCamelContext(object, camelContext);
+
+        if (object instanceof Service) {
+            Service service = (Service) object;
+
+            if (useLifecycleStrategies) {
+                for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
+                    if (service instanceof Endpoint) {
+                        // use specialized endpoint add
+                        strategy.onEndpointAdd((Endpoint) service);
+                    } else {
+                        Route route;
+                        if (service instanceof RouteAware) {
+                            route = ((RouteAware) service).getRoute();
+                        } else {
+                            // if the service is added while creating a new route then grab the route from the startup manager
+                            route = internalRouteStartupManager.getSetupRoute();
+                        }
+                        strategy.onServiceAdd(camelContext, service, route);
+                    }
+                }
+            }
+
+            if (!forceStart) {
+                ServiceHelper.initService(service);
+                // now start the service (and defer starting if CamelContext is
+                // starting up itself)
+                camelContext.deferStartService(object, stopOnShutdown);
+            } else {
+                // only add to services to close if its a singleton
+                // otherwise we could for example end up with a lot of prototype
+                // scope endpoints
+                boolean singleton = true; // assume singleton by default
+                if (object instanceof IsSingleton) {
+                    singleton = ((IsSingleton) service).isSingleton();
+                }
+                // do not add endpoints as they have their own list
+                if (singleton && !(service instanceof Endpoint)) {
+                    // only add to list of services to stop if its not already there
+                    if (stopOnShutdown && !camelContext.hasService(service)) {
+                        // special for type converter / type converter registry which is stopped manual later
+                        boolean tc = service instanceof TypeConverter || service instanceof TypeConverterRegistry;
+                        if (!tc) {
+                            services.add(service);
+                        }
+                    }
+                }
+
+                if (camelContext instanceof BaseService baseService) {
+                    if (baseService.isStartingOrStarted()) {
+                        ServiceHelper.startService(service);
+                    } else {
+                        ServiceHelper.initService(service);
+                        deferStartService(object, stopOnShutdown, true);
+                    }
+                }
+            }
+        }
+    }
+
+    public void deferStartService(Object object, boolean stopOnShutdown, boolean startEarly) {
+        if (object instanceof Service) {
+            Service service = (Service) object;
+
+            // only add to services to close if its a singleton
+            // otherwise we could for example end up with a lot of prototype
+            // scope endpoints
+            boolean singleton = true; // assume singleton by default
+            if (object instanceof IsSingleton) {
+                singleton = ((IsSingleton) service).isSingleton();
+            }
+            // do not add endpoints as they have their own list
+            if (singleton && !(service instanceof Endpoint)) {
+                // only add to list of services to stop if its not already there
+                if (stopOnShutdown && !camelContext.hasService(service)) {
+                    services.add(service);
+                }
+            }
+            // are we already started?
+            if (camelContext.isStarted()) {
+                ServiceHelper.startService(service);
+            } else {
+                deferStartupListener.addService(service, startEarly);
+            }
+        }
+    }
+
+    public boolean removeService(Service service) {
+        return services.remove(service);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> Set<T> hasServices(Class<T> type) {
+        if (services.isEmpty()) {
+            return Collections.emptySet();
+        }
+
+        Set<T> set = new HashSet<>();
+        for (Service service : services) {
+            if (type.isInstance(service)) {
+                set.add((T) service);
+            }
+        }
+        return set;
+    }
+
+    public boolean hasService(Object object) {
+        if (services.isEmpty()) {
+            return false;
+        }
+        if (object instanceof Service) {
+            Service service = (Service) object;
+            return services.contains(service);
+        }
+        return false;
+    }
+
+    public <T> T hasService(Class<T> type) {
+        if (services.isEmpty()) {
+            return null;
+        }
+        for (Service service : services) {
+            if (type.isInstance(service)) {
+                return type.cast(service);
+            }
+        }
+        return null;
+    }
+
+    public void stopConsumers() {
+        for (Service service : services) {
+            if (service instanceof Consumer) {
+                InternalServiceManager.shutdownServices(camelContext, service);
+            }
+        }
+    }
+
+    public void shutdownServices() {
+        InternalServiceManager.shutdownServices(camelContext, services);
+        services.clear();
+    }
+
+    public static void shutdownServices(CamelContext camelContext, Collection<?> services) {
+        // reverse stopping by default
+        shutdownServices(camelContext, services, true);
+    }
+
+    public List<Service> getServices() {
+        return Collections.unmodifiableList(services);
+    }
+
+    public static void shutdownServices(CamelContext camelContext, Collection<?> services, boolean reverse) {
+        Collection<?> list = services;
+        if (reverse) {
+            List<Object> reverseList = new ArrayList<>(services);
+            Collections.reverse(reverseList);
+            list = reverseList;
+        }
+
+        for (Object service : list) {
+            shutdownServices(camelContext, service);
+        }
+    }
+
+    public static void shutdownServices(CamelContext camelContext, Object service) {
+        // do not rethrow exception as we want to keep shutting down in case of
+        // problems
+
+        // allow us to do custom work before delegating to service helper
+        try {
+            if (service instanceof Service) {
+                ServiceHelper.stopAndShutdownService(service);
+            } else if (service instanceof Collection) {
+                ServiceHelper.stopAndShutdownServices((Collection<?>) service);
+            }
+        } catch (Exception e) {
+            LOG.warn("Error occurred while shutting down service: {}. This exception will be ignored.", service, e);
+            // fire event
+            EventHelper.notifyServiceStopFailure(camelContext, service, e);
+        }
+    }
+}