This is an automated email from the ASF dual-hosted git repository. robbie pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/qpid-jms.git
The following commit(s) were added to refs/heads/master by this push: new 74e0427 QPIDJMS-448: support simple variable expansion for JNDI config 74e0427 is described below commit 74e0427f43049b357342d1a9e69b32027c59cf2a Author: Robbie Gemmell <rob...@apache.org> AuthorDate: Mon Apr 1 18:23:20 2019 +0100 QPIDJMS-448: support simple variable expansion for JNDI config --- qpid-jms-client/pom.xml | 10 + .../qpid/jms/jndi/JmsInitialContextFactory.java | 32 ++- .../apache/qpid/jms/util/VariableExpansion.java | 137 ++++++++++ .../jms/jndi/JmsInitialContextFactoryTest.java | 133 +++++++++ .../qpid/jms/util/VariableExpansionTest.java | 302 +++++++++++++++++++++ qpid-jms-docs/Configuration.md | 8 +- 6 files changed, 613 insertions(+), 9 deletions(-) diff --git a/qpid-jms-client/pom.xml b/qpid-jms-client/pom.xml index a952fc4..6875d71 100644 --- a/qpid-jms-client/pom.xml +++ b/qpid-jms-client/pom.xml @@ -128,6 +128,16 @@ </resources> <plugins> <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables combine.children="append"> + <!-- Env variable for use with VariableExpansionTest and JmsInitialContextFactoryTest --> + <VAR_EXPANSION_TEST_ENV_VAR>TestEnvVariableValue123</VAR_EXPANSION_TEST_ENV_VAR> + </environmentVariables> + </configuration> + </plugin> + <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <configuration> diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/jndi/JmsInitialContextFactory.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/jndi/JmsInitialContextFactory.java index 5588095..e55970c 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/jndi/JmsInitialContextFactory.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/jndi/JmsInitialContextFactory.java @@ -40,6 +40,7 @@ import javax.naming.spi.InitialContextFactory; import org.apache.qpid.jms.JmsConnectionFactory; import org.apache.qpid.jms.JmsQueue; import org.apache.qpid.jms.JmsTopic; +import org.apache.qpid.jms.util.VariableExpansion; public class JmsInitialContextFactory implements InitialContextFactory { @@ -184,7 +185,7 @@ public class JmsInitialContextFactory implements InitialContextFactory { value = String.valueOf(entry.getValue()); } - factories.put(factoryName, value); + factories.put(factoryName, expand(value, environment)); } } @@ -206,7 +207,8 @@ public class JmsInitialContextFactory implements InitialContextFactory { String key = String.valueOf(entry.getKey()); if (key.toLowerCase().startsWith(CONNECTION_FACTORY_DEFAULT_KEY_PREFIX)) { String jndiName = key.substring(CONNECTION_FACTORY_DEFAULT_KEY_PREFIX.length()); - map.put(jndiName, String.valueOf(entry.getValue())); + String value = String.valueOf(entry.getValue()); + map.put(jndiName, expand(value, environment)); } } @@ -224,7 +226,8 @@ public class JmsInitialContextFactory implements InitialContextFactory { if (key.toLowerCase().startsWith(CONNECTION_FACTORY_PROPERTY_KEY_PREFIX)) { if(key.substring(CONNECTION_FACTORY_PROPERTY_KEY_PREFIX.length()).startsWith(factoryNameSuffix)) { String propertyName = key.substring(CONNECTION_FACTORY_PROPERTY_KEY_PREFIX.length() + factoryNameSuffix.length()); - map.put(propertyName, String.valueOf(entry.getValue())); + String value = String.valueOf(entry.getValue()); + map.put(propertyName, expand(value, environment)); } } } @@ -238,7 +241,8 @@ public class JmsInitialContextFactory implements InitialContextFactory { String key = entry.getKey().toString(); if (key.startsWith(QUEUE_KEY_PREFIX)) { String jndiName = key.substring(QUEUE_KEY_PREFIX.length()); - bindings.put(jndiName, createQueue(entry.getValue().toString())); + String value = expand(entry.getValue().toString(), environment); + bindings.put(jndiName, createQueue(value)); } } } @@ -249,7 +253,8 @@ public class JmsInitialContextFactory implements InitialContextFactory { String key = entry.getKey().toString(); if (key.startsWith(TOPIC_KEY_PREFIX)) { String jndiName = key.substring(TOPIC_KEY_PREFIX.length()); - bindings.put(jndiName, createTopic(entry.getValue().toString())); + String value = expand(entry.getValue().toString(), environment); + bindings.put(jndiName, createTopic(value)); } } } @@ -284,4 +289,21 @@ public class JmsInitialContextFactory implements InitialContextFactory { return factory; } + + protected static String expand(String input, Map<Object, Object> environment) { + return VariableExpansion.expand(input, variable -> { + String resolve = VariableExpansion.SYS_PROP_RESOLVER.resolve(variable); + if (resolve == null) { + resolve = VariableExpansion.ENV_VAR_RESOLVER.resolve(variable); + if (resolve == null) { + Object o = environment.get(variable); + if (o != null) { + resolve = String.valueOf(o); + } + } + } + + return resolve; + }); + } } diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/util/VariableExpansion.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/util/VariableExpansion.java new file mode 100644 index 0000000..f5093c5 --- /dev/null +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/util/VariableExpansion.java @@ -0,0 +1,137 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jms.util; + +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class VariableExpansion { + + @FunctionalInterface + public interface Resolver { + String resolve(String name); + } + + private static final Pattern VARIABLE_OR_ESCAPE_PATTERN = Pattern.compile("(?:\\$\\{([^\\}]*)\\})|(?:\\$(\\$))"); + private static final String ESCAPE = "$"; + + private VariableExpansion() { + // No instances + } + + //================== Helper Resolvers ================== + + public static final Resolver ENV_VAR_RESOLVER = prop -> System.getenv(prop); + + public static final Resolver SYS_PROP_RESOLVER = prop -> System.getProperty(prop); + + public static final class MapResolver implements Resolver { + private final Map<String, String> map; + + public MapResolver(Map<String, String> map) { + this.map = map; + } + + public String resolve(String name) { + return map.get(name); + } + } + + //======================= Methods ====================== + + /** + * Expands any variables found in the given input string. + * + * @param input + * the string to expand any variables in + * @param resolver + * the resolver to use + * @return the expanded output + * + * @throws IllegalArgumentException + * if an argument can't be expanded, e.g because a variable is not resolvable. + * @throws NullPointerException + * if a resolver is not supplied + */ + public static final String expand(String input, Resolver resolver) throws IllegalArgumentException, NullPointerException { + if(resolver == null) { + throw new NullPointerException("Resolver must be supplied"); + } + + if (input == null) { + return null; + } + + return expand(input, resolver, new Stack<String>()); + } + + private static final String expand(String input, Resolver resolver, Stack<String> stack) { + Matcher matcher = VARIABLE_OR_ESCAPE_PATTERN.matcher(input); + + StringBuffer result = null; + while (matcher.find()) { + if(result == null) { + result = new StringBuffer(); + } + + String var = matcher.group(1); // Variable match + if (var != null) { + matcher.appendReplacement(result, Matcher.quoteReplacement(resolve(var, resolver, stack))); + } else { + String esc = matcher.group(2); // Escape matcher + if (ESCAPE.equals(esc)) { + matcher.appendReplacement(result, Matcher.quoteReplacement(ESCAPE)); + } else { + throw new IllegalArgumentException(esc); + } + } + } + + if(result == null) { + // No match found, return the original input + return input; + } + + matcher.appendTail(result); + + return result.toString(); + } + + private static final String resolve(String var, Resolver resolver, Stack<String> stack) { + if (stack.contains(var)) { + throw new IllegalArgumentException(String.format("Recursively defined variable '%s', stack=%s", var, stack)); + } + + String result = resolver.resolve(var); + if (result == null) { + throw new IllegalArgumentException("Unable to resolve variable: " + var); + } + + stack.push(var); + try { + return expand(result, resolver, stack); + } finally { + stack.pop(); + } + } +} \ No newline at end of file diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/jndi/JmsInitialContextFactoryTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/jndi/JmsInitialContextFactoryTest.java index 9400ad5..7d4a7ad 100644 --- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/jndi/JmsInitialContextFactoryTest.java +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/jndi/JmsInitialContextFactoryTest.java @@ -17,9 +17,11 @@ package org.apache.qpid.jms.jndi; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import java.io.File; import java.io.FileNotFoundException; @@ -43,6 +45,10 @@ import org.junit.Test; public class JmsInitialContextFactoryTest extends QpidJmsTestCase { + // Environment variable name+value for test, configured in Surefire config + private static final String TEST_ENV_VARIABLE_NAME = "VAR_EXPANSION_TEST_ENV_VAR"; + private static final String TEST_ENV_VARIABLE_VALUE = "TestEnvVariableValue123"; + private JmsInitialContextFactory factory; private Context context; @@ -450,4 +456,131 @@ public class JmsInitialContextFactoryTest extends QpidJmsTestCase { f.delete(); } } + + @Test + public void testVariableExpansionUnresolvableVariable() throws Exception { + //Check exception is thrown for variable that doesn't resolve + String factoryName = "myFactory"; + String unknownVariable = "unknownVariable"; + String uri = "amqp://${"+ unknownVariable +"}:1234"; + + Hashtable<Object, Object> env = new Hashtable<Object, Object>(); + env.put("connectionfactory." + factoryName, uri); + + try { + createInitialContext(env); + fail("Expected to fail due to unresolved variable"); + } catch (IllegalArgumentException iae) { + // Expected + } + + String nowKnownHostValue = "nowKnownValue"; + + //Now make the variable resolve, check the exact same env+URI now works + setTestSystemProperty(unknownVariable, nowKnownHostValue); + + Context ctx = createInitialContext(env); + + Object o = ctx.lookup("myFactory"); + + assertNotNull("No object returned", o); + assertEquals("Unexpected class type for returned object", JmsConnectionFactory.class, o.getClass()); + + assertEquals("Unexpected URI for returned factory", "amqp://" + nowKnownHostValue + ":1234", ((JmsConnectionFactory) o).getRemoteURI()); + } + + @Test + public void testVariableExpansionConnectionFactory() throws Exception { + doVariableExpansionConnectionFactoryTestImpl(false); + } + + @Test + public void testVariableExpansionConnectionFactoryWithEnvVar() throws Exception { + doVariableExpansionConnectionFactoryTestImpl(true); + } + + private void doVariableExpansionConnectionFactoryTestImpl(boolean useEnvVarForHost) throws NamingException { + String factoryName = "myFactory"; + + String hostVariableName = useEnvVarForHost ? TEST_ENV_VARIABLE_NAME : "myHostVar"; + String portVariableName = "myPortVar"; + String clientIdVariableName = "myClientIDVar"; + String hostVariableValue = useEnvVarForHost ? TEST_ENV_VARIABLE_VALUE : "myHostValue"; + String portVariableValue= "1234"; + String clientIdVariableValue= "myClientIDValue" + getTestName(); + Object environmentProperty = "connectionfactory." + factoryName; + + if(useEnvVarForHost) { + // Verify variable is set (by Surefire config), + // prevents spurious failure if not manually configured when run in IDE. + assertEquals("Expected to use env variable name", TEST_ENV_VARIABLE_NAME, hostVariableName); + assumeTrue("Environment variable not set as required", System.getenv().containsKey(TEST_ENV_VARIABLE_NAME)); + assertEquals("Environment variable value not as expected", TEST_ENV_VARIABLE_VALUE, System.getenv(TEST_ENV_VARIABLE_NAME)); + } else { + assertNotEquals("Expected to use a different name", TEST_ENV_VARIABLE_NAME, hostVariableName); + + setTestSystemProperty(hostVariableName, hostVariableValue); + } + setTestSystemProperty(portVariableName, portVariableValue); + + String uri = "amqp://${" + hostVariableName + "}:${" + portVariableName + "}?jms.clientID=${" + clientIdVariableName + "}"; + + Hashtable<Object, Object> env = new Hashtable<Object, Object>(); + env.put(environmentProperty, uri); + env.put(clientIdVariableName, clientIdVariableValue); + + Context ctx = createInitialContext(env); + + Object o = ctx.lookup(factoryName); + + assertNotNull("No object returned", o); + assertEquals("Unexpected class type for returned object", JmsConnectionFactory.class, o.getClass()); + + assertEquals("Unexpected ClientID for returned factory", clientIdVariableValue, ((JmsConnectionFactory) o).getClientID()); + + String expectedURI = "amqp://" + hostVariableValue + ":" + portVariableValue; + assertEquals("Unexpected URI for returned factory", expectedURI, ((JmsConnectionFactory) o).getRemoteURI()); + } + + @Test + public void testVariableExpansionQueue() throws Exception { + String lookupName = "myQueueLookup"; + String variableName = "myQueueVariable"; + String variableValue = "myQueueName"; + + Hashtable<Object, Object> env = new Hashtable<Object, Object>(); + env.put("queue." + lookupName, "${" + variableName +"}"); + + setTestSystemProperty(variableName, variableValue); + + Context ctx = createInitialContext(env); + + Object o = ctx.lookup(lookupName); + + assertNotNull("No object returned", o); + assertEquals("Unexpected class type for returned object", JmsQueue.class, o.getClass()); + + assertEquals("Unexpected name for returned queue", variableValue, ((JmsQueue) o).getQueueName()); + } + + @Test + public void testVariableExpansionTopic() throws Exception { + String lookupName = "myTopicLookup"; + String variableName = "myTopicVariable"; + String variableValue = "myTopicName"; + + Hashtable<Object, Object> env = new Hashtable<Object, Object>(); + env.put("topic." + lookupName, "${" + variableName +"}"); + + setTestSystemProperty(variableName, variableValue); + + Context ctx = createInitialContext(env); + + Object o = ctx.lookup(lookupName); + + assertNotNull("No object returned", o); + assertEquals("Unexpected class type for returned object", JmsTopic.class, o.getClass()); + + assertEquals("Unexpected name for returned queue", variableValue, ((JmsTopic) o).getTopicName()); + } } diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/VariableExpansionTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/VariableExpansionTest.java new file mode 100644 index 0000000..d13aa89 --- /dev/null +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/VariableExpansionTest.java @@ -0,0 +1,302 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jms.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.jms.test.QpidJmsTestCase; +import org.apache.qpid.jms.util.VariableExpansion.Resolver; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class VariableExpansionTest extends QpidJmsTestCase { + + // Environment variable name+value for test, configured in Surefire config + private static final String TEST_ENV_VARIABLE_NAME = "VAR_EXPANSION_TEST_ENV_VAR"; + private static final String TEST_ENV_VARIABLE_VALUE = "TestEnvVariableValue123"; + + private static final String TEST_ENV_VARIABLE_NAME_NOT_SET = "VAR_EXPANSION_TEST_ENV_VAR_NOT_SET"; + private static final String ESCAPE = "$"; + + private String testNamePrefix; + private String testPropName; + private String testPropValue; + private String testVariableForExpansion; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + + testNamePrefix = getTestName() + "."; + + testPropName = testNamePrefix + "myPropKey"; + testPropValue = testNamePrefix + "myPropValue"; + testVariableForExpansion = "${" + testPropName + "}"; + } + + // ===== Resolver tests ===== + + @Test + public void testResolveWithSysPropResolver() { + Resolver sysPropResolver = VariableExpansion.SYS_PROP_RESOLVER; + + assertNull("System property value unexpectedly set already", System.getProperty(testPropName)); + assertNull("Expected resolve to return null as property not set", sysPropResolver.resolve(testPropName)); + + setTestSystemProperty(testPropName, testPropValue); + + assertEquals("System property value not as expected", testPropValue, System.getProperty(testPropName)); + assertEquals("Resolved variable not as expected", testPropValue, sysPropResolver.resolve(testPropName)); + } + + @Test + public void testResolveWithEnvVarResolver() { + // Verify variable is set (by Surefire config), + // prevents spurious failure if not manually configured when run in IDE. + assumeTrue("Environment variable not set as required", System.getenv().containsKey(TEST_ENV_VARIABLE_NAME)); + assumeFalse("Environment variable unexpectedly set", System.getenv().containsKey(TEST_ENV_VARIABLE_NAME_NOT_SET)); + + assertEquals("Environment variable value not as expected", TEST_ENV_VARIABLE_VALUE, System.getenv(TEST_ENV_VARIABLE_NAME)); + + final Resolver envVarResolver = VariableExpansion.ENV_VAR_RESOLVER; + + assertNull("Expected resolve to return null as property not set", envVarResolver.resolve(TEST_ENV_VARIABLE_NAME_NOT_SET)); + + assertEquals("Resolved variable not as expected", TEST_ENV_VARIABLE_VALUE, envVarResolver.resolve(TEST_ENV_VARIABLE_NAME)); + } + + // ===== Expansion tests ===== + + @Test + public void testExpandWithResolverNotProvided() { + try { + VariableExpansion.expand("no-expansion-needed", null); + fail("Should have failed to expand,resolver not given"); + } catch (NullPointerException npe) { + // Expected + } + } + + @Test + public void testExpandNull() { + assertNull("Expected null", VariableExpansion.expand(null, variable -> "foo")); + } + + @Test + public void testExpandWithSysPropResolver() { + final Resolver resolver = VariableExpansion.SYS_PROP_RESOLVER; + + try { + VariableExpansion.expand(testVariableForExpansion, resolver); + fail("Should have failed to expand, property not set"); + } catch (IllegalArgumentException iae) { + // Expected + } + + setTestSystemProperty(testPropName, testPropValue); + + assertEquals("System property value not as expected", testPropValue, System.getProperty(testPropName)); + + String expanded = VariableExpansion.expand(testVariableForExpansion, resolver); + assertEquals("Expanded variable not as expected", testPropValue, expanded); + } + + @Test + public void testExpandWithEnvVarResolver() { + // Verify variable is set (by Surefire config), + // prevents spurious failure if not manually configured when run in IDE. + assumeTrue("Environment variable not set as required", System.getenv().containsKey(TEST_ENV_VARIABLE_NAME)); + + assertEquals("Environment variable value not as expected", TEST_ENV_VARIABLE_VALUE, System.getenv(TEST_ENV_VARIABLE_NAME)); + + final Resolver resolver = VariableExpansion.ENV_VAR_RESOLVER; + + try { + VariableExpansion.expand("${" + TEST_ENV_VARIABLE_NAME + "_NOT_SET" + "}", resolver); + fail("Should have failed to expand unset env variable"); + } catch (IllegalArgumentException iae) { + // Expected + } + + String expanded = VariableExpansion.expand("${" + TEST_ENV_VARIABLE_NAME + "}", resolver); + + assertEquals("Expanded variable not as expected", TEST_ENV_VARIABLE_VALUE, expanded); + } + + @Test + public void testExpandBasicWithMapResolver() { + Map<String,String> propsMap = new HashMap<>(); + propsMap.put(testPropName, testPropValue); + Resolver resolver = new VariableExpansion.MapResolver(propsMap); + + try { + VariableExpansion.expand("${" + testNamePrefix + "-not-set" + "}", resolver); + fail("Should have failed to expand, property not set"); + } catch (IllegalArgumentException iae) { + // Expected + } + + String expanded = VariableExpansion.expand(testVariableForExpansion, resolver); + + assertEquals("Expanded variable not as expected", testPropValue, expanded); + } + + @Test + public void testExpandBasic() { + // Variable is the full input + doBasicExpansionTestImpl(testVariableForExpansion, testPropValue); + + // Variable trails a prefix + String prefix = "prefix"; + doBasicExpansionTestImpl(prefix + testVariableForExpansion, prefix + testPropValue); + + // Variable precedes a suffix + String suffix = "suffix"; + doBasicExpansionTestImpl(testVariableForExpansion + suffix, testPropValue + suffix); + + // Variable is between prefix and suffix + doBasicExpansionTestImpl(prefix + testVariableForExpansion + suffix, prefix + testPropValue + suffix); + } + + @Test + public void testExpandMultipleVariables() { + String propName2 = "propName2"; + String propValue2 = "propValue2"; + String propName3 = "propName3"; + String propValue3 = "propValue3"; + + Map<String,String> propsMap = new HashMap<>(); + propsMap.put(testPropName, testPropValue); + propsMap.put(propName2, propValue2); + propsMap.put(propName3, propValue3); + + Resolver resolver = new VariableExpansion.MapResolver(propsMap); + + // Variables are the full input + String toExpand = testVariableForExpansion + "${" + propName2 +"}${" + propName3 + "}"; + String expected = testPropValue + propValue2 + propValue3; + + doBasicExpansionTestImpl(toExpand, expected, resolver); + + // Variable internal to overall input + toExpand = "prefix" + testVariableForExpansion + "-foo-${" + propName2 +"}-bar-${" + propName3 + "}" + "suffix"; + expected = "prefix" + testPropValue + "-foo-" + propValue2 +"-bar-" + propValue3 + "suffix"; + + doBasicExpansionTestImpl(toExpand, expected, resolver); + } + + @Test + public void testExpandMultipleInstancesOfVariable() { + Map<String,String> propsMap = new HashMap<>(); + propsMap.put(testPropName, testPropValue); + + Resolver resolver = new VariableExpansion.MapResolver(propsMap); + + String toExpand = "1-" + testVariableForExpansion + "2-" + testVariableForExpansion + "3-" + testVariableForExpansion; + String expected = "1-" + testPropValue + "2-" + testPropValue + "3-" + testPropValue; + + doBasicExpansionTestImpl(toExpand, expected, resolver); + } + + @Test + public void testExpandRecursiveThrows() { + String propName1 = "propName1"; + String propName2 = "propName2"; + String propValue1 = "propValue1-${" + propName2 + "}"; + String propValue2 = "recursive-${" + propName1 + "}"; + + Map<String,String> propsMap = new HashMap<>(); + propsMap.put(propName1, propValue1); + propsMap.put(propName2, propValue2); + try { + VariableExpansion.expand("${" + propName1 + "}", new VariableExpansion.MapResolver(propsMap)); + fail("Expected exception to be thrown"); + } catch (IllegalArgumentException iae) { + // Expected + } + } + + @Test + public void testExpandWithoutVariable() { + doBasicExpansionTestImpl("no-expansion-needed", "no-expansion-needed"); + doBasicExpansionTestImpl(ESCAPE + "no-expansion-needed", ESCAPE + "no-expansion-needed"); + doBasicExpansionTestImpl("no-expansion-needed" + ESCAPE, "no-expansion-needed" + ESCAPE); + doBasicExpansionTestImpl(ESCAPE + "no-expansion-needed" + ESCAPE, ESCAPE + "no-expansion-needed" + ESCAPE); + doBasicExpansionTestImpl("no" + ESCAPE + "-expansion-needed", "no" + ESCAPE + "-expansion-needed"); + } + + @Test + public void testExpandSkipsEscapedVariables() { + doBasicExpansionTestImpl(ESCAPE + testVariableForExpansion, testVariableForExpansion); + + String prefix = "prefix"; + doBasicExpansionTestImpl(prefix + ESCAPE + testVariableForExpansion, prefix + testVariableForExpansion); + + String suffix = "suffix"; + doBasicExpansionTestImpl(ESCAPE + testVariableForExpansion + suffix, testVariableForExpansion + suffix); + + doBasicExpansionTestImpl(prefix + ESCAPE + testVariableForExpansion + suffix, prefix + testVariableForExpansion + suffix); + } + + + private void doBasicExpansionTestImpl(String toExpand, String expectedExpansion) { + final Resolver mockResolver = Mockito.mock(Resolver.class); + + Mockito.when(mockResolver.resolve(testPropName)).thenReturn(testPropValue); + + doBasicExpansionTestImpl(toExpand, expectedExpansion, mockResolver); + } + + private void doBasicExpansionTestImpl(String toExpand, String expectedExpansion, Resolver resolver) { + String expanded = VariableExpansion.expand(toExpand, resolver); + assertEquals("Expanded variable not as expected", expectedExpansion, expanded); + } + + @Test + public void testExpandFailsToResolve() { + doBasicExpansionTestImpl(testVariableForExpansion); + } + + private void doBasicExpansionTestImpl(String toExpand) { + final Resolver mockResolver = Mockito.mock(Resolver.class); + + // Check when resolution fails + Mockito.when(mockResolver.resolve(testPropName)).thenReturn(null); + try { + VariableExpansion.expand(toExpand, mockResolver); + fail("Should have failed to expand, property not resolve"); + } catch (IllegalArgumentException iae) { + // Expected + } + + Mockito.verify(mockResolver).resolve(testPropName); + Mockito.verifyNoMoreInteractions(mockResolver); + } +} diff --git a/qpid-jms-docs/Configuration.md b/qpid-jms-docs/Configuration.md index ee51c44..7d9b520 100644 --- a/qpid-jms-docs/Configuration.md +++ b/qpid-jms-docs/Configuration.md @@ -43,11 +43,11 @@ Applications use a JNDI InitialContext, itself obtained from an InitialContextFa The property syntax used in the properties file or environment Hashtable is as follows: -+ To define a ConnectionFactory, use format: *connectionfactory.lookupName = URI* -+ To define a Queue, use format: *queue.lookupName = queueName* -+ To define a Topic use format: *topic.lookupName = topicName* ++ To define a ConnectionFactory, use format: *connectionfactory.<lookup-name> = <connection-uri>* ++ To define a Queue, use format: *queue.<lookup-name> = <queue-name>* ++ To define a Topic use format: *topic.<lookup-name> = <topic-name>* -For more details of the Connection URI, see the next section. +The property values which which constitute the connection URI, queue name, or topic name can also utilise simple *${variable}* expansion resolved in order from system properties, environment variables, or the properties file / environment Hashtable. For more details of the Connection URI, see the next section. As an example, consider the following properties used to define a ConnectionFactory, Queue, and Topic: --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org