This is an automated email from the ASF dual-hosted git repository. jfrazee pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/streams.git
The following commit(s) were added to refs/heads/master by this push: new c809e47 STREAMS-580: Namespacing with fallbacks in ComponentConfigurator c809e47 is described below commit c809e474d4777160578cadb6a5b63be3971a70bd Author: Steve Blackmon <sblack...@apache.org> AuthorDate: Fri Feb 9 15:20:59 2018 -0600 STREAMS-580: Namespacing with fallbacks in ComponentConfigurator Closes #421 --- streams-config/pom.xml | 5 + .../streams/config/ComponentConfigurator.java | 99 ++++++++++ .../main/jsonschema/ComponentConfiguration.json | 6 +- streams-config/src/main/resources/reference.conf | 4 + .../ComponentConfigurationForTestingNumberOne.java | 7 + .../ComponentConfigurationForTestingNumberTwo.java | 7 + .../config/test/ComponentConfiguratorTest.java | 214 ++++++++++++++++----- .../src/test/resources/canonicalClassName.conf | 8 + .../src/test/resources/classHierarchy.conf | 21 ++ .../src/test/resources/componentTest.conf | 11 +- .../src/test/resources/packageHierarchy.conf | 21 ++ streams-config/src/test/resources/packagePath.conf | 8 + .../src/test/resources/simpleClassName.conf | 8 + 13 files changed, 358 insertions(+), 61 deletions(-) diff --git a/streams-config/pom.xml b/streams-config/pom.xml index 1a42c9e..0c4d5c8 100644 --- a/streams-config/pom.xml +++ b/streams-config/pom.xml @@ -84,6 +84,11 @@ <artifactId>junit</artifactId> </dependency> <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-all</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> </dependency> diff --git a/streams-config/src/main/java/org/apache/streams/config/ComponentConfigurator.java b/streams-config/src/main/java/org/apache/streams/config/ComponentConfigurator.java index ce88a23..e8f8760 100644 --- a/streams-config/src/main/java/org/apache/streams/config/ComponentConfigurator.java +++ b/streams-config/src/main/java/org/apache/streams/config/ComponentConfigurator.java @@ -21,10 +21,14 @@ package org.apache.streams.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.typesafe.config.Config; import com.typesafe.config.ConfigRenderOptions; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; /** * ComponentConfigurator supplies serializable configuration beans derived from a specified typesafe path or object. @@ -52,6 +56,88 @@ public class ComponentConfigurator<T extends Serializable> { private static final ObjectMapper mapper = new ObjectMapper(); /** + * resolve a serializable configuration pojo compiled from appropriate parts of the global JVM configuration tree. + * + * the entire object, or fragments of it, will be collected and merged from: + * - the simple class name of the configured class + * - the fully qualified class name of the configured class + * - any of the ancestor classes of the configured class + * - the configured class's package + * - any of the parent packages of the configured class's package + * + * @return result + */ + public T detectConfiguration() { + + T pojoConfig = null; + + Config rootConfig = StreamsConfigurator.getConfig(); + + Config cascadeConfig = null; + + String[] canonicalNameParts = StringUtils.split(configClass.getCanonicalName(), '.'); + + for( int partIndex = 1; partIndex < canonicalNameParts.length; partIndex++) { + String[] partialPathParts = ArrayUtils.subarray(canonicalNameParts, 0, partIndex); + String partialPath = StringUtils.join(partialPathParts, '.'); + + if( rootConfig.hasPath(partialPath) ) { + Config partialPathConfig = rootConfig.getConfig(partialPath).withoutPath(canonicalNameParts[partIndex]); + if( !partialPathConfig.root().isEmpty()) { + if (cascadeConfig == null) { + cascadeConfig = partialPathConfig; + } else { + cascadeConfig = partialPathConfig.withFallback(cascadeConfig); + } + } + } + + } + + List<Class> superclasses = getSuperClasses(configClass); + + for( Class superclass : superclasses) { + String superclassCanonicalName = superclass.getCanonicalName(); + if( rootConfig.hasPath(superclassCanonicalName)) { + Config superclassConfig = rootConfig.getConfig(superclassCanonicalName); + if (cascadeConfig == null) { + cascadeConfig = superclassConfig; + } else { + cascadeConfig = superclassConfig.withFallback(cascadeConfig); + } + } + } + + + if( rootConfig.hasPath(configClass.getSimpleName()) ) { + Config simpleNameConfig = rootConfig.getConfig(configClass.getSimpleName()); + if( cascadeConfig == null ) { + cascadeConfig = simpleNameConfig; + } else { + cascadeConfig = simpleNameConfig.withFallback(cascadeConfig); + } + } + + if( rootConfig.hasPath(configClass.getCanonicalName()) ) { + Config canonicalNameConfig = rootConfig.getConfig(configClass.getCanonicalName()); + if( cascadeConfig == null ) { + cascadeConfig = canonicalNameConfig; + } else { + cascadeConfig = canonicalNameConfig.withFallback(cascadeConfig); + } + } + + try { + pojoConfig = mapper.readValue(cascadeConfig.root().render(ConfigRenderOptions.concise()), configClass); + } catch (Exception ex) { + ex.printStackTrace(); + LOGGER.warn("Could not parse:", cascadeConfig); + } + + return pojoConfig; + } + + /** * resolve a serializable configuration pojo from a given typesafe config object. * @param typesafeConfig typesafeConfig * @return result @@ -89,4 +175,17 @@ public class ComponentConfigurator<T extends Serializable> { public T detectConfiguration(Config typesafeConfig, String subConfig) { return detectConfiguration( typesafeConfig.getConfig(subConfig)); } + + /* + * return class hierarchy in order from furthest to nearest ancestor + */ + public static List<Class> getSuperClasses(Class clazz) { + List<Class> classList = new ArrayList<Class>(); + Class superclass = clazz.getSuperclass(); + while (superclass != null && !superclass.isInterface() && superclass != java.lang.Object.class) { + classList.add(0, superclass); + superclass = superclass.getSuperclass(); + } + return classList; + } } diff --git a/streams-config/src/main/jsonschema/ComponentConfiguration.json b/streams-config/src/main/jsonschema/ComponentConfiguration.json index 4e015fe..9ed9add 100644 --- a/streams-config/src/main/jsonschema/ComponentConfiguration.json +++ b/streams-config/src/main/jsonschema/ComponentConfiguration.json @@ -12,15 +12,13 @@ "type" : "array", "items" : { "type" : "string" - }, - "default": ["java.lang.String"] + } }, "outClasses": { "type" : "array", "items" : { "type" : "string" - }, - "default": ["java.lang.String"] + } } } } \ No newline at end of file diff --git a/streams-config/src/main/resources/reference.conf b/streams-config/src/main/resources/reference.conf index 67e36af..6e83394 100644 --- a/streams-config/src/main/resources/reference.conf +++ b/streams-config/src/main/resources/reference.conf @@ -9,3 +9,7 @@ queueSize = 1000 shutdownCheckDelay = 1000 shutdownCheckInterval = 1000 startedAt = -1 +defaultComponent { + inClasses = [ "java.lang.Object" ] + outClasses = [ "java.lang.Object" ] +} diff --git a/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfigurationForTestingNumberOne.java b/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfigurationForTestingNumberOne.java new file mode 100644 index 0000000..fa073d7 --- /dev/null +++ b/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfigurationForTestingNumberOne.java @@ -0,0 +1,7 @@ +package org.apache.streams.config.test; + +import org.apache.streams.config.ComponentConfiguration; + +public class ComponentConfigurationForTestingNumberOne extends ComponentConfiguration { + +} diff --git a/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfigurationForTestingNumberTwo.java b/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfigurationForTestingNumberTwo.java new file mode 100644 index 0000000..ba98eee --- /dev/null +++ b/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfigurationForTestingNumberTwo.java @@ -0,0 +1,7 @@ +package org.apache.streams.config.test; + +import org.apache.streams.config.ComponentConfiguration; + +public class ComponentConfigurationForTestingNumberTwo extends ComponentConfigurationForTestingNumberOne { + +} diff --git a/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfiguratorTest.java b/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfiguratorTest.java index 80fa7de..765758a 100644 --- a/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfiguratorTest.java +++ b/streams-config/src/test/java/org/apache/streams/config/test/ComponentConfiguratorTest.java @@ -33,93 +33,211 @@ import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.core.IsEqual.equalTo; + /** -* Test for -* @see org.apache.streams.config.ComponentConfigurator -*/ + * Test for + * @see ComponentConfigurator + */ @RunWith(PowerMockRunner.class) @PrepareForTest(StreamsConfigurator.class) public class ComponentConfiguratorTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void testDetectDefaults() throws Exception { + + Config config = ConfigFactory.load("componentTest"); + + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + + ComponentConfiguration defaultPojo = configurator.detectConfiguration(config.getConfig("defaultComponent")); + + assert(defaultPojo != null); + + ComponentConfiguration configuredPojo = configurator.detectConfiguration(config.getConfig("configuredComponent")); + + assert(configuredPojo != null); + + Assert.assertEquals(configuredPojo,defaultPojo); + + } + + @Test + public void testDetectConfigurationConfig() throws Exception { + + Config config = ConfigFactory.load("componentTest").getConfig("configuredComponent"); + + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + + ComponentConfiguration testPojo = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), ComponentConfiguration.class); + + assert(testPojo != null); + + ComponentConfiguration configuredPojo = configurator.detectConfiguration(config); + + assert(configuredPojo != null); + + Assert.assertEquals(configuredPojo,testPojo); + + } + + @Test + public void testDetectConfigurationString() throws Exception { + + Config config = ConfigFactory.load("componentTest"); + + PowerMockito.mockStatic(StreamsConfigurator.class); + + PowerMockito.when(StreamsConfigurator.getConfig()) + .thenReturn(config); + + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + + ComponentConfiguration testPojo = mapper.readValue(config.root().get("configuredComponent").render(ConfigRenderOptions.concise()), ComponentConfiguration.class); + + assert(testPojo != null); + + ComponentConfiguration configuredPojo = configurator.detectConfiguration("configuredComponent"); + + assert(configuredPojo != null); + + Assert.assertEquals(configuredPojo,testPojo); + } + + @Test + public void testDetectConfigurationConfigString() throws Exception { + + Config config = ConfigFactory.load("componentTest"); + + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + + ComponentConfiguration testPojo = mapper.readValue(config.root().get("configuredComponent").render(ConfigRenderOptions.concise()), ComponentConfiguration.class); + + assert(testPojo != null); + + ComponentConfiguration configuredPojo = configurator.detectConfiguration(config, "configuredComponent"); + + assert(configuredPojo != null); + + Assert.assertEquals(configuredPojo,testPojo); + } + + @Test + public void testDetectConfigurationCompoundPath() throws Exception { + + Config testConfig = ConfigFactory.parseResourcesAnySyntax("packagePath.conf"); + + StreamsConfigurator.setConfig(testConfig); + + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + + ComponentConfiguration configuredPojo = configurator.detectConfiguration("org.apache.streams.config"); + + Assert.assertThat(configuredPojo, is(notNullValue())); + + Assert.assertThat(configuredPojo.getInClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getInClasses().size(), is(greaterThan(0))); + + Assert.assertThat(configuredPojo.getOutClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getOutClasses().size(), is(greaterThan(0))); + + } + + @Test + public void testDetectConfigurationSimpleClassName() throws Exception { - @Test - public void testDetectDefaults() throws Exception { + Config testConfig = ConfigFactory.parseResourcesAnySyntax("simpleClassName.conf"); - Config config = ConfigFactory.load("componentTest"); + StreamsConfigurator.setConfig(testConfig); - ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); - - ComponentConfiguration defaultPojo = configurator.detectConfiguration(config.getConfig("defaultComponent")); + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); - assert(defaultPojo != null); + ComponentConfiguration configuredPojo = configurator.detectConfiguration(); - ComponentConfiguration configuredPojo = configurator.detectConfiguration(config.getConfig("configuredComponent")); + Assert.assertThat(configuredPojo, is(notNullValue())); - assert(configuredPojo != null); + Assert.assertThat(configuredPojo.getInClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getInClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getInClasses().get(0), equalTo("java.lang.Object")); - Assert.assertEquals(configuredPojo,defaultPojo); + Assert.assertThat(configuredPojo.getOutClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getOutClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getOutClasses().get(0), equalTo("java.lang.Object")); - } + } - @Test - public void testDetectConfigurationConfig() throws Exception { + @Test + public void testDetectConfigurationCanonicalClassName() throws Exception { - Config config = ConfigFactory.load("componentTest").getConfig("configuredComponent"); + Config testConfig = ConfigFactory.parseResourcesAnySyntax("canonicalClassName.conf"); - ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + StreamsConfigurator.setConfig(testConfig); - ComponentConfiguration testPojo = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), ComponentConfiguration.class); + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); - assert(testPojo != null); + ComponentConfiguration configuredPojo = configurator.detectConfiguration(); - ComponentConfiguration configuredPojo = configurator.detectConfiguration(config); + Assert.assertThat(configuredPojo, is(notNullValue())); - assert(configuredPojo != null); + Assert.assertThat(configuredPojo.getInClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getInClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getInClasses().get(0), equalTo("java.lang.Object")); - Assert.assertEquals(configuredPojo,testPojo); + Assert.assertThat(configuredPojo.getOutClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getOutClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getOutClasses().get(0), equalTo("java.lang.Object")); - } + } - @Test - public void testDetectConfigurationString() throws Exception { + @Test + public void testDetectConfigurationClassHierarchy() throws Exception { - Config config = ConfigFactory.load("componentTest"); + Config testConfig = ConfigFactory.parseResourcesAnySyntax("classHierarchy.conf"); - PowerMockito.mockStatic(StreamsConfigurator.class); + StreamsConfigurator.setConfig(testConfig); - PowerMockito.when(StreamsConfigurator.getConfig()) - .thenReturn(config); + ComponentConfigurator<ComponentConfigurationForTestingNumberTwo> configurator = new ComponentConfigurator(ComponentConfigurationForTestingNumberTwo.class); - ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + ComponentConfiguration configuredPojo = configurator.detectConfiguration(); - ComponentConfiguration testPojo = mapper.readValue(config.root().get("configuredComponent").render(ConfigRenderOptions.concise()), ComponentConfiguration.class); + Assert.assertThat(configuredPojo, is(notNullValue())); - assert(testPojo != null); + Assert.assertThat(configuredPojo.getInClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getInClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getInClasses().get(0), equalTo("java.lang.Integer")); - ComponentConfiguration configuredPojo = configurator.detectConfiguration("configuredComponent"); + Assert.assertThat(configuredPojo.getOutClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getOutClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getOutClasses().get(0), equalTo("java.lang.Float")); - assert(configuredPojo != null); + } - Assert.assertEquals(configuredPojo,testPojo); - } + @Test + public void testDetectConfigurationPackageHierarchy() throws Exception { - @Test - public void testDetectConfigurationConfigString() throws Exception { + Config testConfig = ConfigFactory.parseResourcesAnySyntax("packageHierarchy.conf"); - Config config = ConfigFactory.load("componentTest"); + StreamsConfigurator.setConfig(testConfig); - ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator<>(ComponentConfiguration.class); + ComponentConfigurator<ComponentConfiguration> configurator = new ComponentConfigurator(ComponentConfiguration.class); - ComponentConfiguration testPojo = mapper.readValue(config.root().get("configuredComponent").render(ConfigRenderOptions.concise()), ComponentConfiguration.class); + ComponentConfiguration configuredPojo = configurator.detectConfiguration(); + Assert.assertThat(configuredPojo, is(notNullValue())); - assert(testPojo != null); + Assert.assertThat(configuredPojo.getInClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getInClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getInClasses().get(0), equalTo("java.lang.Integer")); - ComponentConfiguration configuredPojo = configurator.detectConfiguration(config, "configuredComponent"); + Assert.assertThat(configuredPojo.getOutClasses(), is(notNullValue())); + Assert.assertThat(configuredPojo.getOutClasses().size(), is(greaterThan(0))); + Assert.assertThat(configuredPojo.getOutClasses().get(0), equalTo("java.lang.Float")); - assert(configuredPojo != null); + } - Assert.assertEquals(configuredPojo,testPojo); - } } \ No newline at end of file diff --git a/streams-config/src/test/resources/canonicalClassName.conf b/streams-config/src/test/resources/canonicalClassName.conf new file mode 100644 index 0000000..4cbcd34 --- /dev/null +++ b/streams-config/src/test/resources/canonicalClassName.conf @@ -0,0 +1,8 @@ +org.apache.streams.config.ComponentConfiguration = { + inClasses = [ + "java.lang.Object" + ] + outClasses = [ + "java.lang.Object" + ] +} \ No newline at end of file diff --git a/streams-config/src/test/resources/classHierarchy.conf b/streams-config/src/test/resources/classHierarchy.conf new file mode 100644 index 0000000..d95176a --- /dev/null +++ b/streams-config/src/test/resources/classHierarchy.conf @@ -0,0 +1,21 @@ +org.apache.streams.config.ComponentConfiguration = { + inClasses = [ + "java.lang.Object" + ] + outClasses = [ + "java.lang.Object" + ] +} +org.apache.streams.config.test.ComponentConfigurationForTestingNumberOne = { + inClasses = [ + "java.lang.Integer" + ] + outClasses = [ + "java.lang.Integer" + ] +} +org.apache.streams.config.test.ComponentConfigurationForTestingNumberTwo = { + outClasses = [ + "java.lang.Float" + ] +} diff --git a/streams-config/src/test/resources/componentTest.conf b/streams-config/src/test/resources/componentTest.conf index 83b1155..ec864dc 100644 --- a/streams-config/src/test/resources/componentTest.conf +++ b/streams-config/src/test/resources/componentTest.conf @@ -13,14 +13,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -defaultComponent { - -} configuredComponent { - inClasses = [ - java.lang.String - ] - outClasses = [ - java.lang.String - ] + inClasses = [ "java.lang.Object" ] + outClasses = [ "java.lang.Object" ] } diff --git a/streams-config/src/test/resources/packageHierarchy.conf b/streams-config/src/test/resources/packageHierarchy.conf new file mode 100644 index 0000000..a765833 --- /dev/null +++ b/streams-config/src/test/resources/packageHierarchy.conf @@ -0,0 +1,21 @@ +org.apache = { + inClasses = [ + "java.lang.Object" + ] + outClasses = [ + "java.lang.Object" + ] +} +org.apache.streams = { + inClasses = [ + "java.lang.Integer" + ] + outClasses = [ + "java.lang.Integer" + ] +} +org.apache.streams.config = { + outClasses = [ + "java.lang.Float" + ] +} diff --git a/streams-config/src/test/resources/packagePath.conf b/streams-config/src/test/resources/packagePath.conf new file mode 100644 index 0000000..ab04f46 --- /dev/null +++ b/streams-config/src/test/resources/packagePath.conf @@ -0,0 +1,8 @@ +org.apache.streams.config = { + inClasses = [ + "java.lang.String" + ] + outClasses = [ + "java.lang.String" + ] +} \ No newline at end of file diff --git a/streams-config/src/test/resources/simpleClassName.conf b/streams-config/src/test/resources/simpleClassName.conf new file mode 100644 index 0000000..619bdca --- /dev/null +++ b/streams-config/src/test/resources/simpleClassName.conf @@ -0,0 +1,8 @@ +ComponentConfiguration = { + inClasses = [ + "java.lang.Object" + ] + outClasses = [ + "java.lang.Object" + ] +} \ No newline at end of file -- To stop receiving notification emails like this one, please contact jfra...@apache.org.