Tighten up PropertiesLauncher's contract

The main changes are:

- Switch to `loader.properties` instead of `application.properties`
- Search for `loader.properties` in `loader.home` as well as in
  the classpath
- Placeholder replacements in MANIFEST.MF (using `loader.properties`
  or system/env vars)

See gh-7221
Closes gh-8346
This commit is contained in:
Dave Syer
2017-02-21 12:06:57 +00:00
committed by Andy Wilkinson
parent 5278baca01
commit e4c807b884
8 changed files with 159 additions and 67 deletions

View File

@@ -142,48 +142,64 @@ public class PropertiesLauncher extends Launcher {
}
protected File getHomeDirectory() {
return new File(SystemPropertyUtils
.resolvePlaceholders(System.getProperty(HOME, "${user.dir}")));
try {
return new File(getPropertyWithDefault(HOME, "${user.dir}"));
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
private void initializeProperties() throws Exception, IOException {
String config = "classpath:BOOT-INF/classes/"
+ SystemPropertyUtils.resolvePlaceholders(
SystemPropertyUtils.getProperty(CONFIG_NAME, "application"))
+ ".properties";
config = SystemPropertyUtils.resolvePlaceholders(
SystemPropertyUtils.getProperty(CONFIG_LOCATION, config));
InputStream resource = getResource(config);
if (resource != null) {
log("Found: " + config);
try {
this.properties.load(resource);
}
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);
}
}
if (SystemPropertyUtils
.resolvePlaceholders("${" + SET_SYSTEM_PROPERTIES + ":false}")
.equals("true")) {
log("Adding resolved properties to System properties");
for (Object key : Collections.list(this.properties.propertyNames())) {
String value = this.properties.getProperty((String) key);
System.setProperty((String) key, value);
}
}
List<String> configs = new ArrayList<String>();
if (getProperty(CONFIG_LOCATION) != null) {
configs.add(getProperty(CONFIG_LOCATION));
}
else {
log("Not found: " + config);
String[] names = getPropertyWithDefault(CONFIG_NAME, "loader,application")
.split(",");
for (String name : names) {
configs.add("file:" + getHomeDirectory() + "/" + name + ".properties");
configs.add("classpath:" + name + ".properties");
configs.add("classpath:BOOT-INF/classes/" + name + ".properties");
}
}
for (String config : configs) {
InputStream resource = getResource(config);
if (resource != null) {
log("Found: " + config);
try {
this.properties.load(resource);
}
finally {
resource.close();
}
for (Object key : Collections.list(this.properties.propertyNames())) {
if (config.endsWith("application.properties")
&& ((String) key).startsWith("loader.")) {
warn("WARNING: use of application.properties for PropertiesLauncher is deprecated");
}
String text = this.properties.getProperty((String) key);
String value = SystemPropertyUtils
.resolvePlaceholders(this.properties, text);
if (value != null) {
this.properties.put(key, value);
}
}
if ("true".equals(getProperty(SET_SYSTEM_PROPERTIES))) {
log("Adding resolved properties to System properties");
for (Object key : Collections.list(this.properties.propertyNames())) {
String value = this.properties.getProperty((String) key);
System.setProperty((String) key, value);
}
}
// Load the first one we find
return;
}
else {
log("Not found: " + config);
}
}
}
private InputStream getResource(String config) throws Exception {
@@ -354,34 +370,50 @@ public class PropertiesLauncher extends Launcher {
}
private String getProperty(String propertyKey) throws Exception {
return getProperty(propertyKey, null);
return getProperty(propertyKey, null, null);
}
private String getProperty(String propertyKey, String manifestKey) throws Exception {
return getProperty(propertyKey, manifestKey, null);
}
private String getPropertyWithDefault(String propertyKey, String defaultValue)
throws Exception {
return getProperty(propertyKey, null, defaultValue);
}
private String getProperty(String propertyKey, String manifestKey,
String defaultValue) throws Exception {
if (manifestKey == null) {
manifestKey = propertyKey.replace('.', '-');
manifestKey = toCamelCase(manifestKey);
}
String property = SystemPropertyUtils.getProperty(propertyKey);
if (property != null) {
String value = SystemPropertyUtils.resolvePlaceholders(property);
String value = SystemPropertyUtils.resolvePlaceholders(this.properties,
property);
log("Property '" + propertyKey + "' from environment: " + value);
return value;
}
if (this.properties.containsKey(propertyKey)) {
String value = SystemPropertyUtils
.resolvePlaceholders(this.properties.getProperty(propertyKey));
String value = SystemPropertyUtils.resolvePlaceholders(this.properties,
this.properties.getProperty(propertyKey));
log("Property '" + propertyKey + "' from properties: " + value);
return value;
}
try {
// Prefer home dir for MANIFEST if there is one
Manifest manifest = new ExplodedArchive(this.home, false).getManifest();
if (manifest != null) {
String value = manifest.getMainAttributes().getValue(manifestKey);
log("Property '" + manifestKey + "' from home directory manifest: "
+ value);
return value;
if (this.home != null) {
// Prefer home dir for MANIFEST if there is one
Manifest manifest = new ExplodedArchive(this.home, false).getManifest();
if (manifest != null) {
String value = manifest.getMainAttributes().getValue(manifestKey);
if (value != null) {
log("Property '" + manifestKey
+ "' from home directory manifest: " + value);
return SystemPropertyUtils.resolvePlaceholders(this.properties,
value);
}
}
}
}
catch (IllegalStateException ex) {
@@ -393,10 +425,11 @@ public class PropertiesLauncher extends Launcher {
String value = manifest.getMainAttributes().getValue(manifestKey);
if (value != null) {
log("Property '" + manifestKey + "' from archive manifest: " + value);
return value;
return SystemPropertyUtils.resolvePlaceholders(this.properties, value);
}
}
return null;
return defaultValue == null ? defaultValue
: SystemPropertyUtils.resolvePlaceholders(this.properties, defaultValue);
}
@Override
@@ -436,10 +469,10 @@ public class PropertiesLauncher extends Launcher {
log("Adding classpath entries from archive " + archive.getUrl() + root);
lib.add(archive);
}
Archive nested = getNestedArchive(root);
List<Archive> nested = getNestedArchive(root);
if (nested != null) {
log("Adding classpath entries from nested " + nested.getUrl() + root);
lib.add(nested);
log("Adding classpath entries from nested " + root);
lib.addAll(nested);
}
return lib;
}
@@ -457,19 +490,21 @@ public class PropertiesLauncher extends Launcher {
return null;
}
private Archive getNestedArchive(String root) throws Exception {
private List<Archive> getNestedArchive(String root) throws Exception {
List<Archive> list = new ArrayList<Archive>();
if (root.startsWith("/")
|| this.parent.getUrl().equals(this.home.toURI().toURL())) {
// If home dir is same as parent archive, no need to add it twice.
return null;
return list;
}
EntryFilter filter = new PrefixMatchingArchiveFilter(root);
if (this.parent.getNestedArchives(filter).isEmpty()) {
return null;
return list;
}
// If there are more archives nested in this subdirectory (root) then create a new
// virtual archive for them, and have it added to the classpath
return new FilteredArchive(this.parent, filter);
list.add(new FilteredArchive(this.parent, filter));
return list;
}
private void addNestedEntries(List<Archive> lib) {
@@ -548,6 +583,11 @@ public class PropertiesLauncher extends Launcher {
}
}
private void warn(String message) {
// We shouldn't use java.util.logging because of classpath issues
System.out.println(message);
}
/**
* Convenience class for finding nested archives that have a prefix in their file path
* (e.g. "lib/").

View File

@@ -31,6 +31,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.MockitoAnnotations;
@@ -50,6 +51,9 @@ public class PropertiesLauncherTests {
@Rule
public InternalOutputCapture output = new InternalOutputCapture();
@Rule
public ExpectedException expected = ExpectedException.none();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -72,9 +76,28 @@ public class PropertiesLauncherTests {
@Test
public void testDefaultHome() {
System.clearProperty("loader.home");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getHomeDirectory())
.isEqualTo(new File(System.getProperty("user.dir")));
}
@Test
public void testAlternateHome() throws Exception {
System.setProperty("loader.home", "src/test/resources/home");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getHomeDirectory())
.isEqualTo(new File(System.getProperty("loader.home")));
assertThat(launcher.getMainClass()).isEqualTo("demo.HomeApplication");
}
@Test
public void testNonExistentHome() throws Exception {
System.setProperty("loader.home", "src/test/resources/nonexistent");
this.expected.expectMessage("Invalid source folder");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getHomeDirectory())
.isNotEqualTo(new File(System.getProperty("loader.home")));
}
@Test
@@ -93,6 +116,13 @@ public class PropertiesLauncherTests {
.isEqualTo("[etc/]");
}
@Test
public void testRootOfClasspathFirst() throws Exception {
System.setProperty("loader.config.name", "bar");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("my.BarApplication");
}
@Test
public void testUserSpecifiedDotPath() throws Exception {
System.setProperty("loader.path", ".");
@@ -232,6 +262,13 @@ public class PropertiesLauncherTests {
.containsExactly("/foo.jar", "/bar/");
}
@Test
public void testManifestWithPlaceholders() throws Exception {
System.setProperty("loader.home", "src/test/resources/placeholders");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("demo.FooApplication");
}
private void waitFor(String value) throws Exception {
int count = 0;
boolean timeout = false;

View File

@@ -1 +1 @@
loader.main: demo.Application
loader.main: demo.NoSuchApplication

View File

@@ -0,0 +1 @@
loader.main: my.BootInfBarApplication

View File

@@ -0,0 +1 @@
loader.main: demo.Application

View File

@@ -0,0 +1 @@
loader.main: demo.HomeApplication

View File

@@ -0,0 +1 @@
foo.main: demo.FooApplication