Allow PropertyLauncher loader.path to be configured using manifest

Closes gh-7178
pull/7262/head
Andy Wilkinson 8 years ago
parent a638dcd51b
commit ee7141cf63

@ -200,12 +200,12 @@ the appropriate launcher:
properties (System properties, environment variables, manifest entries or properties (System properties, environment variables, manifest entries or
`application.properties`). `application.properties`).
[cols="2,4"]
|=== |===
|Key |Purpose |Key |Purpose
|`loader.path` |`loader.path`
|Comma-separated Classpath, e.g. `lib,${HOME}/app/lib`. Earlier entries take precedence, just like a regular `-classpath` on the `javac` command line. |Comma-separated Classpath, e.g. `lib,${HOME}/app/lib`. Earlier entries take precedence,
just like a regular `-classpath` on the `javac` command line.
|`loader.home` |`loader.home`
|Location of additional properties file, e.g. `file:///opt/app` |Location of additional properties file, e.g. `file:///opt/app`
@ -227,25 +227,52 @@ properties (System properties, environment variables, manifest entries or
|`loader.system` |`loader.system`
|Boolean flag to indicate that all properties should be added to System properties |Boolean flag to indicate that all properties should be added to System properties
(defaults to `false`) (defaults to `false`)
|=== |===
Manifest entry keys are formed by capitalizing initial letters of words and changing the When specified as environment variables or manifest entries, the following names should
separator to "`-`" from "`.`" (e.g. `Loader-Path`). The exception is `loader.main` which be used:
is looked up as `Start-Class` in the manifest for compatibility with `JarLauncher`).
|===
|Key | Manifest entry | Environment variable
|`loader.path`
|`Loader-Path`
|`LOADER_PATH`
|`loader.home`
|
|`LOADER_HOME`
|`loader.args`
|`Loader-Args`
|`LOADER_ARGS`
|`loader.main`
|`Start-Class`
|`LOADER_MAIN`
|`loader.config.location`
|
|`LOADER_CONFIG_LOCATION`
|`loader.system`
|
|`LOADER_SYSTEM`
|===
TIP: Build plugins automatically move the `Main-Class` attribute to `Start-Class` when TIP: Build plugins automatically move the `Main-Class` attribute to `Start-Class` when
the fat jar is built. If you are using that, specify the name of the class to launch using the fat jar is built. If you are using that, specify the name of the class to launch using
the `Main-Class` attribute and leave out `Start-Class`. the `Main-Class` attribute and leave out `Start-Class`.
Environment variables can be capitalized with underscore separators instead of periods.
* `loader.home` is the directory location of an additional properties file (overriding * `loader.home` is the directory location of an additional properties file (overriding
the default) as long as `loader.config.location` is not specified. the default) as long as `loader.config.location` is not specified.
* `loader.path` can contain directories (scanned recursively for jar and zip files), * `loader.path` can contain directories (scanned recursively for jar and zip files),
archive paths, or wildcard patterns (for the default JVM behavior). archive paths, or wildcard patterns (for the default JVM behavior).
* `loader.path` (if empty) defaults to `lib` (meaning a local directory or a nested one if * `loader.path` (if empty) defaults to `BOOT-INF/lib` (meaning a local directory or a
running from an archive). Because of this `PropertiesLauncher` behaves the same as nested one if running from an archive). Because of this `PropertiesLauncher` behaves the
`JarLauncher` when no additional configuration is provided. same as `JarLauncher` when no additional configuration is provided.
* Placeholder replacement is done from System and environment variables plus the * Placeholder replacement is done from System and environment variables plus the
properties file itself on all values before use. properties file itself on all values before use.

@ -132,7 +132,7 @@ public class PropertiesLauncher extends Launcher {
public PropertiesLauncher() { public PropertiesLauncher() {
try { try {
this.home = getHomeDirectory(); this.home = getHomeDirectory();
initializeProperties(this.home); initializeProperties();
initializePaths(); initializePaths();
this.parent = createArchive(); this.parent = createArchive();
} }
@ -146,7 +146,7 @@ public class PropertiesLauncher extends Launcher {
.resolvePlaceholders(System.getProperty(HOME, "${user.dir}"))); .resolvePlaceholders(System.getProperty(HOME, "${user.dir}")));
} }
private void initializeProperties(File home) throws Exception, IOException { private void initializeProperties() throws Exception, IOException {
String config = "classpath:BOOT-INF/classes/" String config = "classpath:BOOT-INF/classes/"
+ SystemPropertyUtils.resolvePlaceholders( + SystemPropertyUtils.resolvePlaceholders(
SystemPropertyUtils.getProperty(CONFIG_NAME, "application")) SystemPropertyUtils.getProperty(CONFIG_NAME, "application"))
@ -273,14 +273,10 @@ public class PropertiesLauncher extends Launcher {
} }
} }
private void initializePaths() throws IOException { private void initializePaths() throws Exception {
String path = SystemPropertyUtils.getProperty(PATH); String path = getProperty(PATH);
if (path == null) {
path = this.properties.getProperty(PATH);
}
if (path != null) { if (path != null) {
this.paths = parsePathsProperty( this.paths = parsePathsProperty(path);
SystemPropertyUtils.resolvePlaceholders(path));
} }
log("Nested archive paths: " + this.paths); log("Nested archive paths: " + this.paths);
} }

@ -17,16 +17,21 @@
package org.springframework.boot.loader; package org.springframework.boot.loader;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.Archive;
@ -45,6 +50,9 @@ public class PropertiesLauncherTests {
@Rule @Rule
public InternalOutputCapture output = new InternalOutputCapture(); public InternalOutputCapture output = new InternalOutputCapture();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Before @Before
public void setup() throws IOException { public void setup() throws IOException {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@ -207,6 +215,23 @@ public class PropertiesLauncherTests {
.isEqualTo("[foo, bar]"); .isEqualTo("[foo, bar]");
} }
@SuppressWarnings("unchecked")
@Test
public void testLoadPathCustomizedUsingManifest() throws Exception {
System.setProperty("loader.home",
this.temporaryFolder.getRoot().getAbsolutePath());
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
manifest.getMainAttributes().putValue("Loader-Path", "/foo.jar, /bar");
File manifestFile = new File(this.temporaryFolder.getRoot(),
"META-INF/MANIFEST.MF");
manifestFile.getParentFile().mkdirs();
manifest.write(new FileOutputStream(manifestFile));
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat((List<String>) ReflectionTestUtils.getField(launcher, "paths"))
.containsExactly("/foo.jar", "/bar/");
}
private void waitFor(String value) throws Exception { private void waitFor(String value) throws Exception {
int count = 0; int count = 0;
boolean timeout = false; boolean timeout = false;

@ -1,2 +1 @@
loader.main: demo.Application loader.main: demo.Application
loader.path: etc/,lib,.

Loading…
Cancel
Save