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