Loader changes
This commit is contained in:
parent
053c072155
commit
e9fd7c96b8
|
|
@ -195,8 +195,4 @@ public abstract class Launcher {
|
||||||
return (Runnable) constructor.newInstance(mainClass, args);
|
return (Runnable) constructor.newInstance(mainClass, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isArchive(String name) {
|
|
||||||
return name.endsWith(".jar") || name.endsWith(".zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,22 +23,19 @@ import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.springframework.boot.loader.util.SystemPropertyUtils;
|
import org.springframework.boot.loader.util.SystemPropertyUtils;
|
||||||
import org.springframework.util.ResourceUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
|
||||||
* {@link Launcher} for archives with user-configured classpath and main class via a
|
* {@link Launcher} for archives with user-configured classpath and main class via a
|
||||||
* properties file. This model is often more flexible and more amenable to creating
|
* properties file. This model is often more flexible and more amenable to creating
|
||||||
* well-behaved OS-level services than a model based on executable jars.
|
* well-behaved OS-level services than a model based on executable jars.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Looks in various places for a properties file to extract loader settings, defaulting to
|
* Looks in various places for a properties file to extract loader settings, defaulting to
|
||||||
|
|
@ -48,7 +45,7 @@ import org.springframework.util.ResourceUtils;
|
||||||
* will look for <code>foo.properties</code>. If that file doesn't exist then tries
|
* will look for <code>foo.properties</code>. If that file doesn't exist then tries
|
||||||
* <code>loader.config.location</code> (with allowed prefixes <code>classpath:</code> and
|
* <code>loader.config.location</code> (with allowed prefixes <code>classpath:</code> and
|
||||||
* <code>file:</code> or any valid URL). Once that file is located turns it into
|
* <code>file:</code> or any valid URL). Once that file is located turns it into
|
||||||
* Properties and extracts optional values (which can also be provided oroverridden as
|
* Properties and extracts optional values (which can also be provided overridden as
|
||||||
* System properties in case the file doesn't exist):
|
* System properties in case the file doesn't exist):
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
|
|
@ -59,16 +56,13 @@ import org.springframework.util.ResourceUtils;
|
||||||
* loader is set up. No default, but will fall back to looking in a
|
* loader is set up. No default, but will fall back to looking in a
|
||||||
* <code>MANIFEST.MF</code> if there is one.</li>
|
* <code>MANIFEST.MF</code> if there is one.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
public class PropertiesLauncher extends Launcher {
|
public class PropertiesLauncher extends Launcher {
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(Launcher.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties key for main class
|
* Properties key for main class
|
||||||
*/
|
*/
|
||||||
|
|
@ -81,11 +75,9 @@ public class PropertiesLauncher extends Launcher {
|
||||||
|
|
||||||
public static final String HOME = "loader.home";
|
public static final String HOME = "loader.home";
|
||||||
|
|
||||||
public static String CONFIG_NAME = "loader.config.name";
|
public static final String CONFIG_NAME = "loader.config.name";
|
||||||
|
|
||||||
public static String CONFIG_LOCATION = "loader.config.location";
|
public static final String CONFIG_LOCATION = "loader.config.location";
|
||||||
|
|
||||||
private Logger logger = Logger.getLogger(Launcher.class.getName());
|
|
||||||
|
|
||||||
private static final List<String> DEFAULT_PATHS = Arrays.asList("lib/");
|
private static final List<String> DEFAULT_PATHS = Arrays.asList("lib/");
|
||||||
|
|
||||||
|
|
@ -94,51 +86,20 @@ public class PropertiesLauncher extends Launcher {
|
||||||
private Properties properties = new Properties();
|
private Properties properties = new Properties();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(String[] args) {
|
protected void launch(String[] args, ProtectionDomain protectionDomain)
|
||||||
try {
|
throws Exception {
|
||||||
launch(args, new ExplodedArchive(new File(getHomeDirectory())));
|
launch(args, new ExplodedArchive(getHomeDirectory()));
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getHomeDirectory() {
|
protected File getHomeDirectory() {
|
||||||
return SystemPropertyUtils.resolvePlaceholders(System.getProperty(HOME,
|
return new File(SystemPropertyUtils.resolvePlaceholders(System.getProperty(HOME,
|
||||||
"${user.dir}"));
|
"${user.dir}")));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isNestedArchive(Archive.Entry entry) {
|
|
||||||
String name = entry.getName();
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
for (String path : this.paths) {
|
|
||||||
if (path.length() > 0 && name.equals(path)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (String path : this.paths) {
|
|
||||||
if (path.length() > 0 && name.startsWith(path) && isArchive(name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postProcessLib(Archive archive, List<Archive> lib) throws Exception {
|
|
||||||
lib.add(0, archive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look in various places for a properties file to extract loader settings. Default to
|
* Look in various places for a properties file to extract loader settings. Default to
|
||||||
* <code>application.properties</code> either on the current classpath or in the
|
* <code>application.properties</code> either on the current classpath or in the
|
||||||
* current working directory.
|
* current working directory.
|
||||||
*
|
|
||||||
* @see org.springframework.boot.loader.Launcher#launch(java.lang.String[],
|
* @see org.springframework.boot.loader.Launcher#launch(java.lang.String[],
|
||||||
* org.springframework.boot.loader.Archive)
|
* org.springframework.boot.loader.Archive)
|
||||||
*/
|
*/
|
||||||
|
|
@ -149,83 +110,24 @@ public class PropertiesLauncher extends Launcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initialize() throws Exception {
|
protected void initialize() throws Exception {
|
||||||
|
initializeProperties();
|
||||||
|
initializePaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeProperties() throws Exception, IOException {
|
||||||
String config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
String config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
||||||
CONFIG_NAME, "application")) + ".properties";
|
CONFIG_NAME, "application")) + ".properties";
|
||||||
while (config.startsWith("/")) {
|
InputStream resource = getClasspathResource(config);
|
||||||
config = config.substring(1);
|
|
||||||
}
|
|
||||||
this.logger.fine("Trying default location: " + config);
|
|
||||||
InputStream resource = getClass().getResourceAsStream("/" + config);
|
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
|
|
||||||
config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
||||||
CONFIG_LOCATION, config));
|
CONFIG_LOCATION, config));
|
||||||
|
resource = getResource(config);
|
||||||
if (config.startsWith("classpath:")) {
|
|
||||||
|
|
||||||
config = config.substring("classpath:".length());
|
|
||||||
while (config.startsWith("/")) {
|
|
||||||
config = config.substring(1);
|
|
||||||
}
|
|
||||||
config = "/" + config;
|
|
||||||
this.logger.fine("Trying classpath: " + config);
|
|
||||||
resource = getClass().getResourceAsStream(config);
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (config.startsWith("file:")) {
|
|
||||||
|
|
||||||
config = config.substring("file:".length());
|
|
||||||
if (config.startsWith("//")) {
|
|
||||||
config = config.substring(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (!config.contains(":")) {
|
|
||||||
|
|
||||||
File file = new File(config);
|
|
||||||
this.logger.fine("Trying file: " + config);
|
|
||||||
if (file.canRead()) {
|
|
||||||
resource = new FileInputStream(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
URL url = new URL(config);
|
|
||||||
if (exists(url)) {
|
|
||||||
URLConnection con = url.openConnection();
|
|
||||||
try {
|
|
||||||
resource = con.getInputStream();
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
// Close the HTTP connection (if applicable).
|
|
||||||
if (con instanceof HttpURLConnection) {
|
|
||||||
((HttpURLConnection) con).disconnect();
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
this.logger.info("Found: " + config);
|
this.logger.info("Found: " + config);
|
||||||
this.properties.load(resource);
|
|
||||||
try {
|
try {
|
||||||
String path = System.getProperty(PATH);
|
this.properties.load(resource);
|
||||||
if (path == null) {
|
|
||||||
path = this.properties.getProperty(PATH);
|
|
||||||
}
|
|
||||||
if (path != null) {
|
|
||||||
path = SystemPropertyUtils.resolvePlaceholders(path);
|
|
||||||
this.paths = new ArrayList<String>(Arrays.asList(path.split(",")));
|
|
||||||
for (int i = 0; i < this.paths.size(); i++) {
|
|
||||||
this.paths.set(i, this.paths.get(i).trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
resource.close();
|
resource.close();
|
||||||
|
|
@ -234,53 +136,153 @@ public class PropertiesLauncher extends Launcher {
|
||||||
else {
|
else {
|
||||||
this.logger.info("Not found: " + config);
|
this.logger.info("Not found: " + config);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < this.paths.size(); i++) {
|
}
|
||||||
if (!this.paths.get(i).endsWith("/")) {
|
|
||||||
// Always a directory
|
private InputStream getResource(String config) throws Exception {
|
||||||
this.paths.set(i, this.paths.get(i) + "/");
|
if (config.startsWith("classpath:")) {
|
||||||
}
|
return getClasspathResource(config.substring("classpath:".length()));
|
||||||
if (this.paths.get(i).startsWith("./")) {
|
}
|
||||||
// No need for current dir path
|
config = stripFileUrlPrefix(config);
|
||||||
this.paths.set(i, this.paths.get(i).substring(2));
|
if (isUrl(config)) {
|
||||||
|
return getURLResource(config);
|
||||||
|
}
|
||||||
|
return getFileResource(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stripFileUrlPrefix(String config) {
|
||||||
|
if (config.startsWith("file:")) {
|
||||||
|
config = config.substring("file:".length());
|
||||||
|
if (config.startsWith("//")) {
|
||||||
|
config = config.substring(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Iterator<String> iter = this.paths.iterator(); iter.hasNext();) {
|
return config;
|
||||||
String path = iter.next();
|
}
|
||||||
if (path.equals(".") || path.equals("")) {
|
|
||||||
// Empty path is always on the classpath so no need for it to be
|
private boolean isUrl(String config) {
|
||||||
// explicitly listed here
|
return config.contains("://");
|
||||||
iter.remove();
|
}
|
||||||
|
|
||||||
|
private InputStream getClasspathResource(String config) {
|
||||||
|
while (config.startsWith("/")) {
|
||||||
|
config = config.substring(1);
|
||||||
|
}
|
||||||
|
config = "/" + config;
|
||||||
|
this.logger.fine("Trying classpath: " + config);
|
||||||
|
return getClass().getResourceAsStream(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getFileResource(String config) throws Exception {
|
||||||
|
File file = new File(config);
|
||||||
|
this.logger.fine("Trying file: " + config);
|
||||||
|
if (file.canRead()) {
|
||||||
|
return new FileInputStream(file);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getURLResource(String config) throws Exception {
|
||||||
|
URL url = new URL(config);
|
||||||
|
if (exists(url)) {
|
||||||
|
URLConnection con = url.openConnection();
|
||||||
|
try {
|
||||||
|
return con.getInputStream();
|
||||||
}
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
// Close the HTTP connection (if applicable).
|
||||||
|
if (con instanceof HttpURLConnection) {
|
||||||
|
((HttpURLConnection) con).disconnect();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean exists(URL url) throws IOException {
|
||||||
|
// Try a URL connection content-length header...
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
try {
|
||||||
|
connection.setUseCaches(connection.getClass().getSimpleName()
|
||||||
|
.startsWith("JNLP"));
|
||||||
|
if (connection instanceof HttpURLConnection) {
|
||||||
|
HttpURLConnection httpConnection = (HttpURLConnection) connection;
|
||||||
|
httpConnection.setRequestMethod("HEAD");
|
||||||
|
int responseCode = httpConnection.getResponseCode();
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (connection.getContentLength() >= 0);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (connection instanceof HttpURLConnection) {
|
||||||
|
((HttpURLConnection) connection).disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializePaths() throws IOException {
|
||||||
|
String path = System.getProperty(PATH);
|
||||||
|
if (path == null) {
|
||||||
|
path = this.properties.getProperty(PATH);
|
||||||
|
}
|
||||||
|
if (path != null) {
|
||||||
|
this.paths = parsePathsProperty(SystemPropertyUtils.resolvePlaceholders(path));
|
||||||
}
|
}
|
||||||
this.logger.info("Nested archive paths: " + this.paths);
|
this.logger.info("Nested archive paths: " + this.paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean exists(URL url) throws IOException {
|
private List<String> parsePathsProperty(String commaSeparatedPaths) {
|
||||||
|
List<String> paths = new ArrayList<String>();
|
||||||
|
for (String path : commaSeparatedPaths.split(",")) {
|
||||||
|
path = cleanupPath(path);
|
||||||
|
// Empty path is always on the classpath so no need for it to be explicitly
|
||||||
|
// listed here
|
||||||
|
if (!(path.equals(".") || path.equals(""))) {
|
||||||
|
paths.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
// Try a URL connection content-length header...
|
private String cleanupPath(String path) {
|
||||||
URLConnection con = url.openConnection();
|
path = path.trim();
|
||||||
ResourceUtils.useCachesIfNecessary(con);
|
// Always a directory
|
||||||
HttpURLConnection httpCon = (con instanceof HttpURLConnection ? (HttpURLConnection) con
|
if (!path.endsWith("/")) {
|
||||||
: null);
|
path = path + "/";
|
||||||
if (httpCon != null) {
|
|
||||||
httpCon.setRequestMethod("HEAD");
|
|
||||||
int code = httpCon.getResponseCode();
|
|
||||||
if (code == HttpURLConnection.HTTP_OK) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (con.getContentLength() >= 0) {
|
// No need for current dir path
|
||||||
return true;
|
if (path.startsWith("./")) {
|
||||||
|
path = path.substring(2);
|
||||||
}
|
}
|
||||||
if (httpCon != null) {
|
return path;
|
||||||
// no HTTP OK status, and no content-length header: give up
|
}
|
||||||
httpCon.disconnect();
|
|
||||||
|
@Override
|
||||||
|
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||||
|
String name = entry.getName();
|
||||||
|
for (String path : this.paths) {
|
||||||
|
if (path.length() > 0) {
|
||||||
|
if ((entry.isDirectory() && name.equals(path))
|
||||||
|
|| (!entry.isDirectory() && name.startsWith(path) && isArchive(name))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isArchive(String name) {
|
||||||
|
return name.endsWith(".jar") || name.endsWith(".zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postProcessLib(Archive archive, List<Archive> lib) throws Exception {
|
||||||
|
lib.add(0, archive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ package org.springframework.boot.loader.util;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for resolving placeholders in texts. Usually applied to file paths.
|
* Helper class for resolving placeholders in texts. Usually applied to file paths.
|
||||||
*
|
*
|
||||||
|
|
@ -50,7 +48,7 @@ public abstract class SystemPropertyUtils {
|
||||||
/** Value separator for system property placeholders: ":" */
|
/** Value separator for system property placeholders: ":" */
|
||||||
public static final String VALUE_SEPARATOR = ":";
|
public static final String VALUE_SEPARATOR = ":";
|
||||||
|
|
||||||
private static final PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper();
|
private static final String SIMPLE_PREFIX = PLACEHOLDER_PREFIX.substring(1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve ${...} placeholders in the given text, replacing them with corresponding
|
* Resolve ${...} placeholders in the given text, replacing them with corresponding
|
||||||
|
|
@ -62,147 +60,120 @@ public abstract class SystemPropertyUtils {
|
||||||
* @throws IllegalArgumentException if there is an unresolvable placeholder
|
* @throws IllegalArgumentException if there is an unresolvable placeholder
|
||||||
*/
|
*/
|
||||||
public static String resolvePlaceholders(String text) {
|
public static String resolvePlaceholders(String text) {
|
||||||
return helper.replacePlaceholders(text);
|
if (text == null) {
|
||||||
|
throw new IllegalArgumentException("Argument 'value' must not be null.");
|
||||||
|
}
|
||||||
|
return parseStringValue(text, text, new HashSet<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
static protected class PropertyPlaceholderHelper {
|
private static String parseStringValue(String value, String current,
|
||||||
|
Set<String> visitedPlaceholders) {
|
||||||
|
|
||||||
private static final String simplePrefix = PLACEHOLDER_PREFIX.substring(1);
|
StringBuilder buf = new StringBuilder(current);
|
||||||
|
|
||||||
/**
|
int startIndex = current.indexOf(PLACEHOLDER_PREFIX);
|
||||||
* Replaces all placeholders of format {@code $ name} with the value returned from
|
while (startIndex != -1) {
|
||||||
* the supplied {@link PlaceholderResolver}.
|
int endIndex = findPlaceholderEndIndex(buf, startIndex);
|
||||||
* @param value the value containing the placeholders to be replaced.
|
if (endIndex != -1) {
|
||||||
* @return the supplied value with placeholders replaced inline.
|
String placeholder = buf.substring(
|
||||||
*/
|
startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
|
||||||
public String replacePlaceholders(String value) {
|
String originalPlaceholder = placeholder;
|
||||||
Assert.notNull(value, "Argument 'value' must not be null.");
|
if (!visitedPlaceholders.add(originalPlaceholder)) {
|
||||||
return parseStringValue(value, value, new HashSet<String>());
|
throw new IllegalArgumentException("Circular placeholder reference '"
|
||||||
}
|
+ originalPlaceholder + "' in property definitions");
|
||||||
|
}
|
||||||
private String parseStringValue(String value, String current,
|
// Recursive invocation, parsing placeholders contained in the
|
||||||
Set<String> visitedPlaceholders) {
|
// placeholder
|
||||||
|
// key.
|
||||||
StringBuilder buf = new StringBuilder(current);
|
placeholder = parseStringValue(value, placeholder, visitedPlaceholders);
|
||||||
|
// Now obtain the value for the fully resolved key...
|
||||||
int startIndex = current.indexOf(PLACEHOLDER_PREFIX);
|
String propVal = resolvePlaceholder(value, placeholder);
|
||||||
while (startIndex != -1) {
|
if (propVal == null && VALUE_SEPARATOR != null) {
|
||||||
int endIndex = findPlaceholderEndIndex(buf, startIndex);
|
int separatorIndex = placeholder.indexOf(VALUE_SEPARATOR);
|
||||||
if (endIndex != -1) {
|
if (separatorIndex != -1) {
|
||||||
String placeholder = buf.substring(
|
String actualPlaceholder = placeholder.substring(0,
|
||||||
startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
|
separatorIndex);
|
||||||
String originalPlaceholder = placeholder;
|
String defaultValue = placeholder.substring(separatorIndex
|
||||||
if (!visitedPlaceholders.add(originalPlaceholder)) {
|
+ VALUE_SEPARATOR.length());
|
||||||
throw new IllegalArgumentException(
|
propVal = resolvePlaceholder(value, actualPlaceholder);
|
||||||
"Circular placeholder reference '" + originalPlaceholder
|
if (propVal == null) {
|
||||||
+ "' in property definitions");
|
propVal = defaultValue;
|
||||||
}
|
|
||||||
// Recursive invocation, parsing placeholders contained in the
|
|
||||||
// placeholder
|
|
||||||
// key.
|
|
||||||
placeholder = parseStringValue(value, placeholder,
|
|
||||||
visitedPlaceholders);
|
|
||||||
// Now obtain the value for the fully resolved key...
|
|
||||||
String propVal = resolvePlaceholder(value, placeholder);
|
|
||||||
if (propVal == null && VALUE_SEPARATOR != null) {
|
|
||||||
int separatorIndex = placeholder.indexOf(VALUE_SEPARATOR);
|
|
||||||
if (separatorIndex != -1) {
|
|
||||||
String actualPlaceholder = placeholder.substring(0,
|
|
||||||
separatorIndex);
|
|
||||||
String defaultValue = placeholder.substring(separatorIndex
|
|
||||||
+ VALUE_SEPARATOR.length());
|
|
||||||
propVal = resolvePlaceholder(value, actualPlaceholder);
|
|
||||||
if (propVal == null) {
|
|
||||||
propVal = defaultValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (propVal != null) {
|
}
|
||||||
// Recursive invocation, parsing placeholders contained in the
|
if (propVal != null) {
|
||||||
// previously resolved placeholder value.
|
// Recursive invocation, parsing placeholders contained in the
|
||||||
propVal = parseStringValue(value, propVal, visitedPlaceholders);
|
// previously resolved placeholder value.
|
||||||
buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(),
|
propVal = parseStringValue(value, propVal, visitedPlaceholders);
|
||||||
propVal);
|
buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(),
|
||||||
startIndex = buf.indexOf(PLACEHOLDER_PREFIX,
|
propVal);
|
||||||
startIndex + propVal.length());
|
startIndex = buf.indexOf(PLACEHOLDER_PREFIX,
|
||||||
}
|
startIndex + propVal.length());
|
||||||
else {
|
|
||||||
// Proceed with unprocessed value.
|
|
||||||
startIndex = buf.indexOf(PLACEHOLDER_PREFIX, endIndex
|
|
||||||
+ PLACEHOLDER_SUFFIX.length());
|
|
||||||
}
|
|
||||||
visitedPlaceholders.remove(originalPlaceholder);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
startIndex = -1;
|
// Proceed with unprocessed value.
|
||||||
|
startIndex = buf.indexOf(PLACEHOLDER_PREFIX, endIndex
|
||||||
|
+ PLACEHOLDER_SUFFIX.length());
|
||||||
}
|
}
|
||||||
|
visitedPlaceholders.remove(originalPlaceholder);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
return buf.toString();
|
startIndex = -1;
|
||||||
}
|
|
||||||
|
|
||||||
private String resolvePlaceholder(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;
|
|
||||||
}
|
|
||||||
catch (Throwable ex) {
|
|
||||||
System.err.println("Could not resolve placeholder '" + placeholderName
|
|
||||||
+ "' in [" + text + "] as system property: " + ex);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
return buf.toString();
|
||||||
int index = startIndex + PLACEHOLDER_PREFIX.length();
|
}
|
||||||
int withinNestedPlaceholder = 0;
|
|
||||||
while (index < buf.length()) {
|
private static String resolvePlaceholder(String text, String placeholderName) {
|
||||||
if (substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {
|
try {
|
||||||
if (withinNestedPlaceholder > 0) {
|
String propVal = System.getProperty(placeholderName);
|
||||||
withinNestedPlaceholder--;
|
if (propVal == null) {
|
||||||
index = index + PLACEHOLDER_SUFFIX.length();
|
// Fall back to searching the system environment.
|
||||||
}
|
propVal = System.getenv(placeholderName);
|
||||||
else {
|
}
|
||||||
return index;
|
return propVal;
|
||||||
}
|
}
|
||||||
}
|
catch (Throwable ex) {
|
||||||
else if (substringMatch(buf, index,
|
System.err.println("Could not resolve placeholder '" + placeholderName
|
||||||
PropertyPlaceholderHelper.simplePrefix)) {
|
+ "' in [" + text + "] as system property: " + ex);
|
||||||
withinNestedPlaceholder++;
|
return null;
|
||||||
index = index + PropertyPlaceholderHelper.simplePrefix.length();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
||||||
|
int index = startIndex + PLACEHOLDER_PREFIX.length();
|
||||||
|
int withinNestedPlaceholder = 0;
|
||||||
|
while (index < buf.length()) {
|
||||||
|
if (substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {
|
||||||
|
if (withinNestedPlaceholder > 0) {
|
||||||
|
withinNestedPlaceholder--;
|
||||||
|
index = index + PLACEHOLDER_SUFFIX.length();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
index++;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
else if (substringMatch(buf, index, SIMPLE_PREFIX)) {
|
||||||
}
|
withinNestedPlaceholder++;
|
||||||
|
index = index + SIMPLE_PREFIX.length();
|
||||||
private static boolean substringMatch(CharSequence str, int index,
|
|
||||||
CharSequence substring) {
|
|
||||||
for (int j = 0; j < substring.length(); j++) {
|
|
||||||
int i = index + j;
|
|
||||||
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
else {
|
||||||
}
|
index++;
|
||||||
|
|
||||||
private static class Assert {
|
|
||||||
|
|
||||||
public static void notNull(Object target, String message) {
|
|
||||||
if (target == null) {
|
|
||||||
throw new IllegalStateException(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean substringMatch(CharSequence str, int index,
|
||||||
|
CharSequence substring) {
|
||||||
|
for (int j = 0; j < substring.length(); j++) {
|
||||||
|
int i = index + j;
|
||||||
|
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.boot.loader;
|
package org.springframework.boot.loader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
@ -40,7 +42,8 @@ public class PropertiesLauncherTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultHome() {
|
public void testDefaultHome() {
|
||||||
assertEquals(System.getProperty("user.dir"), this.launcher.getHomeDirectory());
|
assertEquals(new File(System.getProperty("user.dir")),
|
||||||
|
this.launcher.getHomeDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue