From a3af83cf9656cec35e1d9a6909484a90f0e39381 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 24 Sep 2013 09:15:24 +0100 Subject: [PATCH] Add internal property replacement E.g. in application.properties (for launcher) foo: Application loader.main: my.${foo} loader.path: etc --- .../boot/loader/PropertiesLauncher.java | 27 ++++++++++- .../boot/loader/util/SystemPropertyUtils.java | 45 ++++++++++++++----- .../src/test/resources/foo.properties | 3 +- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java index 6e18e175e5..00f7900482 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java @@ -25,6 +25,7 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.logging.Logger; @@ -69,14 +70,30 @@ public class PropertiesLauncher implements ArchiveFilter { public static final String MAIN = "loader.main"; /** - * Properties key for classpath entries (directories possibly containing jars) + * Properties key for classpath entries (directories possibly containing jars). + * Defaults to "lib/" (relative to {@link #HOME loader home directory}). */ public static final String PATH = "loader.path"; + /** + * Properties key for home directory. This is the location of external configuration + * if not on classpath, and also the base path for any relative paths in the + * {@link #PATH loader path}. Defaults to current working directory ( + * ${user.home}). + */ public static final String HOME = "loader.home"; + /** + * Properties key for name of external configuration file (excluding suffix). Defaults + * to "application". Ignored if {@link #CONFIG_LOCATION loader config location} is + * provided instead. + */ public static final String CONFIG_NAME = "loader.config.name"; + /** + * Properties key for config file location (including optional classpath:, file: or + * URL prefix) + */ public static final String CONFIG_LOCATION = "loader.config.location"; private static final List DEFAULT_PATHS = Arrays.asList("lib/"); @@ -188,6 +205,14 @@ public class PropertiesLauncher implements ArchiveFilter { finally { resource.close(); } + for (Object key : Collections.list(this.properties.propertyNames())) { + String text = this.properties.getProperty((String) key); + String value = SystemPropertyUtils.resolvePlaceholders(this.properties, + text); + if (value != null) { + this.properties.put(key, value); + } + } } else { this.logger.info("Not found: " + config); diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/SystemPropertyUtils.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/SystemPropertyUtils.java index 644c73dc73..cd58b7094b 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/SystemPropertyUtils.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/SystemPropertyUtils.java @@ -17,6 +17,7 @@ package org.springframework.boot.loader.util; import java.util.HashSet; +import java.util.Properties; import java.util.Set; /** @@ -61,13 +62,30 @@ public abstract class SystemPropertyUtils { */ public static String resolvePlaceholders(String text) { if (text == null) { - throw new IllegalArgumentException("Argument 'value' must not be null."); + return text; } - return parseStringValue(text, text, new HashSet()); + return parseStringValue(null, text, text, new HashSet()); } - private static String parseStringValue(String value, String current, - Set visitedPlaceholders) { + /** + * Resolve ${...} placeholders in the given text, replacing them with corresponding + * system property values. + * @param properties a properties instance to use in addition to System + * @param text the String to resolve + * @return the resolved String + * @see #PLACEHOLDER_PREFIX + * @see #PLACEHOLDER_SUFFIX + * @throws IllegalArgumentException if there is an unresolvable placeholder + */ + public static String resolvePlaceholders(Properties properties, String text) { + if (text == null) { + return text; + } + return parseStringValue(properties, text, text, new HashSet()); + } + + private static String parseStringValue(Properties properties, String value, + String current, Set visitedPlaceholders) { StringBuilder buf = new StringBuilder(current); @@ -85,9 +103,10 @@ public abstract class SystemPropertyUtils { // Recursive invocation, parsing placeholders contained in the // placeholder // key. - placeholder = parseStringValue(value, placeholder, visitedPlaceholders); + placeholder = parseStringValue(properties, value, placeholder, + visitedPlaceholders); // Now obtain the value for the fully resolved key... - String propVal = resolvePlaceholder(value, placeholder); + String propVal = resolvePlaceholder(properties, value, placeholder); if (propVal == null && VALUE_SEPARATOR != null) { int separatorIndex = placeholder.indexOf(VALUE_SEPARATOR); if (separatorIndex != -1) { @@ -95,7 +114,7 @@ public abstract class SystemPropertyUtils { separatorIndex); String defaultValue = placeholder.substring(separatorIndex + VALUE_SEPARATOR.length()); - propVal = resolvePlaceholder(value, actualPlaceholder); + propVal = resolvePlaceholder(properties, value, actualPlaceholder); if (propVal == null) { propVal = defaultValue; } @@ -104,7 +123,8 @@ public abstract class SystemPropertyUtils { if (propVal != null) { // Recursive invocation, parsing placeholders contained in the // previously resolved placeholder value. - propVal = parseStringValue(value, propVal, visitedPlaceholders); + propVal = parseStringValue(properties, value, propVal, + visitedPlaceholders); buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal); startIndex = buf.indexOf(PLACEHOLDER_PREFIX, @@ -125,20 +145,23 @@ public abstract class SystemPropertyUtils { return buf.toString(); } - private static String resolvePlaceholder(String text, String placeholderName) { + private static String resolvePlaceholder(Properties properties, String text, + String placeholderName) { try { String propVal = System.getProperty(placeholderName); if (propVal == null) { // Fall back to searching the system environment. propVal = System.getenv(placeholderName); } - return propVal; + if (propVal != null) { + return propVal; + } } catch (Throwable ex) { System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" + text + "] as system property: " + ex); - return null; } + return properties.getProperty(placeholderName); } private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) { diff --git a/spring-boot-tools/spring-boot-loader/src/test/resources/foo.properties b/spring-boot-tools/spring-boot-loader/src/test/resources/foo.properties index 495dc6b44d..c6aa3d40a5 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/resources/foo.properties +++ b/spring-boot-tools/spring-boot-loader/src/test/resources/foo.properties @@ -1,2 +1,3 @@ -loader.main: my.Application +foo: Application +loader.main: my.${foo} loader.path: etc \ No newline at end of file