From 0464ffbd42243351342de148d1ecb8da02ca8583 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Thu, 8 Feb 2018 16:31:16 +0100 Subject: [PATCH] feat(#159): Creates SPI to resolve placeholders in configuration files --- config/impl-base/pom.xml | 24 +- ...spathConfigurationPlaceholderResolver.java | 20 ++ .../impl/extension/ConfigExtension.java | 4 + .../extension/ConfigurationRegistrar.java | 30 +- .../ConfigurationSysPropResolver.java | 19 +- .../extension/StringPropertyReplacer.java | 261 +++++++++++------- ...DescriptorClasspathPropertiesTestCase.java | 73 +++++ ...rquillianDescriptorPropertiesTestCase.java | 34 +-- .../ClasspathReplacementInArqXmlTestCase.java | 119 ++++++++ .../ConfigurationRegistrarTestCase.java | 35 +++ .../SyspropReplacementInArqXmlTestCase.java | 43 +++ .../resources/arquillian_classpathprop.xml | 9 + config/spi/pom.xml | 8 + .../spi/ConfigurationPlaceholderResolver.java | 16 ++ .../core/test/AbstractManagerTestBase.java | 6 +- 15 files changed, 574 insertions(+), 127 deletions(-) create mode 100644 config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ClasspathConfigurationPlaceholderResolver.java create mode 100644 config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorClasspathPropertiesTestCase.java create mode 100644 config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ClasspathReplacementInArqXmlTestCase.java create mode 100644 config/impl-base/src/test/resources/arquillian_classpathprop.xml create mode 100644 config/spi/src/main/java/org/jboss/arquillian/config/spi/ConfigurationPlaceholderResolver.java diff --git a/config/impl-base/pom.xml b/config/impl-base/pom.xml index e6683eff1..7f1d50634 100644 --- a/config/impl-base/pom.xml +++ b/config/impl-base/pom.xml @@ -35,6 +35,11 @@ arquillian-config-api ${project.version} + + org.jboss.arquillian.config + arquillian-config-spi + ${project.version} + org.jboss.arquillian.core arquillian-core-api @@ -65,12 +70,29 @@ ${project.version} test + + org.jboss.arquillian.test + arquillian-test-spi + ${project.version} + test + + + org.jboss.arquillian.test + arquillian-test-impl-base + ${project.version} + test + junit junit test + + org.mockito + mockito-all + test + - \ No newline at end of file + diff --git a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ClasspathConfigurationPlaceholderResolver.java b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ClasspathConfigurationPlaceholderResolver.java new file mode 100644 index 000000000..11731d642 --- /dev/null +++ b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ClasspathConfigurationPlaceholderResolver.java @@ -0,0 +1,20 @@ +package org.jboss.arquillian.config.impl.extension; + +import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; + +class ClasspathConfigurationPlaceholderResolver implements ConfigurationPlaceholderResolver { + + public ArquillianDescriptor resolve(ArquillianDescriptor descriptor) { + + final String descriptorAsString = descriptor.exportAsString(); + return Descriptors.importAs(ArquillianDescriptor.class) + .fromString(StringPropertyReplacer.replaceClasspath(descriptorAsString)); + + } + + public int precedence() { + return 0; + } +} diff --git a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigExtension.java b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigExtension.java index d918110cc..ccc88974a 100644 --- a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigExtension.java +++ b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigExtension.java @@ -17,6 +17,7 @@ */ package org.jboss.arquillian.config.impl.extension; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; import org.jboss.arquillian.core.spi.LoadableExtension; /** @@ -29,5 +30,8 @@ public class ConfigExtension implements LoadableExtension { @Override public void register(ExtensionBuilder builder) { builder.observer(ConfigurationRegistrar.class); + + builder.service(ConfigurationPlaceholderResolver.class, ConfigurationSysPropResolver.class); + builder.service(ConfigurationPlaceholderResolver.class, ClasspathConfigurationPlaceholderResolver.class); } } diff --git a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrar.java b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrar.java index d364d986b..db212404a 100644 --- a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrar.java +++ b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrar.java @@ -16,20 +16,26 @@ */ package org.jboss.arquillian.config.impl.extension; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; +import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.InstanceProducer; import org.jboss.arquillian.core.api.annotation.ApplicationScoped; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.core.api.annotation.Observes; import org.jboss.arquillian.core.api.event.ManagerStarted; +import org.jboss.arquillian.core.spi.ServiceLoader; import org.jboss.shrinkwrap.descriptor.api.Descriptors; import java.io.InputStream; import java.util.Map; import java.util.Properties; -import static org.jboss.arquillian.config.impl.extension.ConfigurationSysPropResolver.resolveSystemProperties; - /** * Configurator * @@ -49,6 +55,9 @@ public class ConfigurationRegistrar { @ApplicationScoped private InstanceProducer descriptorInst; + @Inject + private Instance serviceLoaderInstance; + public void loadConfiguration(@Observes ManagerStarted event) { final InputStream input = FileUtils.loadArquillianXml(ARQUILLIAN_XML_PROPERTY, ARQUILLIAN_XML_DEFAULT); @@ -67,7 +76,22 @@ public void loadConfiguration(@Observes ManagerStarted event) { propertiesParser.addProperties(descriptor, envProperties); //Placeholder resolver - final ArquillianDescriptor resolvedDesc = resolveSystemProperties(descriptor); + ArquillianDescriptor resolvedDesc = descriptor; + + final List configurationPlaceholderResolvers = + new ArrayList(serviceLoaderInstance.get().all(ConfigurationPlaceholderResolver.class)); + + Collections.sort(configurationPlaceholderResolvers, new Comparator() { + public int compare(ConfigurationPlaceholderResolver o1, ConfigurationPlaceholderResolver o2) { + Integer a = o1.precedence(); + Integer b = o2.precedence(); + return b.compareTo(a); + } + }); + + for (ConfigurationPlaceholderResolver configurationPlaceholderResolver : configurationPlaceholderResolvers) { + resolvedDesc = configurationPlaceholderResolver.resolve(resolvedDesc); + } descriptorInst.set(resolvedDesc); } diff --git a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationSysPropResolver.java b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationSysPropResolver.java index b3c33579d..05375277a 100644 --- a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationSysPropResolver.java +++ b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/ConfigurationSysPropResolver.java @@ -17,6 +17,7 @@ package org.jboss.arquillian.config.impl.extension; import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; import org.jboss.shrinkwrap.descriptor.api.Descriptors; /** @@ -30,13 +31,7 @@ * * @author Andrew Lee Rubinger */ -class ConfigurationSysPropResolver { - /** - * Internal ctor, not to be called - */ - private ConfigurationSysPropResolver() { - throw new UnsupportedOperationException("No instances permitted"); - } +class ConfigurationSysPropResolver implements ConfigurationPlaceholderResolver { /** * Returns a new instance of {@link ArquillianDescriptor} by resolving any @@ -48,10 +43,18 @@ private ConfigurationSysPropResolver() { * * @throws IllegalArgumentException */ - static ArquillianDescriptor resolveSystemProperties(final ArquillianDescriptor descriptor) + private ArquillianDescriptor resolveSystemProperties(final ArquillianDescriptor descriptor) throws IllegalArgumentException { final String descriptorAsString = descriptor.exportAsString(); return Descriptors.importAs(ArquillianDescriptor.class) .fromString(StringPropertyReplacer.replaceProperties(descriptorAsString)); } + + public ArquillianDescriptor resolve(ArquillianDescriptor arquillianDescriptor) { + return resolveSystemProperties(arquillianDescriptor); + } + + public int precedence() { + return 0; + } } diff --git a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/StringPropertyReplacer.java b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/StringPropertyReplacer.java index b987bd88b..76fd0e455 100644 --- a/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/StringPropertyReplacer.java +++ b/config/impl-base/src/main/java/org/jboss/arquillian/config/impl/extension/StringPropertyReplacer.java @@ -22,6 +22,7 @@ package org.jboss.arquillian.config.impl.extension; import java.io.File; +import java.net.URL; import java.util.Map; import java.util.Properties; @@ -38,25 +39,6 @@ * @version $Revision: 2898 $ */ public final class StringPropertyReplacer { - /** - * File separator value - */ - private static final String FILE_SEPARATOR = File.separator; - - /** - * Path separator value - */ - private static final String PATH_SEPARATOR = File.pathSeparator; - - /** - * File separator alias - */ - private static final String FILE_SEPARATOR_ALIAS = "/"; - - /** - * Path separator alias - */ - private static final String PATH_SEPARATOR_ALIAS = ":"; /** * Environment variable base property @@ -68,6 +50,21 @@ public final class StringPropertyReplacer { private static final int SEEN_DOLLAR = 1; private static final int IN_BRACKET = 2; + /** + * Go through the input string and replace any occurrence of ${classpath(p)} with + * the classpath URL value. If there is no such p defined in classpath, + * then the ${p} reference will remain unchanged. + * + * @param string + * - the string with possible ${classpath()} references + * + * @return the input string with all property references replaced if any. + * If there are no valid references the input string will be returned. + */ + public static String replaceClasspath(String string) { + return replaceProperties(string, new ClasspathPropertyResolver()); + } + /** * Go through the input string and replace any occurrence of ${p} with * the System.getProtocolProperty(p) value. If there is no such property p defined, @@ -104,7 +101,7 @@ public static String replaceProperties(final String string) { props.setProperty(propKey, var.getValue()); } } - return replaceProperties(string, props); + return replaceProperties(string, new PropertiesPropertyResolver(props)); } /** @@ -124,13 +121,13 @@ public static String replaceProperties(final String string) { * * @param string * - the string with possible ${} references - * @param props - * - the source for ${x} property ref values, null means use System.getProtocolProperty() + * @param propertyResolver + * - property resolver to get the value from detected key * * @return the input string with all property references replaced if any. * If there are no valid references the input string will be returned. */ - public static String replaceProperties(final String string, final Properties props) { + private static String replaceProperties(final String string, PropertyResolver propertyResolver) { final char[] chars = string.toCharArray(); StringBuffer buffer = new StringBuffer(); boolean properties = false; @@ -167,45 +164,7 @@ else if (c == '}' && state == IN_BRACKET) { String key = string.substring(start + 2, i); - // check for alias - if (FILE_SEPARATOR_ALIAS.equals(key)) { - value = FILE_SEPARATOR; - } else if (PATH_SEPARATOR_ALIAS.equals(key)) { - value = PATH_SEPARATOR; - } else { - // check from the properties - if (props != null) { - value = props.getProperty(key); - } else { - value = System.getProperty(key); - } - - if (value == null) { - // Check for a default value ${key:default} - int colon = key.indexOf(':'); - if (colon > 0) { - String realKey = key.substring(0, colon); - if (props != null) { - value = props.getProperty(realKey); - } else { - value = System.getProperty(realKey); - } - - if (value == null) { - // Check for a composite key, "key1,key2" - value = resolveCompositeKey(realKey, props); - - // Not a composite key either, use the specified default - if (value == null) { - value = key.substring(colon + 1); - } - } - } else { - // No default, check for a composite key, "key1,key2" - value = resolveCompositeKey(key, props); - } - } - } + value = propertyResolver.getValue(key); if (value != null) { properties = true; @@ -235,47 +194,155 @@ else if (c == '}' && state == IN_BRACKET) { return buffer.toString(); } - /** - * Try to resolve a "key" from the provided properties by - * checking if it is actually a "key1,key2", in which case - * try first "key1", then "key2". If all fails, return null. - *

- * It also accepts "key1," and ",key2". - * - * @param key - * the key to resolve - * @param props - * the properties to use - * - * @return the resolved key or null - */ - private static String resolveCompositeKey(String key, Properties props) { - String value = null; - - // Look for the comma - int comma = key.indexOf(','); - if (comma > -1) { - // If we have a first part, try resolve it - if (comma > 0) { - // Check the first part - String key1 = key.substring(0, comma); - if (props != null) { - value = props.getProperty(key1); - } else { - value = System.getProperty(key1); + + public interface PropertyResolver { + String getValue(String key); + } + + static class ClasspathPropertyResolver implements PropertyResolver { + + /** + * Classpath base property + */ + private static final String CLASSPATH = "classpath("; + + public String getValue(String key) { + + if (key.startsWith(CLASSPATH)) { + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + String classpathResource = key.substring(CLASSPATH.length(), key.length() - 1); + final URL resource = contextClassLoader.getResource(classpathResource); + + //If resource is not found it is returned as null so no change is applicable. + if (resource == null) { + return null; } + + return resource.toString(); } - // Check the second part, if there is one and first lookup failed - if (value == null && comma < key.length() - 1) { - String key2 = key.substring(comma + 1); + + return null; + } + } + + static class PropertiesPropertyResolver implements PropertyResolver { + + /** + * File separator value + */ + private static final String FILE_SEPARATOR = File.separator; + + /** + * Path separator value + */ + private static final String PATH_SEPARATOR = File.pathSeparator; + + /** + * File separator alias + */ + private static final String FILE_SEPARATOR_ALIAS = "/"; + + /** + * Path separator alias + */ + private static final String PATH_SEPARATOR_ALIAS = ":"; + + + private final Properties props; + + PropertiesPropertyResolver(Properties props) { + this.props = props; + } + + public String getValue(String key) { + + String value; + + if (FILE_SEPARATOR_ALIAS.equals(key)) { + value = FILE_SEPARATOR; + } else if (PATH_SEPARATOR_ALIAS.equals(key)) { + value = PATH_SEPARATOR; + } else { + // check from the properties if (props != null) { - value = props.getProperty(key2); + value = props.getProperty(key); } else { - value = System.getProperty(key2); + value = System.getProperty(key); + } + + if (value == null) { + // Check for a default value ${key:default} + int colon = key.indexOf(':'); + if (colon > 0) { + String realKey = key.substring(0, colon); + if (props != null) { + value = props.getProperty(realKey); + } else { + value = System.getProperty(realKey); + } + + if (value == null) { + // Check for a composite key, "key1,key2" + value = resolveCompositeKey(realKey, props); + + // Not a composite key either, use the specified default + if (value == null) { + value = key.substring(colon + 1); + } + } + } else { + // No default, check for a composite key, "key1,key2" + value = resolveCompositeKey(key, props); + } } } + + return value; + } + + /** + * Try to resolve a "key" from the provided properties by + * checking if it is actually a "key1,key2", in which case + * try first "key1", then "key2". If all fails, return null. + *

+ * It also accepts "key1," and ",key2". + * + * @param key + * the key to resolve + * @param props + * the properties to use + * + * @return the resolved key or null + */ + private String resolveCompositeKey(String key, Properties props) { + String value = null; + + // Look for the comma + int comma = key.indexOf(','); + if (comma > -1) { + // If we have a first part, try resolve it + if (comma > 0) { + // Check the first part + String key1 = key.substring(0, comma); + if (props != null) { + value = props.getProperty(key1); + } else { + value = System.getProperty(key1); + } + } + // Check the second part, if there is one and first lookup failed + if (value == null && comma < key.length() - 1) { + String key2 = key.substring(comma + 1); + if (props != null) { + value = props.getProperty(key2); + } else { + value = System.getProperty(key2); + } + } + } + // Return whatever we've found or null + return value; } - // Return whatever we've found or null - return value; } + } diff --git a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorClasspathPropertiesTestCase.java b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorClasspathPropertiesTestCase.java new file mode 100644 index 000000000..c42cfea99 --- /dev/null +++ b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorClasspathPropertiesTestCase.java @@ -0,0 +1,73 @@ +package org.jboss.arquillian.config.impl.extension; + +import java.net.URL; +import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.junit.Test; + +import static org.jboss.arquillian.config.descriptor.impl.AssertXPath.assertXPath; + +public class ArquillianDescriptorClasspathPropertiesTestCase { + + + @Test + public void should_replace_properties_with_classpath_location() throws Exception { + // given + String element = "arquillian_sysprop.xml"; + ArquillianDescriptor desc = create() + .container("daemon") + .property("javaVmArguments", "-Djavax.net.ssl.trustStore=${classpath(" + element + ")}"); + final ClasspathConfigurationPlaceholderResolver classpathConfigurationPlaceholderResolver = new ClasspathConfigurationPlaceholderResolver(); + + // when + desc = classpathConfigurationPlaceholderResolver.resolve(desc); + + // then + final String descString = desc.exportAsString(); + final URL resource = Thread.currentThread().getContextClassLoader().getResource(element); + assertXPath(descString, "/arquillian/container/configuration/property", + String.format("-Djavax.net.ssl.trustStore=%s", resource.toString())); + + } + + @Test + public void should_not_replace_placeholder_if_classpath_resource_not_found() throws Exception { + String element = "notfound.txt"; + ArquillianDescriptor desc = create() + .container("daemon") + .property("javaVmArguments", "-Djavax.net.ssl.trustStore=${classpath(" + element + ")}"); + final ClasspathConfigurationPlaceholderResolver classpathConfigurationPlaceholderResolver = new ClasspathConfigurationPlaceholderResolver(); + + // when + desc = classpathConfigurationPlaceholderResolver.resolve(desc); + + // then + final String descString = desc.exportAsString(); + assertXPath(descString, "/arquillian/container/configuration/property", + String.format("-Djavax.net.ssl.trustStore=${classpath(%s)}", element)); + } + + @Test + public void should_not_replace_other_placeholders() throws Exception { + + ArquillianDescriptor desc = create() + .container("daemon") + .property("javaVmArguments", "-Djavax.net.ssl.trustStore=${env.LAUNCHER_TESTS_TRUSTSTORE_PATH}"); + final ClasspathConfigurationPlaceholderResolver classpathConfigurationPlaceholderResolver = new ClasspathConfigurationPlaceholderResolver(); + + // when + desc = classpathConfigurationPlaceholderResolver.resolve(desc); + + // then + final String descString = desc.exportAsString(); + assertXPath(descString, "/arquillian/container/configuration/property", + "-Djavax.net.ssl.trustStore=${env.LAUNCHER_TESTS_TRUSTSTORE_PATH}"); + + } + + + private ArquillianDescriptor create() { + return Descriptors.create(ArquillianDescriptor.class); + } + +} diff --git a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorPropertiesTestCase.java b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorPropertiesTestCase.java index f17c20823..601b69397 100644 --- a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorPropertiesTestCase.java +++ b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ArquillianDescriptorPropertiesTestCase.java @@ -100,7 +100,7 @@ public void shouldBeAbleToSetEngineProperties() throws Exception { .deploymentExportPath(setPropKey(KEY_PROPERTY_VALUE_1)) .maxTestClassesBeforeRestart(PROPERTY_INT_VALUE_1) .maxTestClassesBeforeRestart(PROPERTY_INT_VALUE_1); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/engine/property[@name='deploymentExportPath']/text()", PROPERTY_VALUE_1); @@ -118,7 +118,7 @@ public void shouldReturnNullOnEnginePropertiesIfNotSet() throws Exception { // add multiple times to see only one property added desc = create() .engine(); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); ArquillianDescriptor descriptor = create(descString); @@ -135,7 +135,7 @@ public void shouldBeAbleToAddContainer() throws Exception { desc = create() .container(setPropKey(KEY_CONTAINER_NAME_1)).setDefault() .container(setPropKey(KEY_CONTAINER_NAME_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container/@qualifier", CONTAINER_NAME_1, CONTAINER_NAME_2); @@ -156,7 +156,7 @@ public void shouldBeAbleToAddContainerAndOverwrite() throws Exception { desc = create() .container(CONTAINER_NAME_1).setDefault() .container(CONTAINER_NAME_1).setContainerName(setPropKey(KEY_CONTAINER_NAME_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container/@qualifier", CONTAINER_NAME_2); @@ -175,7 +175,7 @@ public void shouldBeAbleToAddDefaultProtocol() throws Exception { desc = create() .defaultProtocol(PROTOCOL_TYPE_1) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_1)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/defaultProtocol/@type", PROTOCOL_TYPE_1); @@ -197,7 +197,7 @@ public void shouldBeAbleToAddDefaultProtocolAndOverwriteProperty() throws Except .defaultProtocol(PROTOCOL_TYPE_1) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_1)) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/defaultProtocol/@type", PROTOCOL_TYPE_1); @@ -221,7 +221,7 @@ public void shouldBeAbleToAddContainerWithDependencies() throws Exception { .container(CONTAINER_NAME_1) .dependency(setPropKey(KEY_DEPENDENCY_1)) .dependency(setPropKey(KEY_DEPENDENCY_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container/dependencies/dependency", DEPENDENCY_1, DEPENDENCY_2); @@ -243,7 +243,7 @@ public void shouldBeAbleToAddContainerWithDependenciesAndOverwrite() throws Exce .container(CONTAINER_NAME_1) .dependency(setPropKey(KEY_DEPENDENCY_1)) .dependency(setPropKey(KEY_DEPENDENCY_1)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container/dependencies/dependency", DEPENDENCY_1); @@ -266,7 +266,7 @@ public void shouldBeAbleToAddContainerWithMultipleProtocols() throws Exception { .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_1)) .protocol(PROTOCOL_TYPE_2) .property(PROPERTY_NAME_2, setPropKey(KEY_PROPERTY_VALUE_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container/@qualifier", CONTAINER_NAME_1); @@ -302,7 +302,7 @@ public void shouldBeAbleToAddContainerAndOverwriteProtocol() throws Exception { .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_1)) .protocol(PROTOCOL_TYPE_1) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container/@qualifier", CONTAINER_NAME_1); @@ -331,7 +331,7 @@ public void shouldBeAbleToAddContainerWithConfiguration() throws Exception { .container(CONTAINER_NAME_2) .property(PROPERTY_NAME_2, setPropKey(KEY_PROPERTY_VALUE_2)) .property(PROPERTY_NAME_4, setPropKey(KEY_ENV_NAME_1)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container[1]/@qualifier", CONTAINER_NAME_1); @@ -366,7 +366,7 @@ public void shouldBeAbleToAddContainerWithConfigurationAndOverwriteProperty() th .container(CONTAINER_NAME_1) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_1)) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/container[1]/@qualifier", CONTAINER_NAME_1); @@ -395,7 +395,7 @@ public void shouldBeAbleToAddGroupWithContainer() throws Exception { .container(setPropKey(KEY_CONTAINER_NAME_2)) .group(GROUP_NAME_2) .container(setPropKey(KEY_CONTAINER_NAME_3)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/group/@qualifier", GROUP_NAME_1, GROUP_NAME_2); @@ -425,7 +425,7 @@ public void shouldBeAbleToAddGroupWithContainerAndOverwriteContainer() throws Ex .group(GROUP_NAME_1) .container(setPropKey(KEY_CONTAINER_NAME_1)) .container(setPropKey(KEY_CONTAINER_NAME_1)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/group/@qualifier", GROUP_NAME_1); @@ -450,7 +450,7 @@ public void shouldBeAbleToAddExtension() throws Exception { .property(PROPERTY_NAME_2, setPropKey(KEY_PROPERTY_VALUE_2)) .extension(EXTENSION_NAME_2) .property(PROPERTY_NAME_3, setPropKey(KEY_PROPERTY_VALUE_3)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/extension/@qualifier", EXTENSION_NAME_1, EXTENSION_NAME_2); @@ -484,7 +484,7 @@ public void shouldBeAbleToRenameExtension() throws Exception { .property(PROPERTY_NAME_1, PROPERTY_VALUE_1) .extension(EXTENSION_NAME_1) .setExtensionName(EXTENSION_NAME_2); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/extension/@qualifier", EXTENSION_NAME_2); @@ -506,7 +506,7 @@ public void shouldBeAbleToAddExtensionAndOverwriteProperty() throws Exception { .extension(EXTENSION_NAME_1) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_1)) .property(PROPERTY_NAME_1, setPropKey(KEY_PROPERTY_VALUE_2)); - desc = ConfigurationSysPropResolver.resolveSystemProperties(desc); + desc = new ConfigurationSysPropResolver().resolve(desc); final String descString = desc.exportAsString(); assertXPath(descString, "/arquillian/extension/@qualifier", EXTENSION_NAME_1); diff --git a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ClasspathReplacementInArqXmlTestCase.java b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ClasspathReplacementInArqXmlTestCase.java new file mode 100644 index 000000000..1e240c9ab --- /dev/null +++ b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ClasspathReplacementInArqXmlTestCase.java @@ -0,0 +1,119 @@ +package org.jboss.arquillian.config.impl.extension; + +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; +import org.jboss.arquillian.config.descriptor.impl.AssertXPath; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; +import org.jboss.arquillian.core.api.Instance; +import org.jboss.arquillian.core.api.annotation.Inject; +import org.jboss.arquillian.core.spi.Manager; +import org.jboss.arquillian.core.spi.ServiceLoader; +import org.jboss.arquillian.core.spi.context.Context; +import org.jboss.arquillian.core.test.AbstractManagerTestBase; +import org.jboss.arquillian.test.impl.context.SuiteContextImpl; +import org.jboss.arquillian.test.spi.annotation.SuiteScoped; +import org.jboss.arquillian.test.spi.context.SuiteContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.jboss.arquillian.config.descriptor.impl.AssertXPath.assertXPath; + +@RunWith(MockitoJUnitRunner.class) +public class ClasspathReplacementInArqXmlTestCase extends AbstractManagerTestBase { + + @Mock + private ServiceLoader serviceLoader; + + /** + * Name of the arquillian.xml to test + */ + private static final String NAME_ARQ_XML = "arquillian_classpathprop.xml"; + + /** + * Name of the system property for EL expressions + */ + private static final String SYSPROP_ARQ_CONTAINER = "arquillian.container"; + + private static final String VALUE_EL_OVERRIDE = "ALR"; + + /** + * The loaded arquillian.xml + */ + @Inject + private Instance desc; + + /** + * Sets the name of the arquillian.xml under test + */ + @BeforeClass + public static void setSysprops() { + // Set a sysprop to denote the name of the arquillian.xml under test + System.setProperty(ConfigurationRegistrar.ARQUILLIAN_XML_PROPERTY, NAME_ARQ_XML); + + // Set a sysprop to override the name of the qualifier + System.setProperty(SYSPROP_ARQ_CONTAINER, VALUE_EL_OVERRIDE); + } + + /** + * Clean up + */ + @AfterClass + public static void clearSysprops() { + System.clearProperty(ConfigurationRegistrar.ARQUILLIAN_XML_PROPERTY); + System.clearProperty(SYSPROP_ARQ_CONTAINER); + } + + @Test + public void should_replace_classpath_in_arquillian_xml() throws Exception { + + final String xml = desc.get().exportAsString(); + System.out.println(xml); + AssertXPath.assertXPath(xml, "/arquillian/container/@qualifier", VALUE_EL_OVERRIDE); + final URL resource = Thread.currentThread().getContextClassLoader().getResource("arquillian_sysprop.xml"); + assertXPath(xml, "/arquillian/container/configuration/property", + String.format("-Djavax.net.ssl.trustStore=%s", resource.toString())); + } + + @Override + protected void beforeStartManager(Manager manager) { + startContexts(manager); + final ConfigurationPlaceholderResolver configurationSysPropResolver = new ConfigurationSysPropResolver(); + final ConfigurationPlaceholderResolver classpathConfigurationPlaceholderResolver = new ClasspathConfigurationPlaceholderResolver(); + + Mockito.when(serviceLoader.all(ConfigurationPlaceholderResolver.class)) + .thenReturn(Arrays.asList(classpathConfigurationPlaceholderResolver, configurationSysPropResolver)); + + bind(SuiteScoped.class, ServiceLoader.class, serviceLoader); + } + + /** + * {@inheritDoc} + * + * @see org.jboss.arquillian.core.test.AbstractManagerTestBase#addExtensions(java.util.List) + */ + @Override + protected void addExtensions(final List> extensions) { + extensions.add(ConfigurationRegistrar.class); + super.addExtensions(extensions); + } + + @Override + protected void addContexts(List> contexts) { + super.addContexts(contexts); + contexts.add(SuiteContextImpl.class); + } + + @Override + protected void startContexts(Manager manager) { + super.startContexts(manager); + manager.getContext(SuiteContext.class).activate(); + } + +} diff --git a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrarTestCase.java b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrarTestCase.java index 51f50888c..7a1ff7237 100644 --- a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrarTestCase.java +++ b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/ConfigurationRegistrarTestCase.java @@ -17,18 +17,31 @@ */ package org.jboss.arquillian.config.impl.extension; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; import org.jboss.arquillian.core.api.Injector; import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.core.api.event.ManagerStarted; +import org.jboss.arquillian.core.spi.Manager; +import org.jboss.arquillian.core.spi.ServiceLoader; +import org.jboss.arquillian.core.spi.context.Context; import org.jboss.arquillian.core.test.AbstractManagerTestBase; +import org.jboss.arquillian.test.impl.context.SuiteContextImpl; +import org.jboss.arquillian.test.spi.annotation.SuiteScoped; +import org.jboss.arquillian.test.spi.context.SuiteContext; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; /** * ConfigurationRegistrarTestCase @@ -36,7 +49,12 @@ * @author Aslak Knutsen * @version $Revision: $ */ +@RunWith(MockitoJUnitRunner.class) public class ConfigurationRegistrarTestCase extends AbstractManagerTestBase { + + @Mock + private ServiceLoader serviceLoader; + @Inject private Instance injectorInst; @@ -56,6 +74,11 @@ static void validate(String property, String value, AssertCallback callback) { @Before public void injectConfigurationRegistrar() { + ConfigurationPlaceholderResolver configurationSysPropResolver = new ConfigurationSysPropResolver(); + + Mockito.when(serviceLoader.all(ConfigurationPlaceholderResolver.class)) + .thenReturn(Arrays.asList(configurationSysPropResolver)); + bind(SuiteScoped.class, ServiceLoader.class, serviceLoader); registrar = injectorInst.get().inject(new ConfigurationRegistrar()); } @@ -460,6 +483,18 @@ public void validate() { }); } + @Override + protected void addContexts(List> contexts) { + super.addContexts(contexts); + contexts.add(SuiteContextImpl.class); + } + + @Override + protected void startContexts(Manager manager) { + super.startContexts(manager); + manager.getContext(SuiteContext.class).activate(); + } + public interface AssertCallback { void validate(); } diff --git a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/SyspropReplacementInArqXmlTestCase.java b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/SyspropReplacementInArqXmlTestCase.java index b84576598..246d223ed 100644 --- a/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/SyspropReplacementInArqXmlTestCase.java +++ b/config/impl-base/src/test/java/org/jboss/arquillian/config/impl/extension/SyspropReplacementInArqXmlTestCase.java @@ -16,15 +16,29 @@ */ package org.jboss.arquillian.config.impl.extension; +import java.util.Arrays; import java.util.List; import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; import org.jboss.arquillian.config.descriptor.impl.AssertXPath; +import org.jboss.arquillian.config.spi.ConfigurationPlaceholderResolver; import org.jboss.arquillian.core.api.Instance; import org.jboss.arquillian.core.api.annotation.Inject; +import org.jboss.arquillian.core.spi.Manager; +import org.jboss.arquillian.core.spi.ServiceLoader; +import org.jboss.arquillian.core.spi.context.Context; import org.jboss.arquillian.core.test.AbstractManagerTestBase; +import org.jboss.arquillian.test.impl.context.SuiteContextImpl; +import org.jboss.arquillian.test.spi.annotation.SuiteScoped; +import org.jboss.arquillian.test.spi.context.ClassContext; +import org.jboss.arquillian.test.spi.context.SuiteContext; +import org.jboss.arquillian.test.spi.context.TestContext; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; /** * Test Cases to ensure that the arquillian.xml loaded by @@ -35,8 +49,12 @@ * * @author Andrew Lee Rubinger */ +@RunWith(MockitoJUnitRunner.class) public class SyspropReplacementInArqXmlTestCase extends AbstractManagerTestBase { + @Mock + private ServiceLoader serviceLoader; + /** * Name of the arquillian.xml to test */ @@ -82,11 +100,24 @@ public static void clearSysprops() { */ @Test public void syspropReplacementInArqXml() throws Exception { + final String xml = desc.get().exportAsString(); System.out.println(xml); AssertXPath.assertXPath(xml, "/arquillian/container/@qualifier", VALUE_EL_OVERRIDE); } + @Override + protected void beforeStartManager(Manager manager) { + startContexts(manager); + final ConfigurationPlaceholderResolver configurationSysPropResolver = new ConfigurationSysPropResolver(); + final ConfigurationPlaceholderResolver classpathConfigurationPlaceholderResolver = new ClasspathConfigurationPlaceholderResolver(); + + Mockito.when(serviceLoader.all(ConfigurationPlaceholderResolver.class)) + .thenReturn(Arrays.asList(configurationSysPropResolver, classpathConfigurationPlaceholderResolver)); + + bind(SuiteScoped.class, ServiceLoader.class, serviceLoader); + } + /** * {@inheritDoc} * @@ -97,4 +128,16 @@ protected void addExtensions(final List> extensions) { extensions.add(ConfigurationRegistrar.class); super.addExtensions(extensions); } + + @Override + protected void addContexts(List> contexts) { + super.addContexts(contexts); + contexts.add(SuiteContextImpl.class); + } + + @Override + protected void startContexts(Manager manager) { + super.startContexts(manager); + manager.getContext(SuiteContext.class).activate(); + } } diff --git a/config/impl-base/src/test/resources/arquillian_classpathprop.xml b/config/impl-base/src/test/resources/arquillian_classpathprop.xml new file mode 100644 index 000000000..faff6a371 --- /dev/null +++ b/config/impl-base/src/test/resources/arquillian_classpathprop.xml @@ -0,0 +1,9 @@ + + + + + -Djavax.net.ssl.trustStore=${classpath(arquillian_sysprop.xml)} + + + diff --git a/config/spi/pom.xml b/config/spi/pom.xml index 1a0a59e28..e629e569a 100644 --- a/config/spi/pom.xml +++ b/config/spi/pom.xml @@ -18,5 +18,13 @@ Arquillian Config SPI SPI for the Config Module. + + + org.jboss.arquillian.config + arquillian-config-api + ${project.version} + + + diff --git a/config/spi/src/main/java/org/jboss/arquillian/config/spi/ConfigurationPlaceholderResolver.java b/config/spi/src/main/java/org/jboss/arquillian/config/spi/ConfigurationPlaceholderResolver.java new file mode 100644 index 000000000..9cd608a06 --- /dev/null +++ b/config/spi/src/main/java/org/jboss/arquillian/config/spi/ConfigurationPlaceholderResolver.java @@ -0,0 +1,16 @@ +package org.jboss.arquillian.config.spi; + +import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; + +public interface ConfigurationPlaceholderResolver { + + ArquillianDescriptor resolve(ArquillianDescriptor arquillianDescriptor); + + /** + * In case then more of one placeholder resolver, they are ordered as they appear on classpath. + * If you need to reorder them, you can use {@code precedence} value. The higher the {@code precedence} is, + * the sooner the observer is executed. + * @return + */ + int precedence(); +} diff --git a/core/impl-base/src/test/java/org/jboss/arquillian/core/test/AbstractManagerTestBase.java b/core/impl-base/src/test/java/org/jboss/arquillian/core/test/AbstractManagerTestBase.java index 82de3b495..321086fb6 100644 --- a/core/impl-base/src/test/java/org/jboss/arquillian/core/test/AbstractManagerTestBase.java +++ b/core/impl-base/src/test/java/org/jboss/arquillian/core/test/AbstractManagerTestBase.java @@ -70,6 +70,7 @@ public final void create() throws Exception { } manager = (ManagerImpl) builder.create(); + beforeStartManager(manager); manager.start(); executeInApplicationScope(new Callable() { @@ -167,6 +168,9 @@ public EventRegister call() throws Exception { // Extendables ------------------------------------------------------------------------|| //-------------------------------------------------------------------------------------|| + protected void beforeStartManager(Manager manager) { + } + protected void addExtensions(List> extensions) { } @@ -314,4 +318,4 @@ public Boolean wasActive(Class context) { return false; } } -} \ No newline at end of file +}