Further re-organization of launcher code
This commit is contained in:
parent
0e0eb7d3fa
commit
15bc25dc29
|
|
@ -13,14 +13,15 @@
|
|||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- TODO: maybe put these in the parent? -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Base class for launchers that can start an application with a fully configured
|
||||
* classpath.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class AbstractLauncher implements ArchiveFilter {
|
||||
|
||||
private Logger logger = Logger.getLogger(AbstractLauncher.class.getName());
|
||||
|
||||
private LaunchHelper helper = new LaunchHelper();
|
||||
|
||||
/**
|
||||
* Launch the application. This method is the initial entry point that should be
|
||||
* called by a subclass {@code public static void main(String[] args)} method.
|
||||
* @param args the incoming arguments
|
||||
*/
|
||||
public void launch(String[] args) {
|
||||
try {
|
||||
launch(args, getClass().getProtectionDomain());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the application given the protection domain.
|
||||
* @param args the incoming arguments
|
||||
* @param protectionDomain the protection domain
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void launch(String[] args, ProtectionDomain protectionDomain)
|
||||
throws Exception {
|
||||
CodeSource codeSource = protectionDomain.getCodeSource();
|
||||
URI location = (codeSource == null ? null : codeSource.getLocation().toURI());
|
||||
String path = (location == null ? null : location.getPath());
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Unable to determine code source archive");
|
||||
}
|
||||
File root = new File(path);
|
||||
if (!root.exists()) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to determine code source archive from " + root);
|
||||
}
|
||||
Archive archive = (root.isDirectory() ? new ExplodedArchive(root)
|
||||
: new JarFileArchive(root));
|
||||
launch(args, archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the application given the archive file
|
||||
* @param args the incoming arguments
|
||||
* @param archive the underlying (zip/war/jar) archive
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void launch(String[] args, Archive archive) throws Exception {
|
||||
List<Archive> lib = new ArrayList<Archive>();
|
||||
lib.addAll(this.helper.findNestedArchives(archive, this));
|
||||
this.logger.fine("Added " + lib.size() + " entries");
|
||||
postProcessLib(archive, lib);
|
||||
String mainClass = this.helper.getMainClass(archive);
|
||||
this.helper.launch(args, mainClass, lib);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to post-process lib entries before they are used. Implementations can add
|
||||
* and remove entries.
|
||||
* @param archive the archive
|
||||
* @param lib the existing lib
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void postProcessLib(Archive archive, List<Archive> lib) throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.Archive.Entry;
|
||||
import org.springframework.boot.loader.archive.Archive.EntryFilter;
|
||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
|
||||
/**
|
||||
* Base class for executable archive {@link Launcher}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class ExecutableArchiveLauncher extends Launcher {
|
||||
|
||||
private final Archive archive;
|
||||
|
||||
public ExecutableArchiveLauncher() {
|
||||
try {
|
||||
this.archive = createArchive();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Archive createArchive() throws Exception {
|
||||
ProtectionDomain protectionDomain = getClass().getProtectionDomain();
|
||||
CodeSource codeSource = protectionDomain.getCodeSource();
|
||||
URI location = (codeSource == null ? null : codeSource.getLocation().toURI());
|
||||
String path = (location == null ? null : location.getPath());
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Unable to determine code source archive");
|
||||
}
|
||||
File root = new File(path);
|
||||
if (!root.exists()) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to determine code source archive from " + root);
|
||||
}
|
||||
return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
|
||||
}
|
||||
|
||||
protected final Archive getArchive() {
|
||||
return this.archive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainClass() throws Exception {
|
||||
return this.archive.getMainClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Archive> getClassPathArchives() throws Exception {
|
||||
List<Archive> archives = new ArrayList<Archive>(
|
||||
this.archive.getNestedArchives(new EntryFilter() {
|
||||
@Override
|
||||
public boolean matches(Entry entry) {
|
||||
return isNestedArchive(entry);
|
||||
}
|
||||
}));
|
||||
postProcessClassPathArchives(archives);
|
||||
return archives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified {@link JarEntry} is a nested item that should be added
|
||||
* to the classpath. The method is called once for each entry.
|
||||
* @param entry the jar entry
|
||||
* @return {@code true} if the entry is a nested item (jar or folder)
|
||||
*/
|
||||
protected abstract boolean isNestedArchive(Archive.Entry entry);
|
||||
|
||||
/**
|
||||
* Called to post-process archive entries before they are used. Implementations can
|
||||
* add and remove entries.
|
||||
* @param archives the archives
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,26 +18,27 @@ package org.springframework.boot.loader;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
|
||||
/**
|
||||
* {@link AbstractLauncher} for JAR based archives. This launcher assumes that dependency
|
||||
* jars are included inside a {@code /lib} directory.
|
||||
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
|
||||
* included inside a {@code /lib} directory.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JarLauncher extends AbstractLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new JarLauncher().launch(args);
|
||||
}
|
||||
public class JarLauncher extends ExecutableArchiveLauncher {
|
||||
|
||||
@Override
|
||||
public boolean isArchive(Archive.Entry entry) {
|
||||
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||
return !entry.isDirectory() && entry.getName().startsWith("lib/");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessLib(Archive archive, List<Archive> lib) throws Exception {
|
||||
lib.add(0, archive);
|
||||
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
||||
archives.add(0, getArchive());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new JarLauncher().launch(args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import java.security.PrivilegedExceptionAction;
|
|||
import org.springframework.boot.loader.jar.RandomAccessJarFile;
|
||||
|
||||
/**
|
||||
* {@link ClassLoader} used by the {@link AbstractLauncher}.
|
||||
* {@link ClassLoader} used by the {@link Launcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -22,79 +22,64 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
|
||||
/**
|
||||
* Common convenience methods shared by launcher implementations.
|
||||
* Base class for launchers that can start an application with a fully configured
|
||||
* classpath backed by one or more {@link Archive}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class LaunchHelper {
|
||||
public abstract class Launcher {
|
||||
|
||||
private Logger logger = Logger.getLogger(LaunchHelper.class.getName());
|
||||
protected Logger logger = Logger.getLogger(Launcher.class.getName());
|
||||
|
||||
/**
|
||||
* The main runner class. This must be loaded by the created ClassLoader so cannot be
|
||||
* directly referenced.
|
||||
*/
|
||||
private static final String RUNNER_CLASS = AbstractLauncher.class.getPackage()
|
||||
.getName() + ".MainMethodRunner";
|
||||
private static final String RUNNER_CLASS = Launcher.class.getPackage().getName()
|
||||
+ ".MainMethodRunner";
|
||||
|
||||
/**
|
||||
* Launch the application. This method is the initial entry point that should be
|
||||
* called by a subclass {@code public static void main(String[] args)} method.
|
||||
* @param args the incoming arguments
|
||||
* @param mainClass the main class
|
||||
* @param lib a collection of archives (zip/jar/war or directory)
|
||||
* @throws Exception
|
||||
*/
|
||||
public void launch(String[] args, String mainClass, List<Archive> lib)
|
||||
throws Exception {
|
||||
ClassLoader classLoader = createClassLoader(lib);
|
||||
launch(args, mainClass, classLoader);
|
||||
protected void launch(String[] args) {
|
||||
try {
|
||||
ClassLoader classLoader = createClassLoader(getClassPathArchives());
|
||||
launch(args, getMainClass(), classLoader);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param archive the archive to search
|
||||
* @return an accumulation of nested archives
|
||||
* @throws Exception
|
||||
*/
|
||||
public List<Archive> findNestedArchives(Archive archive, ArchiveFilter filter)
|
||||
throws Exception {
|
||||
List<Archive> lib = new ArrayList<Archive>();
|
||||
for (Archive.Entry entry : archive.getEntries()) {
|
||||
if (filter.isArchive(entry)) {
|
||||
this.logger.fine("Adding: " + entry.getName());
|
||||
lib.add(archive.getNestedArchive(entry));
|
||||
}
|
||||
}
|
||||
return lib;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the main class that should be used to launch the application. By default
|
||||
* this method uses a {@code Start-Class} manifest entry.
|
||||
* @param archive the archive
|
||||
* @return the main class
|
||||
* @throws Exception
|
||||
*/
|
||||
public String getMainClass(Archive archive) throws Exception {
|
||||
String mainClass = archive.getManifest().getMainAttributes()
|
||||
.getValue("Start-Class");
|
||||
if (mainClass == null) {
|
||||
throw new IllegalStateException("No 'Start-Class' manifest entry specified");
|
||||
}
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a classloader for the specified lib.
|
||||
* @param lib the lib
|
||||
* Create a classloader for the specified archives.
|
||||
* @param archives the archives
|
||||
* @return the classloader
|
||||
* @throws Exception
|
||||
*/
|
||||
protected ClassLoader createClassLoader(List<Archive> lib) throws Exception {
|
||||
URL[] urls = new URL[lib.size()];
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
urls[i] = lib.get(i).getUrl();
|
||||
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
|
||||
List<URL> urls = new ArrayList<URL>(archives.size());
|
||||
for (Archive archive : archives) {
|
||||
urls.add(archive.getUrl());
|
||||
}
|
||||
return createClassLoader(urls);
|
||||
return createClassLoader(urls.toArray(new URL[urls.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a classloader for the specified URLs
|
||||
* @param urls the URLs
|
||||
* @return the classloader
|
||||
* @throws Exception
|
||||
*/
|
||||
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
|
||||
return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -113,16 +98,6 @@ public class LaunchHelper {
|
|||
runnerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a classloader for the specified URLs
|
||||
* @param urls the URLs
|
||||
* @return the classloader
|
||||
* @throws Exception
|
||||
*/
|
||||
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
|
||||
return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@code MainMethodRunner} used to launch the application.
|
||||
* @param mainClass the main class
|
||||
|
|
@ -139,4 +114,17 @@ public class LaunchHelper {
|
|||
return (Runnable) constructor.newInstance(mainClass, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the main class that should be launched.
|
||||
* @return the name of the main class
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract String getMainClass() throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the archives that will be used to construct the class path.
|
||||
* @return the class path archives
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract List<Archive> getClassPathArchives() throws Exception;
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ package org.springframework.boot.loader;
|
|||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Utility class that used by {@link AbstractLauncher}s to call a main method. This class allows
|
||||
* Utility class that used by {@link Launcher}s to call a main method. This class allows
|
||||
* methods to be executed within a thread configured with a specific context class loader.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
|
|
|
|||
|
|
@ -30,11 +30,15 @@ import java.util.List;
|
|||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.Archive.Entry;
|
||||
import org.springframework.boot.loader.archive.Archive.EntryFilter;
|
||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||
import org.springframework.boot.loader.util.SystemPropertyUtils;
|
||||
|
||||
/**
|
||||
* {@link AbstractLauncher} for archives with user-configured classpath and main class via
|
||||
* a properties file. This model is often more flexible and more amenable to creating
|
||||
* {@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
|
||||
* well-behaved OS-level services than a model based on executable jars.
|
||||
*
|
||||
* <p>
|
||||
|
|
@ -60,9 +64,9 @@ import org.springframework.boot.loader.util.SystemPropertyUtils;
|
|||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PropertiesLauncher implements ArchiveFilter {
|
||||
public class PropertiesLauncher extends Launcher {
|
||||
|
||||
private Logger logger = Logger.getLogger(AbstractLauncher.class.getName());
|
||||
private Logger logger = Logger.getLogger(Launcher.class.getName());
|
||||
|
||||
/**
|
||||
* Properties key for main class
|
||||
|
|
@ -105,92 +109,28 @@ public class PropertiesLauncher implements ArchiveFilter {
|
|||
|
||||
private static final List<String> DEFAULT_PATHS = Arrays.asList("lib/");
|
||||
|
||||
private final File home;
|
||||
|
||||
private List<String> paths = new ArrayList<String>(DEFAULT_PATHS);
|
||||
|
||||
private Properties properties = new Properties();
|
||||
|
||||
private LaunchHelper helper = new LaunchHelper();
|
||||
|
||||
public static void main(String[] args) {
|
||||
new PropertiesLauncher().launch(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the application. This method is the initial entry point that should be
|
||||
* called by a subclass {@code public static void main(String[] args)} method.
|
||||
* @param args the incoming arguments
|
||||
*/
|
||||
public void launch(String[] args) {
|
||||
public PropertiesLauncher() {
|
||||
try {
|
||||
File home = getHomeDirectory();
|
||||
initialize(home);
|
||||
this.helper.launch(args, getMainClass(home), getLibrary(home, this.paths));
|
||||
this.home = getHomeDirectory();
|
||||
initializeProperties(this.home);
|
||||
initializePaths();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArchive(Archive.Entry entry) {
|
||||
return entry.isDirectory() || isArchive(entry.getName());
|
||||
}
|
||||
|
||||
protected File getHomeDirectory() {
|
||||
return new File(SystemPropertyUtils.resolvePlaceholders(System.getProperty(HOME,
|
||||
"${user.dir}")));
|
||||
}
|
||||
|
||||
protected String getMainClass(File home) throws Exception {
|
||||
if (System.getProperty(MAIN) != null) {
|
||||
return SystemPropertyUtils.resolvePlaceholders(System.getProperty(MAIN));
|
||||
}
|
||||
if (this.properties.containsKey(MAIN)) {
|
||||
return SystemPropertyUtils.resolvePlaceholders(this.properties
|
||||
.getProperty(MAIN));
|
||||
}
|
||||
return this.helper.getMainClass(new ExplodedArchive(home));
|
||||
}
|
||||
|
||||
protected void initialize(File home) throws Exception {
|
||||
initializeProperties(home);
|
||||
initializePaths();
|
||||
}
|
||||
|
||||
private boolean isArchive(String name) {
|
||||
return name.endsWith(".jar") || name.endsWith(".zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the configured paths and look for nested archives.
|
||||
*
|
||||
* @param home the home directory for this launch
|
||||
* @param paths the directory roots for classpath entries
|
||||
* @return a library of archives that can be used as a classpath
|
||||
* @throws Exception
|
||||
*/
|
||||
private List<Archive> getLibrary(File home, List<String> paths) throws Exception {
|
||||
List<Archive> lib = new ArrayList<Archive>();
|
||||
for (String path : paths) {
|
||||
String root = cleanupPath(stripFileUrlPrefix(path));
|
||||
File file = new File(root);
|
||||
if (!root.startsWith("/")) {
|
||||
file = new File(home, root);
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
this.logger.info("Adding classpath entries from " + path);
|
||||
Archive archive = new ExplodedArchive(file);
|
||||
lib.addAll(this.helper.findNestedArchives(archive, this));
|
||||
lib.add(0, archive);
|
||||
}
|
||||
else {
|
||||
this.logger.info("No directory found at " + path);
|
||||
}
|
||||
}
|
||||
return lib;
|
||||
}
|
||||
|
||||
private void initializeProperties(File home) throws Exception, IOException {
|
||||
String config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
||||
CONFIG_NAME, "application")) + ".properties";
|
||||
|
|
@ -346,6 +286,46 @@ public class PropertiesLauncher implements ArchiveFilter {
|
|||
return paths;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainClass() throws Exception {
|
||||
if (System.getProperty(MAIN) != null) {
|
||||
return SystemPropertyUtils.resolvePlaceholders(System.getProperty(MAIN));
|
||||
}
|
||||
if (this.properties.containsKey(MAIN)) {
|
||||
return SystemPropertyUtils.resolvePlaceholders(this.properties
|
||||
.getProperty(MAIN));
|
||||
}
|
||||
return new ExplodedArchive(this.home).getMainClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Archive> getClassPathArchives() throws Exception {
|
||||
List<Archive> lib = new ArrayList<Archive>();
|
||||
for (String path : this.paths) {
|
||||
String root = cleanupPath(stripFileUrlPrefix(path));
|
||||
File file = new File(root);
|
||||
if (!root.startsWith("/")) {
|
||||
file = new File(this.home, root);
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
this.logger.info("Adding classpath entries from " + path);
|
||||
Archive archive = new ExplodedArchive(file);
|
||||
lib.addAll(archive.getNestedArchives(new EntryFilter() {
|
||||
@Override
|
||||
public boolean matches(Entry entry) {
|
||||
return entry.isDirectory() || entry.getName().endsWith(".jar")
|
||||
|| entry.getName().endsWith(".zip");
|
||||
}
|
||||
}));
|
||||
lib.add(0, archive);
|
||||
}
|
||||
else {
|
||||
this.logger.info("No directory found at " + path);
|
||||
}
|
||||
}
|
||||
return lib;
|
||||
}
|
||||
|
||||
private String cleanupPath(String path) {
|
||||
path = path.trim();
|
||||
// Always a directory
|
||||
|
|
@ -359,4 +339,8 @@ public class PropertiesLauncher implements ArchiveFilter {
|
|||
return path;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new PropertiesLauncher().launch(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,21 +19,19 @@ package org.springframework.boot.loader;
|
|||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
|
||||
/**
|
||||
* {@link AbstractLauncher} for WAR based archives. This launcher for standard WAR
|
||||
* archives. Supports dependencies in {@code WEB-INF/lib} as well as
|
||||
* {@code WEB-INF/lib-provided}, classes are loaded from {@code WEB-INF/classes}.
|
||||
* {@link Launcher} for WAR based archives. This launcher for standard WAR archives.
|
||||
* Supports dependencies in {@code WEB-INF/lib} as well as {@code WEB-INF/lib-provided},
|
||||
* classes are loaded from {@code WEB-INF/classes}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class WarLauncher extends AbstractLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new WarLauncher().launch(args);
|
||||
}
|
||||
public class WarLauncher extends ExecutableArchiveLauncher {
|
||||
|
||||
@Override
|
||||
public boolean isArchive(Archive.Entry entry) {
|
||||
public boolean isNestedArchive(Archive.Entry entry) {
|
||||
if (entry.isDirectory()) {
|
||||
return entry.getName().equals("WEB-INF/classes/");
|
||||
}
|
||||
|
|
@ -44,20 +42,18 @@ public class WarLauncher extends AbstractLauncher {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessLib(Archive archive, List<Archive> lib) throws Exception {
|
||||
lib.add(0, filterArchive(archive));
|
||||
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
||||
archives.add(0, getFilteredArchive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the specified WAR file to exclude elements that should not appear on the
|
||||
* classpath.
|
||||
* @param archive the source archive
|
||||
* @return the filtered archive
|
||||
* @throws IOException on error
|
||||
*/
|
||||
protected Archive filterArchive(Archive archive) throws IOException {
|
||||
return archive.getFilteredArchive(new Archive.EntryFilter() {
|
||||
|
||||
protected Archive getFilteredArchive() throws IOException {
|
||||
return getArchive().getFilteredArchive(new Archive.EntryRenameFilter() {
|
||||
@Override
|
||||
public String apply(String entryName, Archive.Entry entry) {
|
||||
if (entryName.startsWith("META-INF/") || entryName.startsWith("WEB-INF/")) {
|
||||
|
|
@ -68,4 +64,7 @@ public class WarLauncher extends AbstractLauncher {
|
|||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new WarLauncher().launch(args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,48 +14,67 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
package org.springframework.boot.loader.archive;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.springframework.boot.loader.Launcher;
|
||||
|
||||
/**
|
||||
* An archive that can be launched by the {@link AbstractLauncher}.
|
||||
* An archive that can be launched by the {@link Launcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see JarFileArchive
|
||||
*/
|
||||
public interface Archive {
|
||||
|
||||
/**
|
||||
* Returns the manifest of the archive.
|
||||
* @return the manifest
|
||||
* @throws IOException
|
||||
*/
|
||||
Manifest getManifest() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns archive entries.
|
||||
* @return the archive entries
|
||||
*/
|
||||
Iterable<Entry> getEntries();
|
||||
public abstract class Archive {
|
||||
|
||||
/**
|
||||
* Returns a URL that can be used to load the archive.
|
||||
* @return the archive URL
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
URL getUrl() throws MalformedURLException;
|
||||
public abstract URL getUrl() throws MalformedURLException;
|
||||
|
||||
/**
|
||||
* Returns a nest archive from on the the contained entries.
|
||||
* @param entry the entry (may be a directory or file)
|
||||
* @return the nested archive
|
||||
* Obtain the main class that should be used to launch the application. By default
|
||||
* this method uses a {@code Start-Class} manifest entry.
|
||||
* @return the main class
|
||||
* @throws Exception
|
||||
*/
|
||||
public String getMainClass() throws Exception {
|
||||
String mainClass = getManifest().getMainAttributes().getValue("Start-Class");
|
||||
if (mainClass == null) {
|
||||
throw new IllegalStateException("No 'Start-Class' manifest entry specified");
|
||||
}
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the manifest of the archive.
|
||||
* @return the manifest
|
||||
* @throws IOException
|
||||
*/
|
||||
Archive getNestedArchive(Entry entry) throws IOException;
|
||||
public abstract Manifest getManifest() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns all entries from the archive.
|
||||
* @return the archive entries
|
||||
*/
|
||||
public abstract Collection<Entry> getEntries();
|
||||
|
||||
/**
|
||||
* Returns nested {@link Archive}s for entries that match the specified filter.
|
||||
* @param filter the filter used to limit entries
|
||||
* @return nested archives
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract List<Archive> getNestedArchives(EntryFilter filter)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a filtered version of the archive.
|
||||
|
|
@ -63,7 +82,8 @@ public interface Archive {
|
|||
* @return a filter archive
|
||||
* @throws IOException
|
||||
*/
|
||||
Archive getFilteredArchive(EntryFilter filter) throws IOException;
|
||||
public abstract Archive getFilteredArchive(EntryRenameFilter filter)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Represents a single entry in the archive.
|
||||
|
|
@ -85,10 +105,24 @@ public interface Archive {
|
|||
}
|
||||
|
||||
/**
|
||||
* A filter for archive entries.
|
||||
* Strategy interface to filter {@link Entry Entries}.
|
||||
*/
|
||||
public static interface EntryFilter {
|
||||
|
||||
/**
|
||||
* Apply the jar entry filter.
|
||||
* @param entry the entry to filter
|
||||
* @return {@code true} if the filter matches
|
||||
*/
|
||||
boolean matches(Entry entry);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy interface to filter or rename {@link Entry Entries}.
|
||||
*/
|
||||
public static interface EntryRenameFilter {
|
||||
|
||||
/**
|
||||
* Apply the jar entry filter.
|
||||
* @param entryName the current entry name. This may be different that the
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
package org.springframework.boot.loader.archive;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
|
@ -24,10 +24,13 @@ import java.net.MalformedURLException;
|
|||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Manifest;
|
||||
|
|
@ -37,7 +40,7 @@ import java.util.jar.Manifest;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ExplodedArchive implements Archive {
|
||||
public class ExplodedArchive extends Archive {
|
||||
|
||||
private static final Set<String> SKIPPED_NAMES = new HashSet<String>(Arrays.asList(
|
||||
".", ".."));
|
||||
|
|
@ -82,6 +85,12 @@ public class ExplodedArchive implements Archive {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
FilteredURLStreamHandler handler = new FilteredURLStreamHandler();
|
||||
return new URL("file", "", -1, this.root.getAbsolutePath() + "/", handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manifest getManifest() throws IOException {
|
||||
if (this.manifest == null && this.entries.containsKey(MANIFEST_ENTRY_NAME)) {
|
||||
|
|
@ -98,25 +107,28 @@ public class ExplodedArchive implements Archive {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Entry> getEntries() {
|
||||
return this.entries.values();
|
||||
public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {
|
||||
List<Archive> nestedArchives = new ArrayList<Archive>();
|
||||
for (Entry entry : getEntries()) {
|
||||
if (filter.matches(entry)) {
|
||||
nestedArchives.add(getNestedArchive(entry));
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(nestedArchives);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
FilteredURLStreamHandler handler = new FilteredURLStreamHandler();
|
||||
return new URL("file", "", -1, this.root.getAbsolutePath() + "/", handler);
|
||||
// return this.root.toURI().toURL();
|
||||
public Collection<Entry> getEntries() {
|
||||
return Collections.unmodifiableCollection(this.entries.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Archive getNestedArchive(Entry entry) throws IOException {
|
||||
protected Archive getNestedArchive(Entry entry) throws IOException {
|
||||
File file = ((FileEntry) entry).getFile();
|
||||
return (file.isDirectory() ? new ExplodedArchive(file) : new JarFileArchive(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Archive getFilteredArchive(EntryFilter filter) throws IOException {
|
||||
public Archive getFilteredArchive(EntryRenameFilter filter) throws IOException {
|
||||
Map<String, Entry> filteredEntries = new LinkedHashMap<String, Archive.Entry>();
|
||||
for (Map.Entry<String, Entry> entry : this.entries.entrySet()) {
|
||||
String filteredName = filter.apply(entry.getKey(), entry.getValue());
|
||||
|
|
@ -14,13 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
package org.springframework.boot.loader.archive;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
|
@ -35,7 +36,7 @@ import org.springframework.boot.loader.jar.RandomAccessJarFile;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JarFileArchive implements Archive {
|
||||
public class JarFileArchive extends Archive {
|
||||
|
||||
private final RandomAccessJarFile jarFile;
|
||||
|
||||
|
|
@ -55,30 +56,40 @@ public class JarFileArchive implements Archive {
|
|||
this.entries = Collections.unmodifiableList(jarFileEntries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manifest getManifest() throws IOException {
|
||||
return this.jarFile.getManifest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Entry> getEntries() {
|
||||
return this.entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
return this.jarFile.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Archive getNestedArchive(Entry entry) throws IOException {
|
||||
public Manifest getManifest() throws IOException {
|
||||
return this.jarFile.getManifest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {
|
||||
List<Archive> nestedArchives = new ArrayList<Archive>();
|
||||
for (Entry entry : getEntries()) {
|
||||
if (filter.matches(entry)) {
|
||||
nestedArchives.add(getNestedArchive(entry));
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(nestedArchives);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Entry> getEntries() {
|
||||
return Collections.unmodifiableCollection(this.entries);
|
||||
}
|
||||
|
||||
protected Archive getNestedArchive(Entry entry) throws IOException {
|
||||
JarEntry jarEntry = ((JarFileEntry) entry).getJarEntry();
|
||||
RandomAccessJarFile jarFile = this.jarFile.getNestedJarFile(jarEntry);
|
||||
return new JarFileArchive(jarFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Archive getFilteredArchive(final EntryFilter filter) throws IOException {
|
||||
public Archive getFilteredArchive(final EntryRenameFilter filter) throws IOException {
|
||||
RandomAccessJarFile filteredJar = this.jarFile
|
||||
.getFilteredJarFile(new JarEntryFilter() {
|
||||
@Override
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -14,13 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* Abstraction over logical Archives be they backed by a JAR file or unpacked into a
|
||||
* folder.
|
||||
*
|
||||
* @see org.springframework.boot.loader.archive.Archive
|
||||
*/
|
||||
public interface ArchiveFilter {
|
||||
package org.springframework.boot.loader.archive;
|
||||
|
||||
public boolean isArchive(Archive.Entry entry);
|
||||
|
||||
}
|
||||
|
|
@ -17,63 +17,72 @@
|
|||
package org.springframework.boot.loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertiesLauncher}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PropertiesLauncherTests {
|
||||
|
||||
private PropertiesLauncher launcher = new PropertiesLauncher();
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
System.setProperty("loader.home",
|
||||
new File("src/test/resources").getAbsolutePath());
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
System.clearProperty("loader.system");
|
||||
System.clearProperty("loader.home");
|
||||
System.clearProperty("loader.path");
|
||||
System.clearProperty("loader.main");
|
||||
System.clearProperty("loader.config.name");
|
||||
System.clearProperty("loader.config.location");
|
||||
System.clearProperty("loader.system");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultHome() {
|
||||
assertEquals(new File(System.getProperty("user.dir")),
|
||||
this.launcher.getHomeDirectory());
|
||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||
assertEquals(new File(System.getProperty("loader.home")),
|
||||
launcher.getHomeDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserSpecifiedMain() throws Exception {
|
||||
this.launcher.initialize(new File("."));
|
||||
assertEquals("demo.Application", this.launcher.getMainClass(null));
|
||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||
assertEquals("demo.Application", launcher.getMainClass());
|
||||
assertEquals(null, System.getProperty("loader.main"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserSpecifiedConfigName() throws Exception {
|
||||
|
||||
System.setProperty("loader.config.name", "foo");
|
||||
this.launcher.initialize(new File("."));
|
||||
assertEquals("my.Application", this.launcher.getMainClass(null));
|
||||
assertEquals("[etc/]", ReflectionTestUtils.getField(this.launcher, "paths")
|
||||
.toString());
|
||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||
assertEquals("my.Application", launcher.getMainClass());
|
||||
assertEquals("[etc/]", ReflectionTestUtils.getField(launcher, "paths").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemPropertySpecifiedMain() throws Exception {
|
||||
System.setProperty("loader.main", "foo.Bar");
|
||||
this.launcher.initialize(new File("."));
|
||||
assertEquals("foo.Bar", this.launcher.getMainClass(null));
|
||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||
assertEquals("foo.Bar", launcher.getMainClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemPropertiesSet() throws Exception {
|
||||
System.setProperty("loader.system", "true");
|
||||
this.launcher.initialize(new File("."));
|
||||
new PropertiesLauncher();
|
||||
assertEquals("demo.Application", System.getProperty("loader.main"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
package org.springframework.boot.loader.archive;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
|
@ -33,7 +33,10 @@ import org.junit.Before;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.boot.loader.Archive.Entry;
|
||||
import org.springframework.boot.loader.TestJarCreator;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.Archive.Entry;
|
||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
|
@ -126,7 +129,7 @@ public class ExplodedArchiveTests {
|
|||
@Test
|
||||
public void getFilteredArchive() throws Exception {
|
||||
Archive filteredArchive = this.archive
|
||||
.getFilteredArchive(new Archive.EntryFilter() {
|
||||
.getFilteredArchive(new Archive.EntryRenameFilter() {
|
||||
@Override
|
||||
public String apply(String entryName, Entry entry) {
|
||||
if (entryName.equals("1.dat")) {
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader;
|
||||
package org.springframework.boot.loader.archive;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
|
@ -25,9 +25,10 @@ import org.junit.Before;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.boot.loader.Archive;
|
||||
import org.springframework.boot.loader.JarFileArchive;
|
||||
import org.springframework.boot.loader.Archive.Entry;
|
||||
import org.springframework.boot.loader.TestJarCreator;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
import org.springframework.boot.loader.archive.Archive.Entry;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
|
@ -83,7 +84,7 @@ public class JarFileArchiveTests {
|
|||
@Test
|
||||
public void getFilteredArchive() throws Exception {
|
||||
Archive filteredArchive = this.archive
|
||||
.getFilteredArchive(new Archive.EntryFilter() {
|
||||
.getFilteredArchive(new Archive.EntryRenameFilter() {
|
||||
@Override
|
||||
public String apply(String entryName, Entry entry) {
|
||||
if (entryName.equals("1.dat")) {
|
||||
Loading…
Reference in New Issue