From 534a2cace4745fd8b83b91e153d43f37820c6bb4 Mon Sep 17 00:00:00 2001 From: Croway Date: Tue, 16 Sep 2025 17:41:14 +0200 Subject: [PATCH] ContextServicePlugin SPI - provides a plugin system for Apache Camel that allows automatic discovery and initialization of third-party components during CamelContext startup. --- .../camel/spi/ContextServicePlugin.java | 69 +++++++++++++++++ .../impl/engine/AbstractCamelContext.java | 4 + .../DefaultContextServiceLoaderPlugin.java | 75 +++++++++++++++++++ .../camel/impl/engine/SimpleCamelContext.java | 6 ++ 4 files changed, 154 insertions(+) create mode 100644 core/camel-api/src/main/java/org/apache/camel/spi/ContextServicePlugin.java create mode 100644 core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultContextServiceLoaderPlugin.java diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ContextServicePlugin.java b/core/camel-api/src/main/java/org/apache/camel/spi/ContextServicePlugin.java new file mode 100644 index 0000000000000..22e6553f6c2af --- /dev/null +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ContextServicePlugin.java @@ -0,0 +1,69 @@ +/* + * 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.spi; + +import org.apache.camel.CamelContext; + +/** + * A plugin interface that allows third-party components to perform initialization tasks when a CamelContext is being + * configured and started. + *

+ * Implementations of this interface are automatically discovered and loaded via the Java ServiceLoader mechanism. To + * register a plugin, create a service provider configuration file at + * {@code META-INF/services/org.apache.camel.spi.ContextServicePlugin} containing the fully qualified class name of your + * implementation. + *

+ * Common use cases include: + *

+ * + *

Example Usage:

+ * + *
+ * 
+ * public class MyContextServicePlugin implements ContextServicePlugin {
+ *     @Override
+ *     public void load(CamelContext camelContext) {
+ *         // Register a bean in the registry
+ *         camelContext.getRegistry().bind("myBean", new MyBean());
+ *
+ *         // Add an event notifier
+ *         camelContext.getManagementStrategy().addEventNotifier(new MyEventNotifier());
+ *     }
+ * }
+ * 
+ * 
+ * + * @see org.apache.camel.impl.engine.DefaultContextServiceLoaderPlugin + */ +public interface ContextServicePlugin { + + /** + * Called during CamelContext initialization to allow the plugin to configure or customize the context. + *

+ * This method is invoked after the CamelContext has been created but before routes are started. Implementations + * should perform any necessary setup operations such as registering beans, adding event notifiers, or configuring + * global settings. + * + * @param camelContext the CamelContext being initialized, never {@code null} + */ + void load(CamelContext camelContext); +} 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 11effab623915..eec78125a9da6 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 @@ -70,6 +70,7 @@ import org.apache.camel.StartupListener; import org.apache.camel.StartupStep; import org.apache.camel.StartupSummaryLevel; +import org.apache.camel.StatefulService; import org.apache.camel.Suspendable; import org.apache.camel.SuspendableService; import org.apache.camel.TypeConverter; @@ -349,6 +350,7 @@ protected AbstractCamelContext(boolean build) { * Called during object construction to initialize context plugins */ protected void initPlugins() { + camelContextExtension.addContextPlugin(StatefulService.class, createContextServiceLoaderPlugin()); camelContextExtension.addContextPlugin(StartupConditionStrategy.class, createStartupConditionStrategy()); camelContextExtension.addContextPlugin(CamelBeanPostProcessor.class, createBeanPostProcessor()); camelContextExtension.addContextPlugin(CamelDependencyInjectionAnnotationFactory.class, @@ -4471,6 +4473,8 @@ protected abstract EndpointRegistry createEndpointRegistry( protected abstract StartupConditionStrategy createStartupConditionStrategy(); + protected abstract StatefulService createContextServiceLoaderPlugin(); + protected abstract BackOffTimerFactory createBackOffTimerFactory(); protected abstract TaskManagerRegistry createTaskManagerRegistry(); diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultContextServiceLoaderPlugin.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultContextServiceLoaderPlugin.java new file mode 100644 index 0000000000000..5bcb73f65d551 --- /dev/null +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultContextServiceLoaderPlugin.java @@ -0,0 +1,75 @@ +/* + * 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.ServiceLoader; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.spi.ContextServicePlugin; +import org.apache.camel.support.service.ServiceSupport; + +/** + * Default implementation that automatically discovers and loads {@link ContextServicePlugin} implementations using the + * Java ServiceLoader mechanism. + *

+ * This service is responsible for scanning the classpath for implementations of {@code ContextServicePlugin} and + * invoking their {@code load} method during CamelContext startup. Plugin implementations are discovered through service + * provider configuration files located at: {@code META-INF/services/org.apache.camel.spi.ContextServicePlugin} + *

+ * The loading process occurs during the {@link #doStart()} phase, ensuring that all plugins are initialized before + * routes are started but after the CamelContext has been created and configured. + *

+ * This class extends {@link ServiceSupport} to participate in the Camel service lifecycle and implements + * {@link CamelContextAware} to receive the CamelContext instance that plugins will operate on. + * + * @see ContextServicePlugin + * @see ServiceLoader + */ +public class DefaultContextServiceLoaderPlugin extends ServiceSupport implements CamelContextAware { + private CamelContext camelContext; + + /** + * Discovers and loads all {@link ContextServicePlugin} implementations found on the classpath. + *

+ * This method is called during service startup and uses {@link ServiceLoader} to automatically discover plugin + * implementations. Each discovered plugin's {@code load} method is invoked with the current CamelContext, allowing + * plugins to perform their initialization logic. + *

+ * The plugins are loaded in the order they are discovered by the ServiceLoader, which may vary between JVM + * implementations and is generally not guaranteed to be deterministic. + * + * @throws Exception if any plugin fails to load or throws an exception during initialization + */ + @Override + protected void doStart() throws Exception { + ServiceLoader contextServicePlugins = ServiceLoader.load(ContextServicePlugin.class); + for (ContextServicePlugin plugin : contextServicePlugins) { + plugin.load(camelContext); + } + } + + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + + @Override + public CamelContext getCamelContext() { + return camelContext; + } +} diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java index 941ce38253ea5..8b2ec46a60c80 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java @@ -25,6 +25,7 @@ import org.apache.camel.Processor; import org.apache.camel.Route; import org.apache.camel.RouteTemplateContext; +import org.apache.camel.StatefulService; import org.apache.camel.TypeConverter; import org.apache.camel.catalog.RuntimeCamelCatalog; import org.apache.camel.console.DevConsoleRegistry; @@ -755,6 +756,11 @@ protected StartupConditionStrategy createStartupConditionStrategy() { return new DefaultStartupConditionStrategy(); } + @Override + protected StatefulService createContextServiceLoaderPlugin() { + return new DefaultContextServiceLoaderPlugin(); + } + @Override protected BackOffTimerFactory createBackOffTimerFactory() { return new DefaultBackOffTimerFactory(this);