diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java index d9d2d9d45..083e1c45d 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Constants.java @@ -28,6 +28,7 @@ public final class Constants { public static final String ROUTES_LOADER_RESOURCE_PATH = "META-INF/services/org/apache/camel/k/loader/"; public static final String CONTEXT_CUSTOMIZER_RESOURCE_PATH = "META-INF/services/org/apache/camel/k/customizer/"; public static final String PROPERTY_CAMEL_K_CUSTOMIZER = "camel.k.customizer"; + public static final String ENABLE_CUSTOMIZER_PATTERN = "customizer.([\\w][\\w-]*).enabled"; private Constants() { } diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/ContextCustomizer.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/ContextCustomizer.java index c6d6b1dbe..d3fc841e5 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/ContextCustomizer.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/ContextCustomizer.java @@ -17,9 +17,10 @@ package org.apache.camel.k; import org.apache.camel.CamelContext; +import org.apache.camel.Ordered; @FunctionalInterface -public interface ContextCustomizer { +public interface ContextCustomizer extends Ordered, Comparable{ /** * Perform CamelContext customization. * @@ -27,4 +28,14 @@ public interface ContextCustomizer { * @param registry the runtime registry. */ void apply(CamelContext camelContext, Runtime.Registry registry); + + @Override + default int getOrder() { + return 0; + } + + @Override + default int compareTo(ContextCustomizer o) { + return Integer.compare(getOrder(), o.getOrder()); + } } diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java index 597b9ccb4..a5cfff708 100644 --- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java +++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java @@ -32,7 +32,10 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.camel.CamelContext; import org.apache.camel.NoFactoryAvailableException; @@ -46,7 +49,6 @@ import org.apache.camel.spi.FactoryFinder; import org.apache.camel.spi.RestConfiguration; import org.apache.camel.util.ObjectHelper; -import org.apache.camel.util.StringHelper; import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,44 +62,92 @@ private RuntimeSupport() { public static List configureContext(CamelContext context, Runtime.Registry registry) { List appliedCustomizers = new ArrayList<>(); - Set customizers = lookupCustomizerIDs(context); + Map customizers = lookupCustomizers(context); - // this is to initialize all customizers that might be already present in - // the context injected by other means. - for (Map.Entry entry: context.getRegistry().findByTypeWithName(ContextCustomizer.class).entrySet()) { - if (!customizers.remove(entry.getKey())) { - continue; - } + customizers.entrySet().stream() + .sorted(Map.Entry.comparingByValue()) + .forEach(e -> { + LOGGER.info("Apply ContextCustomizer with id={} and type={}", e.getKey(), e.getValue().getClass().getName()); + + bindProperties(context, e.getValue(), "customizer." + e.getKey() + "."); + e.getValue().apply(context, registry); + + appliedCustomizers.add(e.getValue()); + }); - applyCustomizer(context, entry.getKey(), entry.getValue(), registry); + return appliedCustomizers; + } - appliedCustomizers.add(entry.getValue()); + public static void configureRest(CamelContext context) { + RestConfiguration configuration = new RestConfiguration(); + + if (RuntimeSupport.bindProperties(context, configuration, "camel.rest.") > 0) { + // + // Set the rest configuration if only if at least one + // rest parameter has been set. + // + context.setRestConfiguration(configuration); } + } - try { - FactoryFinder finder = context.getFactoryFinder(Constants.CONTEXT_CUSTOMIZER_RESOURCE_PATH); + // ********************************* + // + // Helpers - Customizers + // + // ********************************* - for (String customizerId : customizers) { - ContextCustomizer customizer = (ContextCustomizer) finder.newInstance(customizerId); - applyCustomizer(context, customizerId, customizer, registry); + public static Map lookupCustomizers(CamelContext context) { + Map customizers = new ConcurrentHashMap<>(); - appliedCustomizers.add(customizer); + PropertiesComponent component = context.getComponent("properties", PropertiesComponent.class); + Properties properties = component.getInitialProperties(); + + if (properties != null) { + // + // Lookup customizers listed in Constants.ENV_CAMEL_K_CUSTOMIZERS or Constants.PROPERTY_CAMEL_K_CUSTOMIZER + // for backward compatibility + // + for (String customizerId: lookupCustomizerIDs(context)) { + customizers.computeIfAbsent(customizerId, id -> lookupCustomizerByID(context, id)); } - } catch (NoFactoryAvailableException e) { - throw new RuntimeException(e); + + Pattern pattern = Pattern.compile(Constants.ENABLE_CUSTOMIZER_PATTERN); + + properties.entrySet().stream() + .filter(entry -> entry.getKey() instanceof String) + .filter(entry -> entry.getValue() != null) + .forEach(entry -> { + final String key = (String)entry.getKey(); + final Object val = entry.getValue(); + final Matcher matcher = pattern.matcher(key); + + if (matcher.matches() && matcher.groupCount() == 1) { + if (Boolean.valueOf(String.valueOf(val))) { + // + // Do not override customizers eventually found + // in the registry + // + customizers.computeIfAbsent(matcher.group(1), id -> lookupCustomizerByID(context, id)); + } + } + } + ); } - return appliedCustomizers; + return customizers; } - public static void applyCustomizer(CamelContext context, String customizerId, ContextCustomizer customizer, Runtime.Registry registry) { - ObjectHelper.notNull(customizer, "customizer"); - StringHelper.notEmpty(customizerId, "customizerId"); - - LOGGER.info("Apply ContextCustomizer with id={} and type={}", customizerId, customizer.getClass().getName()); + public static ContextCustomizer lookupCustomizerByID(CamelContext context, String customizerId) { + ContextCustomizer customizer = context.getRegistry().lookupByNameAndType(customizerId, ContextCustomizer.class); + if (customizer == null) { + try { + customizer = (ContextCustomizer) context.getFactoryFinder(Constants.CONTEXT_CUSTOMIZER_RESOURCE_PATH).newInstance(customizerId); + } catch (NoFactoryAvailableException e) { + throw new RuntimeException(e); + } + } - bindProperties(context, customizer, "customizer." + customizerId + "."); - customizer.apply(context, registry); + return customizer; } public static Set lookupCustomizerIDs(CamelContext context) { @@ -122,17 +172,11 @@ public static Set lookupCustomizerIDs(CamelContext context) { return customizers; } - public static void configureRest(CamelContext context) { - RestConfiguration configuration = new RestConfiguration(); - - if (RuntimeSupport.bindProperties(context, configuration, "camel.rest.") > 0) { - // - // Set the rest configuration if only if at least one - // rest parameter has been set. - // - context.setRestConfiguration(configuration); - } - } + // ********************************* + // + // Helpers - Properties + // + // ********************************* public static int bindProperties(CamelContext context, Object target, String prefix) { final PropertiesComponent component = context.getComponent("properties", PropertiesComponent.class); @@ -169,27 +213,6 @@ public static int bindProperties(Properties properties, Object target, String pr return count.get(); } - public static RoutesLoader loaderFor(CamelContext context, Source source) { - return context.getRegistry().findByType(RoutesLoader.class).stream() - .filter(rl -> rl.getSupportedLanguages().contains(source.getLanguage())) - .findFirst() - .orElseGet(() -> lookupLoaderFromResource(context, source)); - } - - public static RoutesLoader lookupLoaderFromResource(CamelContext context, Source source) { - final FactoryFinder finder; - final RoutesLoader loader; - - try { - finder = context.getFactoryFinder(Constants.ROUTES_LOADER_RESOURCE_PATH); - loader = (RoutesLoader)finder.newInstance(source.getLanguage()); - } catch (NoFactoryAvailableException e) { - throw new IllegalArgumentException("Unable to find loader for: " + source, e); - } - - return loader; - } - public static Properties loadProperties() { final String conf = System.getenv(Constants.ENV_CAMEL_K_CONF); final String confd = System.getenv(Constants.ENV_CAMEL_K_CONF_D); @@ -250,4 +273,31 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO return properties; } + + // ********************************* + // + // Helpers - Loaders + // + // ********************************* + + public static RoutesLoader loaderFor(CamelContext context, Source source) { + return context.getRegistry().findByType(RoutesLoader.class).stream() + .filter(rl -> rl.getSupportedLanguages().contains(source.getLanguage())) + .findFirst() + .orElseGet(() -> lookupLoaderFromResource(context, source)); + } + + public static RoutesLoader lookupLoaderFromResource(CamelContext context, Source source) { + final FactoryFinder finder; + final RoutesLoader loader; + + try { + finder = context.getFactoryFinder(Constants.ROUTES_LOADER_RESOURCE_PATH); + loader = (RoutesLoader)finder.newInstance(source.getLanguage()); + } catch (NoFactoryAvailableException e) { + throw new IllegalArgumentException("Unable to find loader for: " + source, e); + } + + return loader; + } } diff --git a/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/NameCustomizer.java b/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/NameCustomizer.java index 38bad6493..0c19cba88 100644 --- a/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/NameCustomizer.java +++ b/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/NameCustomizer.java @@ -17,6 +17,7 @@ package org.apache.camel.k.support; import org.apache.camel.CamelContext; +import org.apache.camel.Ordered; import org.apache.camel.impl.ExplicitCamelContextNameStrategy; import org.apache.camel.k.ContextCustomizer; import org.apache.camel.k.Runtime; @@ -32,6 +33,11 @@ public NameCustomizer(String name) { this.name = name; } + @Override + public int getOrder() { + return Ordered.HIGHEST; + } + @Override public void apply(CamelContext camelContext, Runtime.Registry registry) { camelContext.setNameStrategy(new ExplicitCamelContextNameStrategy(name)); diff --git a/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/RuntimeSupportTest.java b/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/RuntimeSupportTest.java index 592d633fc..11a121016 100644 --- a/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/RuntimeSupportTest.java +++ b/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/RuntimeSupportTest.java @@ -20,8 +20,10 @@ import java.util.Properties; import org.apache.camel.CamelContext; +import org.apache.camel.Ordered; import org.apache.camel.component.properties.PropertiesComponent; import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.impl.ExplicitCamelContextNameStrategy; import org.apache.camel.k.Constants; import org.apache.camel.k.ContextCustomizer; import org.apache.camel.k.InMemoryRegistry; @@ -33,7 +35,7 @@ public class RuntimeSupportTest { @Test - public void testLoadCustomizers() { + public void testLoadCustomizersWithPropertiesFlags() { PropertiesComponent pc = new PropertiesComponent(); Runtime.Registry registry = new InMemoryRegistry(); CamelContext context = new DefaultCamelContext(registry); @@ -48,7 +50,7 @@ public void testLoadCustomizers() { assertThat(customizers).hasSize(0); Properties properties = new Properties(); - properties.setProperty(Constants.PROPERTY_CAMEL_K_CUSTOMIZER, "name"); + properties.setProperty("customizer.name.enabled", "true"); pc.setInitialProperties(properties); customizers = RuntimeSupport.configureContext(context, registry); @@ -57,26 +59,104 @@ public void testLoadCustomizers() { } @Test - public void testLoadCustomizersOrdering() { + public void testLoadCustomizersWithList() { PropertiesComponent pc = new PropertiesComponent(); Runtime.Registry registry = new InMemoryRegistry(); CamelContext context = new DefaultCamelContext(registry); context.addComponent("properties", pc); + NameCustomizer customizer = new NameCustomizer("from-registry"); + registry.bind("name", customizer); + + List customizers = RuntimeSupport.configureContext(context, registry); + assertThat(context.getName()).isNotEqualTo("from-registry"); + assertThat(context.getName()).isNotEqualTo("default"); + assertThat(customizers).hasSize(0); + Properties properties = new Properties(); properties.setProperty(Constants.PROPERTY_CAMEL_K_CUSTOMIZER, "name"); pc.setInitialProperties(properties); + customizers = RuntimeSupport.configureContext(context, registry); + assertThat(context.getName()).isEqualTo("from-registry"); + assertThat(customizers).hasSize(1); + } + + @Test + public void testLoadCustomizers() { + PropertiesComponent pc = new PropertiesComponent(); + Runtime.Registry registry = new InMemoryRegistry(); + CamelContext context = new DefaultCamelContext(registry); + context.addComponent("properties", pc); + + registry.bind("converters", (ContextCustomizer) (camelContext, registry1) -> camelContext.setLoadTypeConverters(false)); + List customizers = RuntimeSupport.configureContext(context, registry); + assertThat(context.getName()).isNotEqualTo("from-registry"); + assertThat(context.getName()).isNotEqualTo("default"); + assertThat(context.isLoadTypeConverters()).isTrue(); + assertThat(customizers).hasSize(0); + + Properties properties = new Properties(); + properties.setProperty("customizer.name.enabled", "true"); + pc.setInitialProperties(properties); + + customizers = RuntimeSupport.configureContext(context, registry); assertThat(context.getName()).isEqualTo("default"); assertThat(customizers).hasSize(1); - NameCustomizer customizer = new NameCustomizer("from-registry"); - registry.bind("name", customizer); + properties.setProperty("customizer.converters.enabled", "true"); + pc.setInitialProperties(properties); customizers = RuntimeSupport.configureContext(context, registry); - assertThat(context.getName()).isEqualTo("from-registry"); - assertThat(customizers).hasSize(1); + assertThat(context.getName()).isEqualTo("default"); + assertThat(context.isLoadTypeConverters()).isFalse(); + assertThat(customizers).hasSize(2); } + @Test + public void testLoadCustomizerOrder() { + PropertiesComponent pc = new PropertiesComponent(); + Runtime.Registry registry = new InMemoryRegistry(); + DefaultCamelContext context = new DefaultCamelContext(registry); + context.setName("camel"); + context.addComponent("properties", pc); + + registry.bind("c1", new ContextCustomizer() { + @Override + public int getOrder() { + return Ordered.LOWEST; + } + @Override + public void apply(CamelContext camelContext, Runtime.Registry registry) { + camelContext.setNameStrategy(new ExplicitCamelContextNameStrategy(camelContext.getName() + "-c1")); + } + }); + registry.bind("c2", new ContextCustomizer() { + @Override + public int getOrder() { + return Ordered.HIGHEST; + } + @Override + public void apply(CamelContext camelContext, Runtime.Registry registry) { + camelContext.setNameStrategy(new ExplicitCamelContextNameStrategy(camelContext.getName() + "-c2")); + } + }); + registry.bind("c3", new ContextCustomizer() { + @Override + public void apply(CamelContext camelContext, Runtime.Registry registry) { + camelContext.setNameStrategy(new ExplicitCamelContextNameStrategy(camelContext.getName() + "-c3")); + } + }); + + Properties properties = new Properties(); + properties.setProperty("customizer.c1.enabled", "true"); + properties.setProperty("customizer.c2.enabled", "true"); + properties.setProperty("customizer.c3.enabled", "true"); + pc.setInitialProperties(properties); + + List customizers = RuntimeSupport.configureContext(context, registry); + assertThat(customizers).hasSize(3); + assertThat(context.getName()).isEqualTo("camel-c2-c3-c1"); + } } diff --git a/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/PropertiesTest.java b/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/PropertiesTest.java index cd82ef740..65916a71d 100644 --- a/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/PropertiesTest.java +++ b/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/PropertiesTest.java @@ -21,7 +21,6 @@ import org.apache.camel.CamelContext; import org.apache.camel.component.seda.SedaComponent; -import org.apache.camel.k.Constants; import org.apache.camel.k.ContextCustomizer; import org.apache.camel.k.Runtime; import org.apache.camel.k.listener.ContextConfigurer; @@ -130,11 +129,12 @@ public void testContextConfiguration() throws Exception { @Test public void testContextCustomizerFromProperty() throws Exception { - System.setProperty(Constants.PROPERTY_CAMEL_K_CUSTOMIZER, "test"); - System.setProperty("customizer.test.messageHistory", "false"); + Properties properties = new Properties(); + properties.setProperty("customizer.test.enabled", "true"); + properties.setProperty("customizer.test.messageHistory", "false"); ApplicationRuntime runtime = new ApplicationRuntime(); - runtime.setProperties(System.getProperties()); + runtime.setProperties(properties); runtime.addListener(new ContextConfigurer()); runtime.addListener(new ContextLifecycleConfigurer()); runtime.addListener(Runtime.Phase.Started, r -> { @@ -149,8 +149,11 @@ public void testContextCustomizerFromProperty() throws Exception { @Test public void testContextCustomizerFromRegistry() throws Exception { + Properties properties = new Properties(); + properties.setProperty("customizer.c1.enabled", "true"); + ApplicationRuntime runtime = new ApplicationRuntime(); - runtime.setProperties(System.getProperties()); + runtime.setProperties(properties); runtime.addListener(new ContextConfigurer()); runtime.addListener(new ContextLifecycleConfigurer()); runtime.getRegistry().bind("c1", (ContextCustomizer) (camelContext, registry) -> {