[SPR-6025] support for recursive property placeholder replacement in system properties

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1847 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Rob Harrop 2009-09-08 17:13:02 +00:00
parent 86ccb01bc2
commit 9e864c9343
4 changed files with 106 additions and 40 deletions

View File

@ -17,6 +17,8 @@
package org.springframework.util;
import java.util.Properties;
import java.util.Set;
import java.util.HashSet;
/**
* Utility class for working with Strings that have placeholder values in them. A placeholder takes the form
@ -63,29 +65,75 @@ public class PropertyPlaceholderUtils {
* @return the supplied value with placeholders replaced inline.
*/
public static String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
StringBuilder result = new StringBuilder(value);
return parseStringValue(value, placeholderResolver, new HashSet<String>());
}
int startIndex = result.indexOf(PLACEHOLDER_PREFIX);
protected static String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
StringBuilder buf = new StringBuilder(strVal);
int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);
while (startIndex != -1) {
int endIndex = result.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());
int endIndex = findPlaceholderEndIndex(buf, startIndex);
if (endIndex != -1) {
String placeholder = result.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();
String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
if (!visitedPlaceholders.add(placeholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + placeholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal != null) {
result.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);
nextIndex = startIndex + propVal.length();
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);
//if (logger.isTraceEnabled()) {
// logger.trace("Resolved placeholder '" + placeholder + "'");
//}
startIndex = buf.indexOf(PLACEHOLDER_PREFIX, startIndex + propVal.length());
}
else {
// Proceed with unprocessed value.
startIndex = buf.indexOf(PLACEHOLDER_PREFIX, endIndex + PLACEHOLDER_SUFFIX.length());
}
startIndex = result.indexOf(PLACEHOLDER_PREFIX, nextIndex);
visitedPlaceholders.remove(placeholder);
}
else {
startIndex = -1;
}
}
return result.toString();
return buf.toString();
}
private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
int index = startIndex + PLACEHOLDER_PREFIX.length();
int withinNestedPlaceholder = 0;
while (index < buf.length()) {
if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {
if (withinNestedPlaceholder > 0) {
withinNestedPlaceholder--;
index = index + PLACEHOLDER_PREFIX.length() - 1;
}
else {
return index;
}
}
else if (StringUtils.substringMatch(buf, index, PLACEHOLDER_PREFIX)) {
withinNestedPlaceholder++;
index = index + PLACEHOLDER_PREFIX.length();
}
else {
index++;
}
}
return -1;
}
/**

View File

@ -16,17 +16,19 @@
package org.springframework.util;
import org.springframework.util.PropertyPlaceholderUtils.PlaceholderResolver;
/**
* Helper class for resolving placeholders in texts. Usually applied to file paths.
*
* <p>A text may contain <code>${...}</code> placeholders, to be resolved as
* system properties: e.g. <code>${user.dir}</code>.
* <p>A text may contain <code>${...}</code> placeholders, to be resolved as system properties: e.g.
* <code>${user.dir}</code>.
*
* @author Juergen Hoeller
* @since 1.2.5
* @see #PLACEHOLDER_PREFIX
* @see #PLACEHOLDER_SUFFIX
* @see System#getProperty(String)
* @since 1.2.5
*/
public abstract class SystemPropertyUtils {
@ -36,51 +38,39 @@ public abstract class SystemPropertyUtils {
/** Suffix for system property placeholders: "}" */
public static final String PLACEHOLDER_SUFFIX = "}";
/**
* Resolve ${...} placeholders in the given text,
* replacing them with corresponding system property values.
* Resolve ${...} placeholders in the given text, replacing them with corresponding system property values.
*
* @param text the String to resolve
* @return the resolved String
* @see #PLACEHOLDER_PREFIX
* @see #PLACEHOLDER_SUFFIX
*/
public static String resolvePlaceholders(String text) {
StringBuilder result = new StringBuilder(text);
public static String resolvePlaceholders(final String text) {
return PropertyPlaceholderUtils.replacePlaceholders(text, new PlaceholderResolver() {
int startIndex = result.indexOf(PLACEHOLDER_PREFIX);
while (startIndex != -1) {
int endIndex = result.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());
if (endIndex != -1) {
String placeholder = result.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();
public String resolvePlaceholder(String placeholderName) {
String propVal = null;
try {
String propVal = System.getProperty(placeholder);
propVal = System.getProperty(placeholderName);
if (propVal == null) {
// Fall back to searching the system environment.
propVal = System.getenv(placeholder);
propVal = System.getenv(placeholderName);
}
if (propVal != null) {
result.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);
nextIndex = startIndex + propVal.length();
}
else {
System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text +
if (propVal == null) {
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" + text +
"] as system property: neither system property nor environment variable found");
}
}
catch (Throwable ex) {
System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text +
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" + text +
"] as system property: " + ex);
}
startIndex = result.indexOf(PLACEHOLDER_PREFIX, nextIndex);
}
else {
startIndex = -1;
}
}
return result.toString();
}
return propVal;
}
});
}
}

View File

@ -43,6 +43,26 @@ public class PropertyPlaceholderUtilsTests {
assertEquals("foo=bar,bar=baz", PropertyPlaceholderUtils.replacePlaceholders(text, props));
}
@Test
public void testRecurseInProperty() {
String text = "foo=${bar}";
Properties props = new Properties();
props.setProperty("bar", "${baz}");
props.setProperty("baz", "bar");
assertEquals("foo=bar", PropertyPlaceholderUtils.replacePlaceholders(text, props));
}
@Test
public void testRecurseInPlaceholder() {
String text = "foo=${b${inner}}";
Properties props = new Properties();
props.setProperty("bar", "bar");
props.setProperty("inner", "ar");
assertEquals("foo=bar", PropertyPlaceholderUtils.replacePlaceholders(text, props));
}
@Test
public void testWithResolver() {
String text = "foo=${foo}";

View File

@ -31,6 +31,14 @@ public class SystemPropertyUtilsTests {
assertEquals("bar", resolved);
}
@Test
public void testRecursiveFromSystemProperty() {
System.setProperty("test.prop", "foo=${bar}");
System.setProperty("bar", "baz");
String resolved = SystemPropertyUtils.resolvePlaceholders("${test.prop}");
assertEquals("foo=baz", resolved);
}
@Test
public void testReplaceFromEnv() {
Map<String,String> env = System.getenv();