This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit 6380fe68d874b894d0eaf2e8b99cbc0382f5f85e Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Jul 1 11:13:20 2019 +0200 CAMEL-13705: Properties component should work with Eclipse MicroProfile Config --- components/camel-microprofile-config/pom.xml | 87 ++++++++++++++++++++++ .../src/main/docs/microprofile-config.adoc | 19 +++++ .../config/CamelMicroProfilePropertiesSource.java | 85 +++++++++++++++++++++ .../org/apache/camel/properties-source-factory | 18 +++++ .../CamelMicroProfilePropertiesSourceTest.java | 74 ++++++++++++++++++ .../src/test/resources/log4j2.properties | 28 +++++++ .../component/properties/PropertiesComponent.java | 61 +++++++++++++++ .../component/properties/PropertiesSource.java | 38 ++++++++++ .../camel/impl/engine/AbstractCamelContext.java | 2 + 9 files changed, 412 insertions(+) diff --git a/components/camel-microprofile-config/pom.xml b/components/camel-microprofile-config/pom.xml new file mode 100644 index 0000000..0f64b79 --- /dev/null +++ b/components/camel-microprofile-config/pom.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>3.0.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-microprofile-config</artifactId> + <packaging>jar</packaging> + + <name>Camel :: MicroProfile Config</name> + <description>Camel Eclipse MicroProfile Config</description> + + <dependencies> + + <!-- extends the properties component --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-properties</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.microprofile.config</groupId> + <artifactId>microprofile-config-api</artifactId> + <version>1.3</version> + </dependency> + + <!-- testing --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>io.smallrye</groupId> + <artifactId>smallrye-config-1.3</artifactId> + <version>1.0.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> +</project> diff --git a/components/camel-microprofile-config/src/main/docs/microprofile-config.adoc b/components/camel-microprofile-config/src/main/docs/microprofile-config.adoc new file mode 100644 index 0000000..ae43410 --- /dev/null +++ b/components/camel-microprofile-config/src/main/docs/microprofile-config.adoc @@ -0,0 +1,19 @@ +[[MicroProfileConfig-MicroProfileConfigComponent]] +=== MicroProfile Config Component + +*Available as of Camel 3.0* + +The microprofile-config component is used for bridging the Eclipse MicroProfile Config with Camels +properties component. This allows to use configuration management from Eclipse MicroProfile with Camel. + +To enable this just add this component to the classpath and Camel should auto-detect this when starting up. + +=== Register manually + +You can also register the microprofile-config component manually with Apache Camel properties component as shown below: + +[source,java] +---- + PropertiesComponent pc = (PropertiesComponent) camelContext.getPropertiesComponent(); + pc.addPropertiesSource(new CamelMicroProfilePropertiesSource()); +---- diff --git a/components/camel-microprofile-config/src/main/java/org/apache/camel/component/microprofile/config/CamelMicroProfilePropertiesSource.java b/components/camel-microprofile-config/src/main/java/org/apache/camel/component/microprofile/config/CamelMicroProfilePropertiesSource.java new file mode 100644 index 0000000..31aed16 --- /dev/null +++ b/components/camel-microprofile-config/src/main/java/org/apache/camel/component/microprofile/config/CamelMicroProfilePropertiesSource.java @@ -0,0 +1,85 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.component.microprofile.config; + +import java.util.Optional; +import java.util.Properties; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.component.properties.PropertiesComponent; +import org.apache.camel.component.properties.PropertiesSource; +import org.apache.camel.support.service.ServiceSupport; +import org.apache.camel.util.OrderedProperties; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * To use Camel's {@link PropertiesComponent} as an Eclipse {@link ConfigSource}. + */ +public class CamelMicroProfilePropertiesSource extends ServiceSupport implements CamelContextAware, PropertiesSource { + + private static final Logger LOG = LoggerFactory.getLogger(CamelMicroProfilePropertiesSource.class); + + private CamelContext camelContext; + private final Properties properties = new OrderedProperties(); + + @Override + public CamelContext getCamelContext() { + return camelContext; + } + + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + + @Override + public String getName() { + return "CamelMicroProfilePropertiesSource"; + } + + @Override + public Properties loadProperties() { + return properties; + } + + @Override + protected void doInit() throws Exception { + Config config = ConfigProvider.getConfig(); + + for (String name : config.getPropertyNames()) { + Optional<String> value = config.getOptionalValue(name, String.class); + value.ifPresent(s -> properties.put(name, s)); + } + + LOG.info("Initialized CamelMicroProfilePropertiesSource with {} properties loaded", properties.size()); + } + + @Override + protected void doStart() throws Exception { + // noop + } + + @Override + protected void doStop() throws Exception { + // noop + } +} diff --git a/components/camel-microprofile-config/src/main/resources/META-INF/services/org/apache/camel/properties-source-factory b/components/camel-microprofile-config/src/main/resources/META-INF/services/org/apache/camel/properties-source-factory new file mode 100644 index 0000000..74bd136 --- /dev/null +++ b/components/camel-microprofile-config/src/main/resources/META-INF/services/org/apache/camel/properties-source-factory @@ -0,0 +1,18 @@ +# +# 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. +# + +class=org.apache.camel.component.microprofile.config.CamelMicroProfileConfigSource diff --git a/components/camel-microprofile-config/src/test/java/org/apache/camel/component/microprofile/config/CamelMicroProfilePropertiesSourceTest.java b/components/camel-microprofile-config/src/test/java/org/apache/camel/component/microprofile/config/CamelMicroProfilePropertiesSourceTest.java new file mode 100644 index 0000000..3bb0c61 --- /dev/null +++ b/components/camel-microprofile-config/src/test/java/org/apache/camel/component/microprofile/config/CamelMicroProfilePropertiesSourceTest.java @@ -0,0 +1,74 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.component.microprofile.config; + +import java.util.Properties; + +import io.smallrye.config.PropertiesConfigSource; +import io.smallrye.config.SmallRyeConfigBuilder; +import org.apache.camel.CamelContext; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.properties.PropertiesComponent; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.junit.Test; + +public class CamelMicroProfilePropertiesSourceTest extends CamelTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + // setup MPC + Properties prop = new Properties(); + prop.put("start", "direct:start"); + prop.put("hi", "World"); + prop.put("my-mock", "result"); + + // create PMC config source and register it so we can use it for testing + PropertiesConfigSource pcs = new PropertiesConfigSource(prop, "my-smallrye-config"); + final Config config = new SmallRyeConfigBuilder().withSources(pcs).build(); + ConfigProviderResolver.instance().registerConfig(config, CamelMicroProfilePropertiesSourceTest.class.getClassLoader()); + + // should auto-detect this JAR on the classpath and use it (but this can only be tested outside this component) + CamelContext context = super.createCamelContext(); + // ... so we add the source manually + PropertiesComponent pc = (PropertiesComponent) context.getPropertiesComponent(); + pc.addPropertiesSource(new CamelMicroProfilePropertiesSource()); + return context; + } + + @Test + public void testMicroProfileConfig() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); + + template.sendBody("direct:start", context.resolvePropertyPlaceholders("Hello {{hi}}")); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("{{start}}") + .to("mock:{{my-mock}}"); + } + }; + } +} diff --git a/components/camel-microprofile-config/src/test/resources/log4j2.properties b/components/camel-microprofile-config/src/test/resources/log4j2.properties new file mode 100644 index 0000000..27dc528 --- /dev/null +++ b/components/camel-microprofile-config/src/test/resources/log4j2.properties @@ -0,0 +1,28 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +appender.file.type = File +appender.file.name = file +appender.file.fileName = target/camel-microprofile-config-test.log +appender.file.layout.type = PatternLayout +appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +appender.out.type = Console +appender.out.name = out +appender.out.layout.type = PatternLayout +appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level = INFO +rootLogger.appenderRef.file.ref = file diff --git a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java index 4ddea23..120fcb6 100644 --- a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java +++ b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java @@ -27,17 +27,25 @@ import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; +import org.apache.camel.CamelContextAware; import org.apache.camel.Endpoint; +import org.apache.camel.ExtendedCamelContext; +import org.apache.camel.NoFactoryAvailableException; import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedOperation; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.spi.FactoryFinder; +import org.apache.camel.spi.HeadersMapFactory; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Component; import org.apache.camel.support.DefaultComponent; import org.apache.camel.support.LRUCacheFactory; +import org.apache.camel.support.service.ServiceHelper; import org.apache.camel.util.FilePathResolver; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.OrderedProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The <a href="http://camel.apache.org/properties">Properties Component</a> allows you to use property placeholders when defining Endpoint URIs @@ -46,6 +54,8 @@ import org.apache.camel.util.OrderedProperties; @ManagedResource(description = "Managed PropertiesComponent") public class PropertiesComponent extends DefaultComponent implements org.apache.camel.spi.PropertiesComponent { + private static final Logger LOG = LoggerFactory.getLogger(PropertiesComponent.class); + /** * Never check system properties. */ @@ -96,6 +106,7 @@ public class PropertiesComponent extends DefaultComponent implements org.apache. private PropertiesResolver propertiesResolver = new DefaultPropertiesResolver(this); private PropertiesParser propertiesParser = new DefaultPropertiesParser(this); private List<PropertiesLocation> locations = Collections.emptyList(); + private List<PropertiesSource> sources = new ArrayList<>(); private transient String propertyPrefixResolved; @@ -204,6 +215,14 @@ public class PropertiesComponent extends DefaultComponent implements org.apache. prop.putAll(initialProperties); } + // add 3rd party sources + if (!sources.isEmpty()) { + for (PropertiesSource ps : sources) { + Properties p = ps.loadProperties(); + prop.putAll(p); + } + } + // use locations if (paths != null) { // location may contain JVM system property or OS environment variables @@ -577,9 +596,50 @@ public class PropertiesComponent extends DefaultComponent implements org.apache. this.cacheMap.clear(); } + /** + * Adds a custom {@link PropertiesSource} + */ + public void addPropertiesSource(PropertiesSource propertiesSource) { + sources.add(propertiesSource); + // prepare properties sources which we must do eager + for (PropertiesSource ps : sources) { + if (ps instanceof CamelContextAware) { + ((CamelContextAware) ps).setCamelContext(getCamelContext()); + } + } + ServiceHelper.initService(propertiesSource); + } + + @Override + protected void doInit() throws Exception { + super.doInit(); + + // discover any 3rd party properties sources + try { + FactoryFinder factoryFinder = getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder("META-INF/services/org/apache/camel"); + Class<?> type = factoryFinder.findClass("properties-source-factory"); + if (type != null) { + Object ps = getCamelContext().getInjector().newInstance(type, false); + if (ps != null) { + if (ps instanceof PropertiesSource) { + LOG.info("PropertiesComponent added custom PropertiesSource: {}", ps); + addPropertiesSource((PropertiesSource) ps); + } else { + LOG.warn("PropertiesComponent cannot add custom PropertiesSource as the type is not a org.apache.camel.component.properties.PropertiesSource but: " + type.getName()); + } + } + } + } catch (NoFactoryAvailableException e) { + // ignore + } catch (Exception e) { + LOG.debug("Error discovering and using custom PropertiesSource due to " + e.getMessage() + ". This exception is ignored", e); + } + } + @Override protected void doStart() throws Exception { super.doStart(); + ServiceHelper.startService(sources); if (systemPropertiesMode != SYSTEM_PROPERTIES_MODE_NEVER && systemPropertiesMode != SYSTEM_PROPERTIES_MODE_FALLBACK @@ -602,6 +662,7 @@ public class PropertiesComponent extends DefaultComponent implements org.apache. protected void doStop() throws Exception { cacheMap.clear(); super.doStop(); + ServiceHelper.stopAndShutdownService(sources); } private List<PropertiesLocation> parseLocations(List<PropertiesLocation> locations) { diff --git a/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesSource.java b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesSource.java new file mode 100644 index 0000000..0f8f43a --- /dev/null +++ b/components/camel-properties/src/main/java/org/apache/camel/component/properties/PropertiesSource.java @@ -0,0 +1,38 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.component.properties; + +import java.util.Properties; + +/** + * A source for 3rd party properties, such as Eclipse MicroProfile Config, a custom implementation + * that loads properties from a database table, or HashiCorp Vault etc. + */ +public interface PropertiesSource { + + /** + * Name of properties source + */ + String getName(); + + /** + * Loads the properties from the source + * + * @return the loaded properties + */ + Properties loadProperties(); +} diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java index 6d5b5c7..4da8795 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java @@ -441,6 +441,8 @@ public abstract class AbstractCamelContext extends ServiceSupport implements Ext // keep reference to properties component up to date if (component instanceof PropertiesComponent && "properties".equals(componentName)) { propertiesComponent = (PropertiesComponent)component; + // ensure properties component is initialize early + ServiceHelper.initService(propertiesComponent); } }