Commit 03325019 authored by Dave Syer's avatar Dave Syer

Allow user to specify classLoader as loader property

PropertiesLauncher now supports creating its own class loader
from looader.classLoader property. It will succeed if the
implementation specified has a default constructor or one
that takes a parent class loader, or one that takes a URL[]
and a parent class loader (like URLClassLoader).
parent f5f41fef
......@@ -233,11 +233,14 @@
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<mainClass>org.springframework.boot.loader.JarLauncher</mainClass>
<mainClass>org.springframework.boot.loader.PropertiesLauncher</mainClass>
</manifest>
<manifestEntries>
<Start-Class>${start-class}</Start-Class>
</manifestEntries>
<manifestEntries>
<Class-Loader>groovy.lang.GroovyClassLoader</Class-Loader>
</manifestEntries>
</archive>
</configuration>
</execution>
......
......@@ -33,7 +33,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.Archive.Entry;
......@@ -116,6 +120,8 @@ public class PropertiesLauncher extends Launcher {
private static final List<String> DEFAULT_PATHS = Arrays.asList("lib/");
private static final Pattern WORD_SEPARATOR = Pattern.compile("\\W+");
private final File home;
private List<String> paths = new ArrayList<String>(DEFAULT_PATHS);
......@@ -123,6 +129,9 @@ public class PropertiesLauncher extends Launcher {
private Properties properties = new Properties();
public PropertiesLauncher() {
if (!isDebug()) {
this.logger.setLevel(Level.SEVERE);
}
try {
this.home = getHomeDirectory();
initializeProperties(this.home);
......@@ -133,6 +142,22 @@ public class PropertiesLauncher extends Launcher {
}
}
private boolean isDebug() {
String debug = System.getProperty("debug");
if (debug != null && !"false".equals(debug)) {
return true;
}
debug = System.getProperty("DEBUG");
if (debug != null && !"false".equals(debug)) {
return true;
}
debug = System.getenv("DEBUG");
if (debug != null && !"false".equals(debug)) {
return true;
}
return false;
}
protected File getHomeDirectory() {
return new File(SystemPropertyUtils.resolvePlaceholders(System.getProperty(HOME,
"${user.dir}")));
......@@ -290,30 +315,82 @@ public class PropertiesLauncher extends Launcher {
@Override
protected String getMainClass() throws Exception {
String property = SystemPropertyUtils.getProperty(MAIN);
String mainClass = getProperty(MAIN, "Start-Class");
if (mainClass == null) {
throw new IllegalStateException("No '" + MAIN
+ "' or 'Start-Class' specified");
}
return mainClass;
}
@Override
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
ClassLoader loader = super.createClassLoader(archives);
String classLoaderType = getProperty("loader.classLoader");
if (classLoaderType != null) {
Class<?> type = Class.forName(classLoaderType, true, loader);
try {
loader = (ClassLoader) type.getConstructor(ClassLoader.class)
.newInstance(loader);
}
catch (NoSuchMethodException e) {
try {
loader = (ClassLoader) type.getConstructor(URL[].class,
ClassLoader.class).newInstance(new URL[0], loader);
}
catch (NoSuchMethodException ex) {
loader = (ClassLoader) type.newInstance();
}
}
this.logger.info("Using custom class loader: " + classLoaderType);
}
return loader;
}
private String getProperty(String propertyKey) throws Exception {
return getProperty(propertyKey, null);
}
private String getProperty(String propertyKey, String manifestKey) throws Exception {
if (manifestKey == null) {
manifestKey = propertyKey.replace(".", "-");
manifestKey = toCamelCase(manifestKey);
}
String property = SystemPropertyUtils.getProperty(propertyKey);
if (property != null) {
String mainClass = SystemPropertyUtils.resolvePlaceholders(property);
this.logger.info("Main class from environment: " + mainClass);
return mainClass;
String value = SystemPropertyUtils.resolvePlaceholders(property);
this.logger.fine("Property '" + propertyKey + "' from environment: " + value);
return value;
}
if (this.properties.containsKey(MAIN)) {
String mainClass = SystemPropertyUtils.resolvePlaceholders(this.properties
.getProperty(MAIN));
this.logger.info("Main class from properties: " + mainClass);
return mainClass;
if (this.properties.containsKey(propertyKey)) {
String value = SystemPropertyUtils.resolvePlaceholders(this.properties
.getProperty(propertyKey));
this.logger.fine("Property '" + propertyKey + "' from properties: " + value);
return value;
}
try {
// Prefer home dir for MANIFEST if there is one
String mainClass = new ExplodedArchive(this.home).getMainClass();
this.logger.info("Main class from home directory manifest: " + mainClass);
return mainClass;
Manifest manifest = new ExplodedArchive(this.home).getManifest();
if (manifest != null) {
String value = manifest.getMainAttributes().getValue(manifestKey);
this.logger.fine("Property '" + manifestKey
+ "' from home directory manifest: " + value);
return value;
}
}
catch (IllegalStateException ex) {
// Otherwise try the parent archive
String mainClass = createArchive().getMainClass();
this.logger.info("Main class from archive manifest: " + mainClass);
return mainClass;
}
// Otherwise try the parent archive
Manifest manifest = createArchive().getManifest();
if (manifest != null) {
String value = manifest.getMainAttributes().getValue(manifestKey);
if (value != null) {
this.logger.fine("Property '" + manifestKey + "' from archive manifest: "
+ value);
return value;
}
}
return null;
}
@Override
......@@ -444,6 +521,28 @@ public class PropertiesLauncher extends Launcher {
new PropertiesLauncher().launch(args);
}
public static String toCamelCase(CharSequence string) {
if (string == null) {
return null;
}
StringBuilder builder = new StringBuilder();
Matcher matcher = WORD_SEPARATOR.matcher(string);
int pos = 0;
while (matcher.find()) {
builder.append(capitalize(string.subSequence(pos, matcher.end()).toString()));
pos = matcher.end();
}
builder.append(capitalize(string.subSequence(pos, string.length()).toString()));
return builder.toString();
}
private static Object capitalize(String str) {
StringBuilder sb = new StringBuilder(str.length());
sb.append(Character.toUpperCase(str.charAt(0)));
sb.append(str.substring(1));
return sb.toString();
}
/**
* Convenience class for finding nested archives (archive entries that can be
* classpath entries).
......
......@@ -18,14 +18,19 @@ package org.springframework.boot.loader;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.loader.archive.Archive;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
......@@ -98,6 +103,27 @@ public class PropertiesLauncherTests {
waitFor("Hello World");
}
@Test
public void testUserSpecifiedClassLoader() throws Exception {
System.setProperty("loader.path", "jars/app.jar");
System.setProperty("loader.classLoader", URLClassLoader.class.getName());
PropertiesLauncher launcher = new PropertiesLauncher();
assertEquals("[jars/app.jar]", ReflectionTestUtils.getField(launcher, "paths")
.toString());
launcher.launch(new String[0]);
waitFor("Hello World");
}
@Test
public void testCustomClassLoaderCreation() throws Exception {
System.setProperty("loader.classLoader", TestLoader.class.getName());
PropertiesLauncher launcher = new PropertiesLauncher();
ClassLoader loader = launcher
.createClassLoader(Collections.<Archive> emptyList());
assertNotNull(loader);
assertEquals(TestLoader.class.getName(), loader.getClass().getName());
}
@Test
public void testUserSpecifiedConfigPathWins() throws Exception {
......@@ -132,4 +158,16 @@ public class PropertiesLauncherTests {
assertTrue("Timed out waiting for (" + value + ")", timeout);
}
public static class TestLoader extends URLClassLoader {
public TestLoader(ClassLoader parent) {
super(new URL[0], parent);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment