Optimize JarLauncher when used with exploded jar
- Previously, we would create a JarFileArchive for all nested jars. This was an additional overhead. We only need to create a JarFileArchive for jars that can have nested jars in them. For all other jars we only need the URL to build the classpath. - While iterating over nested entries in the exploded jar, we only need to look at BOOT-INF and we can skip any entry that does not match that. Closes gh-16655 Co-authored-by: Phillip Webb <pwebb@pivotal.io>
This commit is contained in:
parent
58022d72f5
commit
8f5777cf9e
|
@ -17,8 +17,8 @@
|
||||||
package org.springframework.boot.loader;
|
package org.springframework.boot.loader;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
|
@ -65,27 +65,64 @@ public abstract class ExecutableArchiveLauncher extends Launcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Archive> getClassPathArchives() throws Exception {
|
protected Iterator<Archive> getClassPathArchivesIterator() throws Exception {
|
||||||
List<Archive> archives = new ArrayList<>(this.archive.getNestedArchives(this::isNestedArchive));
|
Iterator<Archive> archives = this.archive.getNestedArchives(this::isSearchCandidate, this::isNestedArchive);
|
||||||
postProcessClassPathArchives(archives);
|
if (isPostProcessingClassPathArchives()) {
|
||||||
|
archives = applyClassPathArchivePostProcessing(archives);
|
||||||
|
}
|
||||||
return archives;
|
return archives;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Iterator<Archive> applyClassPathArchivePostProcessing(Iterator<Archive> archives) throws Exception {
|
||||||
|
List<Archive> list = new ArrayList<Archive>();
|
||||||
|
while (archives.hasNext()) {
|
||||||
|
list.add(archives.next());
|
||||||
|
}
|
||||||
|
postProcessClassPathArchives(list);
|
||||||
|
return list.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the specified {@link JarEntry} is a nested item that should be added
|
* Determine if the specified entry is a a candidate for further searching.
|
||||||
* to the classpath. The method is called once for each entry.
|
* @param entry the entry to check
|
||||||
* @param entry the jar entry
|
* @return {@code true} if the entry is a candidate for further searching
|
||||||
|
*/
|
||||||
|
protected boolean isSearchCandidate(Archive.Entry entry) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the specified entry is a nested item that should be added to the
|
||||||
|
* classpath.
|
||||||
|
* @param entry the entry to check
|
||||||
* @return {@code true} if the entry is a nested item (jar or folder)
|
* @return {@code true} if the entry is a nested item (jar or folder)
|
||||||
*/
|
*/
|
||||||
protected abstract boolean isNestedArchive(Archive.Entry entry);
|
protected abstract boolean isNestedArchive(Archive.Entry entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if post processing needs to be applied to the archives. For back
|
||||||
|
* compatibility this method returns {@true}, but subclasses that don't override
|
||||||
|
* {@link #postProcessClassPathArchives(List)} should provide an implementation that
|
||||||
|
* returns {@code false}.
|
||||||
|
* @return if the {@link #postProcessClassPathArchives(List)} method is implemented
|
||||||
|
*/
|
||||||
|
protected boolean isPostProcessingClassPathArchives() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to post-process archive entries before they are used. Implementations can
|
* Called to post-process archive entries before they are used. Implementations can
|
||||||
* add and remove entries.
|
* add and remove entries.
|
||||||
* @param archives the archives
|
* @param archives the archives
|
||||||
* @throws Exception if the post processing fails
|
* @throws Exception if the post processing fails
|
||||||
|
* @see #isPostProcessingClassPathArchives()
|
||||||
*/
|
*/
|
||||||
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsNestedJars() {
|
||||||
|
return this.archive.supportsNestedJars();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.loader;
|
package org.springframework.boot.loader;
|
||||||
|
|
||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
|
import org.springframework.boot.loader.archive.Archive.EntryFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
|
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
|
||||||
|
@ -29,9 +30,12 @@ import org.springframework.boot.loader.archive.Archive;
|
||||||
*/
|
*/
|
||||||
public class JarLauncher extends ExecutableArchiveLauncher {
|
public class JarLauncher extends ExecutableArchiveLauncher {
|
||||||
|
|
||||||
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
|
static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
|
return entry.getName().equals("BOOT-INF/classes/");
|
||||||
|
}
|
||||||
|
return entry.getName().startsWith("BOOT-INF/lib/");
|
||||||
|
};
|
||||||
|
|
||||||
public JarLauncher() {
|
public JarLauncher() {
|
||||||
}
|
}
|
||||||
|
@ -40,12 +44,19 @@ public class JarLauncher extends ExecutableArchiveLauncher {
|
||||||
super(archive);
|
super(archive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isPostProcessingClassPathArchives() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSearchCandidate(Archive.Entry entry) {
|
||||||
|
return entry.getName().startsWith("BOOT-INF/");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isNestedArchive(Archive.Entry entry) {
|
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||||
if (entry.isDirectory()) {
|
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
|
||||||
return entry.getName().equals(BOOT_INF_CLASSES);
|
|
||||||
}
|
|
||||||
return entry.getName().startsWith(BOOT_INF_LIB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
|
@ -19,9 +19,11 @@ package org.springframework.boot.loader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
|
@ -46,8 +48,10 @@ public abstract class Launcher {
|
||||||
* @throws Exception if the application fails to launch
|
* @throws Exception if the application fails to launch
|
||||||
*/
|
*/
|
||||||
protected void launch(String[] args) throws Exception {
|
protected void launch(String[] args) throws Exception {
|
||||||
JarFile.registerUrlProtocolHandler();
|
if (supportsNestedJars()) {
|
||||||
ClassLoader classLoader = createClassLoader(getClassPathArchives());
|
JarFile.registerUrlProtocolHandler();
|
||||||
|
}
|
||||||
|
ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
|
||||||
launch(args, getMainClass(), classLoader);
|
launch(args, getMainClass(), classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +60,24 @@ public abstract class Launcher {
|
||||||
* @param archives the archives
|
* @param archives the archives
|
||||||
* @return the classloader
|
* @return the classloader
|
||||||
* @throws Exception if the classloader cannot be created
|
* @throws Exception if the classloader cannot be created
|
||||||
|
* @deprecated since 2.3.0 in favor of {@link #createClassLoader(Iterator)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
|
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
|
||||||
List<URL> urls = new ArrayList<>(archives.size());
|
return createClassLoader(archives.iterator());
|
||||||
for (Archive archive : archives) {
|
}
|
||||||
urls.add(archive.getUrl());
|
|
||||||
|
/**
|
||||||
|
* Create a classloader for the specified archives.
|
||||||
|
* @param archives the archives
|
||||||
|
* @return the classloader
|
||||||
|
* @throws Exception if the classloader cannot be created
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
protected ClassLoader createClassLoader(Iterator<Archive> archives) throws Exception {
|
||||||
|
List<URL> urls = new ArrayList<>(50);
|
||||||
|
while (archives.hasNext()) {
|
||||||
|
urls.add(archives.next().getUrl());
|
||||||
}
|
}
|
||||||
return createClassLoader(urls.toArray(new URL[0]));
|
return createClassLoader(urls.toArray(new URL[0]));
|
||||||
}
|
}
|
||||||
|
@ -72,7 +89,10 @@ public abstract class Launcher {
|
||||||
* @throws Exception if the classloader cannot be created
|
* @throws Exception if the classloader cannot be created
|
||||||
*/
|
*/
|
||||||
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
|
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
|
||||||
return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
|
if (supportsNestedJars()) {
|
||||||
|
return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
|
||||||
|
}
|
||||||
|
return new URLClassLoader(urls, getClass().getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,8 +129,23 @@ public abstract class Launcher {
|
||||||
* Returns the archives that will be used to construct the class path.
|
* Returns the archives that will be used to construct the class path.
|
||||||
* @return the class path archives
|
* @return the class path archives
|
||||||
* @throws Exception if the class path archives cannot be obtained
|
* @throws Exception if the class path archives cannot be obtained
|
||||||
|
* @since 2.3.0
|
||||||
*/
|
*/
|
||||||
protected abstract List<Archive> getClassPathArchives() throws Exception;
|
protected Iterator<Archive> getClassPathArchivesIterator() throws Exception {
|
||||||
|
return getClassPathArchives().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the archives that will be used to construct the class path.
|
||||||
|
* @return the class path archives
|
||||||
|
* @throws Exception if the class path archives cannot be obtained
|
||||||
|
* @deprecated since 2.3.0 in favor of implementing
|
||||||
|
* {@link #getClassPathArchivesIterator()}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
protected List<Archive> getClassPathArchives() throws Exception {
|
||||||
|
throw new IllegalStateException("Unexpected call to getClassPathArchives()");
|
||||||
|
}
|
||||||
|
|
||||||
protected final Archive createArchive() throws Exception {
|
protected final Archive createArchive() throws Exception {
|
||||||
ProtectionDomain protectionDomain = getClass().getProtectionDomain();
|
ProtectionDomain protectionDomain = getClass().getProtectionDomain();
|
||||||
|
@ -127,4 +162,14 @@ public abstract class Launcher {
|
||||||
return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
|
return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the launcher needs to support fully nested JARs. If this method returns
|
||||||
|
* {@code false} then only regular JARs are supported and the additional URL and
|
||||||
|
* ClassLoader support infrastructure will not be installed.
|
||||||
|
* @return if nested JARs are supported
|
||||||
|
*/
|
||||||
|
protected boolean supportsNestedJars() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.net.URLConnection;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -447,12 +448,12 @@ public class PropertiesLauncher extends Launcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Archive> getClassPathArchives() throws Exception {
|
protected Iterator<Archive> getClassPathArchivesIterator() throws Exception {
|
||||||
List<Archive> lib = new ArrayList<>();
|
List<Archive> lib = new ArrayList<>();
|
||||||
for (String path : this.paths) {
|
for (String path : this.paths) {
|
||||||
for (Archive archive : getClassPathArchives(path)) {
|
for (Archive archive : getClassPathArchives(path)) {
|
||||||
if (archive instanceof ExplodedArchive) {
|
if (archive instanceof ExplodedArchive) {
|
||||||
List<Archive> nested = new ArrayList<>(archive.getNestedArchives(new ArchiveEntryFilter()));
|
List<Archive> nested = asList(archive.getNestedArchives(null, new ArchiveEntryFilter()));
|
||||||
nested.add(0, archive);
|
nested.add(0, archive);
|
||||||
lib.addAll(nested);
|
lib.addAll(nested);
|
||||||
}
|
}
|
||||||
|
@ -462,7 +463,7 @@ public class PropertiesLauncher extends Launcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addNestedEntries(lib);
|
addNestedEntries(lib);
|
||||||
return lib;
|
return lib.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Archive> getClassPathArchives(String path) throws Exception {
|
private List<Archive> getClassPathArchives(String path) throws Exception {
|
||||||
|
@ -543,7 +544,7 @@ public class PropertiesLauncher extends Launcher {
|
||||||
root = "";
|
root = "";
|
||||||
}
|
}
|
||||||
EntryFilter filter = new PrefixMatchingArchiveFilter(root);
|
EntryFilter filter = new PrefixMatchingArchiveFilter(root);
|
||||||
List<Archive> archives = new ArrayList<>(parent.getNestedArchives(filter));
|
List<Archive> archives = asList(parent.getNestedArchives(null, filter));
|
||||||
if (("".equals(root) || ".".equals(root)) && !path.endsWith(".jar") && parent != this.parent) {
|
if (("".equals(root) || ".".equals(root)) && !path.endsWith(".jar") && parent != this.parent) {
|
||||||
// You can't find the root with an entry filter so it has to be added
|
// You can't find the root with an entry filter so it has to be added
|
||||||
// explicitly. But don't add the root of the parent archive.
|
// explicitly. But don't add the root of the parent archive.
|
||||||
|
@ -557,12 +558,10 @@ public class PropertiesLauncher extends Launcher {
|
||||||
// directories, meaning we are running from an executable JAR. We add nested
|
// directories, meaning we are running from an executable JAR. We add nested
|
||||||
// entries from there with low priority (i.e. at end).
|
// entries from there with low priority (i.e. at end).
|
||||||
try {
|
try {
|
||||||
lib.addAll(this.parent.getNestedArchives((entry) -> {
|
Iterator<Archive> archives = this.parent.getNestedArchives(null, JarLauncher.NESTED_ARCHIVE_ENTRY_FILTER);
|
||||||
if (entry.isDirectory()) {
|
while (archives.hasNext()) {
|
||||||
return entry.getName().equals(JarLauncher.BOOT_INF_CLASSES);
|
lib.add(archives.next());
|
||||||
}
|
}
|
||||||
return entry.getName().startsWith(JarLauncher.BOOT_INF_LIB);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
// Ignore
|
// Ignore
|
||||||
|
@ -591,6 +590,14 @@ public class PropertiesLauncher extends Launcher {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Archive> asList(Iterator<Archive> iterator) {
|
||||||
|
List<Archive> list = new ArrayList<Archive>();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
list.add(iterator.next());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
args = launcher.getArgs(args);
|
args = launcher.getArgs(args);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.loader;
|
package org.springframework.boot.loader;
|
||||||
|
|
||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
|
import org.springframework.boot.loader.archive.Archive.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Launcher} for WAR based archives. This launcher for standard WAR archives.
|
* {@link Launcher} for WAR based archives. This launcher for standard WAR archives.
|
||||||
|
@ -29,14 +30,6 @@ import org.springframework.boot.loader.archive.Archive;
|
||||||
*/
|
*/
|
||||||
public class WarLauncher extends ExecutableArchiveLauncher {
|
public class WarLauncher extends ExecutableArchiveLauncher {
|
||||||
|
|
||||||
private static final String WEB_INF = "WEB-INF/";
|
|
||||||
|
|
||||||
private static final String WEB_INF_CLASSES = WEB_INF + "classes/";
|
|
||||||
|
|
||||||
private static final String WEB_INF_LIB = WEB_INF + "lib/";
|
|
||||||
|
|
||||||
private static final String WEB_INF_LIB_PROVIDED = WEB_INF + "lib-provided/";
|
|
||||||
|
|
||||||
public WarLauncher() {
|
public WarLauncher() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,14 +37,22 @@ public class WarLauncher extends ExecutableArchiveLauncher {
|
||||||
super(archive);
|
super(archive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isPostProcessingClassPathArchives() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSearchCandidate(Entry entry) {
|
||||||
|
return entry.getName().startsWith("WEB-INF/");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNestedArchive(Archive.Entry entry) {
|
public boolean isNestedArchive(Archive.Entry entry) {
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
return entry.getName().equals(WEB_INF_CLASSES);
|
return entry.getName().equals("WEB-INF/classes/");
|
||||||
}
|
|
||||||
else {
|
|
||||||
return entry.getName().startsWith(WEB_INF_LIB) || entry.getName().startsWith(WEB_INF_LIB_PROVIDED);
|
|
||||||
}
|
}
|
||||||
|
return entry.getName().startsWith("WEB-INF/lib/") || entry.getName().startsWith("WEB-INF/lib-provided/");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.loader.archive;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
@ -47,13 +48,44 @@ public interface Archive extends Iterable<Archive.Entry>, AutoCloseable {
|
||||||
*/
|
*/
|
||||||
Manifest getManifest() throws IOException;
|
Manifest getManifest() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns nested {@link Archive}s for entries that match the specified filters.
|
||||||
|
* @param searchFilter filter used to limit when additional sub-entry searching is
|
||||||
|
* required or {@code null} if all entries should be considered.
|
||||||
|
* @param includeFilter filter used to determine which entries should be included in
|
||||||
|
* the result or {@code null} if all entries should be included
|
||||||
|
* @return the nested archives
|
||||||
|
* @throws IOException on IO error
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
default Iterator<Archive> getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter)
|
||||||
|
throws IOException {
|
||||||
|
EntryFilter combinedFilter = (entry) -> (searchFilter == null || searchFilter.matches(entry))
|
||||||
|
&& (includeFilter == null || includeFilter.matches(entry));
|
||||||
|
List<Archive> nestedArchives = getNestedArchives(combinedFilter);
|
||||||
|
return nestedArchives.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns nested {@link Archive}s for entries that match the specified filter.
|
* Returns nested {@link Archive}s for entries that match the specified filter.
|
||||||
* @param filter the filter used to limit entries
|
* @param filter the filter used to limit entries
|
||||||
* @return nested archives
|
* @return nested archives
|
||||||
* @throws IOException if nested archives cannot be read
|
* @throws IOException if nested archives cannot be read
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #getNestedArchives(EntryFilter, EntryFilter)}
|
||||||
*/
|
*/
|
||||||
List<Archive> getNestedArchives(EntryFilter filter) throws IOException;
|
@Deprecated
|
||||||
|
default List<Archive> getNestedArchives(EntryFilter filter) throws IOException {
|
||||||
|
throw new IllegalStateException("Unexpected call to getNestedArchives(filter)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
Iterator<Entry> iterator();
|
||||||
|
|
||||||
|
default boolean supportsNestedJars() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the {@code Archive}, releasing any open resources.
|
* Closes the {@code Archive}, releasing any open resources.
|
||||||
|
@ -87,6 +119,7 @@ public interface Archive extends Iterable<Archive.Entry>, AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Strategy interface to filter {@link Entry Entries}.
|
* Strategy interface to filter {@link Entry Entries}.
|
||||||
*/
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
interface EntryFilter {
|
interface EntryFilter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,8 +20,8 @@ import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -29,7 +29,6 @@ import java.util.Deque;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
@ -39,6 +38,7 @@ import java.util.jar.Manifest;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
|
* @author Madhura Bhave
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
public class ExplodedArchive implements Archive {
|
public class ExplodedArchive implements Archive {
|
||||||
|
@ -99,24 +99,23 @@ public class ExplodedArchive implements Archive {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {
|
public Iterator<Archive> getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException {
|
||||||
List<Archive> nestedArchives = new ArrayList<>();
|
return new ArchiveIterator(this.root, this.recursive, searchFilter, includeFilter);
|
||||||
for (Entry entry : this) {
|
|
||||||
if (filter.matches(entry)) {
|
|
||||||
nestedArchives.add(getNestedArchive(entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(nestedArchives);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Entry> iterator() {
|
public Iterator<Entry> iterator() {
|
||||||
return new FileEntryIterator(this.root, this.recursive);
|
return new EntryIterator(this.root, this.recursive, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Archive getNestedArchive(Entry entry) throws IOException {
|
protected Archive getNestedArchive(Entry entry) throws IOException {
|
||||||
File file = ((FileEntry) entry).getFile();
|
File file = ((FileEntry) entry).getFile();
|
||||||
return (file.isDirectory() ? new ExplodedArchive(file) : new JarFileArchive(file));
|
return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive((FileEntry) entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsNestedJars() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -132,21 +131,30 @@ public class ExplodedArchive implements Archive {
|
||||||
/**
|
/**
|
||||||
* File based {@link Entry} {@link Iterator}.
|
* File based {@link Entry} {@link Iterator}.
|
||||||
*/
|
*/
|
||||||
private static class FileEntryIterator implements Iterator<Entry> {
|
private abstract static class AbstractIterator<T> implements Iterator<T> {
|
||||||
|
|
||||||
private final Comparator<File> entryComparator = new EntryComparator();
|
private static final Comparator<File> entryComparator = Comparator.comparing(File::getAbsolutePath);
|
||||||
|
|
||||||
private final File root;
|
private final File root;
|
||||||
|
|
||||||
private final boolean recursive;
|
private final boolean recursive;
|
||||||
|
|
||||||
|
private final EntryFilter searchFilter;
|
||||||
|
|
||||||
|
private final EntryFilter includeFilter;
|
||||||
|
|
||||||
private final Deque<Iterator<File>> stack = new LinkedList<>();
|
private final Deque<Iterator<File>> stack = new LinkedList<>();
|
||||||
|
|
||||||
private File current;
|
private FileEntry current;
|
||||||
|
|
||||||
FileEntryIterator(File root, boolean recursive) {
|
private String rootUrl;
|
||||||
|
|
||||||
|
AbstractIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
this.rootUrl = this.root.toURI().getPath();
|
||||||
this.recursive = recursive;
|
this.recursive = recursive;
|
||||||
|
this.searchFilter = searchFilter;
|
||||||
|
this.includeFilter = includeFilter;
|
||||||
this.stack.add(listFiles(root));
|
this.stack.add(listFiles(root));
|
||||||
this.current = poll();
|
this.current = poll();
|
||||||
}
|
}
|
||||||
|
@ -157,34 +165,28 @@ public class ExplodedArchive implements Archive {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry next() {
|
public T next() {
|
||||||
if (this.current == null) {
|
FileEntry entry = this.current;
|
||||||
|
if (entry == null) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
File file = this.current;
|
|
||||||
if (file.isDirectory() && (this.recursive || file.getParentFile().equals(this.root))) {
|
|
||||||
this.stack.addFirst(listFiles(file));
|
|
||||||
}
|
|
||||||
this.current = poll();
|
this.current = poll();
|
||||||
String name = file.toURI().getPath().substring(this.root.toURI().getPath().length());
|
return adapt(entry);
|
||||||
return new FileEntry(name, file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Iterator<File> listFiles(File file) {
|
private FileEntry poll() {
|
||||||
File[] files = file.listFiles();
|
|
||||||
if (files == null) {
|
|
||||||
return Collections.emptyIterator();
|
|
||||||
}
|
|
||||||
Arrays.sort(files, this.entryComparator);
|
|
||||||
return Arrays.asList(files).iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
private File poll() {
|
|
||||||
while (!this.stack.isEmpty()) {
|
while (!this.stack.isEmpty()) {
|
||||||
while (this.stack.peek().hasNext()) {
|
while (this.stack.peek().hasNext()) {
|
||||||
File file = this.stack.peek().next();
|
File file = this.stack.peek().next();
|
||||||
if (!SKIPPED_NAMES.contains(file.getName())) {
|
if (SKIPPED_NAMES.contains(file.getName())) {
|
||||||
return file;
|
continue;
|
||||||
|
}
|
||||||
|
FileEntry entry = getFileEntry(file);
|
||||||
|
if (isListable(entry)) {
|
||||||
|
this.stack.addFirst(listFiles(file));
|
||||||
|
}
|
||||||
|
if (this.includeFilter == null || this.includeFilter.matches(entry)) {
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.stack.poll();
|
this.stack.poll();
|
||||||
|
@ -192,21 +194,64 @@ public class ExplodedArchive implements Archive {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FileEntry getFileEntry(File file) {
|
||||||
|
URI uri = file.toURI();
|
||||||
|
String name = uri.getPath().substring(this.rootUrl.length());
|
||||||
|
try {
|
||||||
|
return new FileEntry(name, file, uri.toURL());
|
||||||
|
}
|
||||||
|
catch (MalformedURLException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isListable(FileEntry entry) {
|
||||||
|
return entry.isDirectory() && (this.recursive || entry.getFile().getParentFile().equals(this.root))
|
||||||
|
&& (this.searchFilter == null || this.searchFilter.matches(entry))
|
||||||
|
&& (this.includeFilter == null || !this.includeFilter.matches(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterator<File> listFiles(File file) {
|
||||||
|
File[] files = file.listFiles();
|
||||||
|
if (files == null) {
|
||||||
|
return Collections.emptyIterator();
|
||||||
|
}
|
||||||
|
Arrays.sort(files, entryComparator);
|
||||||
|
return Arrays.asList(files).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException("remove");
|
throw new UnsupportedOperationException("remove");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected abstract T adapt(FileEntry entry);
|
||||||
* {@link Comparator} that orders {@link File} entries by their absolute paths.
|
|
||||||
*/
|
|
||||||
private static class EntryComparator implements Comparator<File> {
|
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public int compare(File o1, File o2) {
|
|
||||||
return o1.getAbsolutePath().compareTo(o2.getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private static class EntryIterator extends AbstractIterator<Entry> {
|
||||||
|
|
||||||
|
EntryIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) {
|
||||||
|
super(root, recursive, searchFilter, includeFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Entry adapt(FileEntry entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ArchiveIterator extends AbstractIterator<Archive> {
|
||||||
|
|
||||||
|
ArchiveIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) {
|
||||||
|
super(root, recursive, searchFilter, includeFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Archive adapt(FileEntry entry) {
|
||||||
|
File file = entry.getFile();
|
||||||
|
return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -220,9 +265,12 @@ public class ExplodedArchive implements Archive {
|
||||||
|
|
||||||
private final File file;
|
private final File file;
|
||||||
|
|
||||||
FileEntry(String name, File file) {
|
private final URL url;
|
||||||
|
|
||||||
|
FileEntry(String name, File file, URL url) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
File getFile() {
|
File getFile() {
|
||||||
|
@ -239,6 +287,55 @@ public class ExplodedArchive implements Archive {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
URL getUrl() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Archive} implementation backed by a simple JAR file that doesn't itself
|
||||||
|
* contain nested archives.
|
||||||
|
*/
|
||||||
|
private static class SimpleJarFileArchive implements Archive {
|
||||||
|
|
||||||
|
private final URL url;
|
||||||
|
|
||||||
|
SimpleJarFileArchive(FileEntry file) {
|
||||||
|
this.url = file.getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getUrl() throws MalformedURLException {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Manifest getManifest() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Archive> getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter)
|
||||||
|
throws IOException {
|
||||||
|
return Collections.emptyIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry> iterator() {
|
||||||
|
return Collections.emptyIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
try {
|
||||||
|
return getUrl().toString();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return "jar archive";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,7 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
@ -80,19 +76,13 @@ public class JarFileArchive implements Archive {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {
|
public Iterator<Archive> getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException {
|
||||||
List<Archive> nestedArchives = new ArrayList<>();
|
return new NestedArchiveIterator(this.jarFile.iterator(), searchFilter, includeFilter);
|
||||||
for (Entry entry : this) {
|
|
||||||
if (filter.matches(entry)) {
|
|
||||||
nestedArchives.add(getNestedArchive(entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(nestedArchives);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Entry> iterator() {
|
public Iterator<Entry> iterator() {
|
||||||
return new EntryIterator(this.jarFile.entries());
|
return new EntryIterator(this.jarFile.iterator(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -169,29 +159,85 @@ public class JarFileArchive implements Archive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Archive.Entry} iterator implementation backed by {@link JarEntry}.
|
* Abstract base class for iterator implementations.
|
||||||
*/
|
*/
|
||||||
private static class EntryIterator implements Iterator<Entry> {
|
private abstract static class AbstractIterator<T> implements Iterator<T> {
|
||||||
|
|
||||||
private final Enumeration<JarEntry> enumeration;
|
private final Iterator<JarEntry> iterator;
|
||||||
|
|
||||||
EntryIterator(Enumeration<JarEntry> enumeration) {
|
private final EntryFilter searchFilter;
|
||||||
this.enumeration = enumeration;
|
|
||||||
|
private final EntryFilter includeFilter;
|
||||||
|
|
||||||
|
private Entry current;
|
||||||
|
|
||||||
|
AbstractIterator(Iterator<JarEntry> iterator, EntryFilter searchFilter, EntryFilter includeFilter) {
|
||||||
|
this.iterator = iterator;
|
||||||
|
this.searchFilter = searchFilter;
|
||||||
|
this.includeFilter = includeFilter;
|
||||||
|
this.current = poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return this.enumeration.hasMoreElements();
|
return this.current != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry next() {
|
public T next() {
|
||||||
return new JarFileEntry(this.enumeration.nextElement());
|
T result = adapt(this.current);
|
||||||
|
this.current = poll();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Entry poll() {
|
||||||
|
while (this.iterator.hasNext()) {
|
||||||
|
JarFileEntry candidate = new JarFileEntry(this.iterator.next());
|
||||||
|
if ((this.searchFilter == null || this.searchFilter.matches(candidate))
|
||||||
|
&& (this.includeFilter == null || this.includeFilter.matches(candidate))) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T adapt(Entry entry);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Archive.Entry} iterator implementation backed by {@link JarEntry}.
|
||||||
|
*/
|
||||||
|
private static class EntryIterator extends AbstractIterator<Entry> {
|
||||||
|
|
||||||
|
EntryIterator(Iterator<JarEntry> iterator, EntryFilter searchFilter, EntryFilter includeFilter) {
|
||||||
|
super(iterator, searchFilter, includeFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
protected Entry adapt(Entry entry) {
|
||||||
throw new UnsupportedOperationException("remove");
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nested {@link Archive} iterator implementation backed by {@link JarEntry}.
|
||||||
|
*/
|
||||||
|
private class NestedArchiveIterator extends AbstractIterator<Archive> {
|
||||||
|
|
||||||
|
NestedArchiveIterator(Iterator<JarEntry> iterator, EntryFilter searchFilter, EntryFilter includeFilter) {
|
||||||
|
super(iterator, searchFilter, includeFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Archive adapt(Entry entry) {
|
||||||
|
try {
|
||||||
|
return getNestedArchive(entry);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ import org.springframework.boot.loader.data.RandomAccessDataFile;
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
public class JarFile extends java.util.jar.JarFile {
|
public class JarFile extends java.util.jar.JarFile implements Iterable<java.util.jar.JarEntry> {
|
||||||
|
|
||||||
private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
|
private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ public class JarFile extends java.util.jar.JarFile {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enumeration<java.util.jar.JarEntry> entries() {
|
public Enumeration<java.util.jar.JarEntry> entries() {
|
||||||
final Iterator<JarEntry> iterator = this.entries.iterator();
|
Iterator<java.util.jar.JarEntry> iterator = iterator();
|
||||||
return new Enumeration<java.util.jar.JarEntry>() {
|
return new Enumeration<java.util.jar.JarEntry>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -207,6 +207,17 @@ public class JarFile extends java.util.jar.JarFile {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an iterator for the contained entries.
|
||||||
|
* @see java.lang.Iterable#iterator()
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public Iterator<java.util.jar.JarEntry> iterator() {
|
||||||
|
return (Iterator) this.entries.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
public JarEntry getJarEntry(CharSequence name) {
|
public JarEntry getJarEntry(CharSequence name) {
|
||||||
return this.entries.getEntry(name);
|
return this.entries.getEntry(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.codehaus.plexus.util.CollectionUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
|
@ -39,7 +40,7 @@ class JarLauncherTests extends AbstractExecutableArchiveLauncherTests {
|
||||||
void explodedJarHasOnlyBootInfClassesAndContentsOfBootInfLibOnClasspath() throws Exception {
|
void explodedJarHasOnlyBootInfClassesAndContentsOfBootInfLibOnClasspath() throws Exception {
|
||||||
File explodedRoot = explode(createJarArchive("archive.jar", "BOOT-INF"));
|
File explodedRoot = explode(createJarArchive("archive.jar", "BOOT-INF"));
|
||||||
JarLauncher launcher = new JarLauncher(new ExplodedArchive(explodedRoot, true));
|
JarLauncher launcher = new JarLauncher(new ExplodedArchive(explodedRoot, true));
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).hasSize(2);
|
assertThat(archives).hasSize(2);
|
||||||
assertThat(getUrls(archives)).containsOnly(new File(explodedRoot, "BOOT-INF/classes").toURI().toURL(),
|
assertThat(getUrls(archives)).containsOnly(new File(explodedRoot, "BOOT-INF/classes").toURI().toURL(),
|
||||||
new File(explodedRoot, "BOOT-INF/lib/foo.jar").toURI().toURL());
|
new File(explodedRoot, "BOOT-INF/lib/foo.jar").toURI().toURL());
|
||||||
|
@ -53,7 +54,7 @@ class JarLauncherTests extends AbstractExecutableArchiveLauncherTests {
|
||||||
File jarRoot = createJarArchive("archive.jar", "BOOT-INF");
|
File jarRoot = createJarArchive("archive.jar", "BOOT-INF");
|
||||||
try (JarFileArchive archive = new JarFileArchive(jarRoot)) {
|
try (JarFileArchive archive = new JarFileArchive(jarRoot)) {
|
||||||
JarLauncher launcher = new JarLauncher(archive);
|
JarLauncher launcher = new JarLauncher(archive);
|
||||||
List<Archive> classPathArchives = launcher.getClassPathArchives();
|
List<Archive> classPathArchives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(classPathArchives).hasSize(2);
|
assertThat(classPathArchives).hasSize(2);
|
||||||
assertThat(getUrls(classPathArchives)).containsOnly(
|
assertThat(getUrls(classPathArchives)).containsOnly(
|
||||||
new URL("jar:" + jarRoot.toURI().toURL() + "!/BOOT-INF/classes!/"),
|
new URL("jar:" + jarRoot.toURI().toURL() + "!/BOOT-INF/classes!/"),
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.util.jar.Manifest;
|
||||||
|
|
||||||
import org.assertj.core.api.Condition;
|
import org.assertj.core.api.Condition;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
|
import org.codehaus.plexus.util.CollectionUtils;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -139,7 +140,7 @@ class PropertiesLauncherTests {
|
||||||
System.setProperty("loader.path", "jars/");
|
System.setProperty("loader.path", "jars/");
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString()).isEqualTo("[jars/]");
|
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString()).isEqualTo("[jars/]");
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +170,7 @@ class PropertiesLauncherTests {
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
|
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
|
||||||
.isEqualTo("[jar:file:./src/test/resources/nested-jars/app.jar!/]");
|
.isEqualTo("[jar:file:./src/test/resources/nested-jars/app.jar!/]");
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
||||||
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
||||||
}
|
}
|
||||||
|
@ -178,7 +179,7 @@ class PropertiesLauncherTests {
|
||||||
void testUserSpecifiedRootOfJarPathWithDot() throws Exception {
|
void testUserSpecifiedRootOfJarPathWithDot() throws Exception {
|
||||||
System.setProperty("loader.path", "nested-jars/app.jar!/./");
|
System.setProperty("loader.path", "nested-jars/app.jar!/./");
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
||||||
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
||||||
}
|
}
|
||||||
|
@ -187,7 +188,7 @@ class PropertiesLauncherTests {
|
||||||
void testUserSpecifiedRootOfJarPathWithDotAndJarPrefix() throws Exception {
|
void testUserSpecifiedRootOfJarPathWithDotAndJarPrefix() throws Exception {
|
||||||
System.setProperty("loader.path", "jar:file:./src/test/resources/nested-jars/app.jar!/./");
|
System.setProperty("loader.path", "jar:file:./src/test/resources/nested-jars/app.jar!/./");
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ class PropertiesLauncherTests {
|
||||||
System.setProperty("loader.path", "nested-jars/app.jar");
|
System.setProperty("loader.path", "nested-jars/app.jar");
|
||||||
System.setProperty("loader.main", "demo.Application");
|
System.setProperty("loader.main", "demo.Application");
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
|
||||||
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
assertThat(archives).areExactly(1, endingWith("app.jar"));
|
||||||
}
|
}
|
||||||
|
@ -206,7 +207,7 @@ class PropertiesLauncherTests {
|
||||||
System.setProperty("loader.path", "nested-jars/app.jar!/foo.jar");
|
System.setProperty("loader.path", "nested-jars/app.jar!/foo.jar");
|
||||||
System.setProperty("loader.main", "demo.Application");
|
System.setProperty("loader.main", "demo.Application");
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).hasSize(1).areExactly(1, endingWith("foo.jar!/"));
|
assertThat(archives).hasSize(1).areExactly(1, endingWith("foo.jar!/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +336,7 @@ class PropertiesLauncherTests {
|
||||||
loaderPath.mkdir();
|
loaderPath.mkdir();
|
||||||
System.setProperty("loader.path", loaderPath.toURI().toURL().toString());
|
System.setProperty("loader.path", loaderPath.toURI().toURL().toString());
|
||||||
PropertiesLauncher launcher = new PropertiesLauncher();
|
PropertiesLauncher launcher = new PropertiesLauncher();
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives.size()).isEqualTo(1);
|
assertThat(archives.size()).isEqualTo(1);
|
||||||
File archiveRoot = (File) ReflectionTestUtils.getField(archives.get(0), "root");
|
File archiveRoot = (File) ReflectionTestUtils.getField(archives.get(0), "root");
|
||||||
assertThat(archiveRoot).isEqualTo(loaderPath);
|
assertThat(archiveRoot).isEqualTo(loaderPath);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.codehaus.plexus.util.CollectionUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.boot.loader.archive.Archive;
|
import org.springframework.boot.loader.archive.Archive;
|
||||||
|
@ -39,7 +40,7 @@ class WarLauncherTests extends AbstractExecutableArchiveLauncherTests {
|
||||||
void explodedWarHasOnlyWebInfClassesAndContentsOfWebInfLibOnClasspath() throws Exception {
|
void explodedWarHasOnlyWebInfClassesAndContentsOfWebInfLibOnClasspath() throws Exception {
|
||||||
File explodedRoot = explode(createJarArchive("archive.war", "WEB-INF"));
|
File explodedRoot = explode(createJarArchive("archive.war", "WEB-INF"));
|
||||||
WarLauncher launcher = new WarLauncher(new ExplodedArchive(explodedRoot, true));
|
WarLauncher launcher = new WarLauncher(new ExplodedArchive(explodedRoot, true));
|
||||||
List<Archive> archives = launcher.getClassPathArchives();
|
List<Archive> archives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(archives).hasSize(2);
|
assertThat(archives).hasSize(2);
|
||||||
assertThat(getUrls(archives)).containsOnly(new File(explodedRoot, "WEB-INF/classes").toURI().toURL(),
|
assertThat(getUrls(archives)).containsOnly(new File(explodedRoot, "WEB-INF/classes").toURI().toURL(),
|
||||||
new File(explodedRoot, "WEB-INF/lib/foo.jar").toURI().toURL());
|
new File(explodedRoot, "WEB-INF/lib/foo.jar").toURI().toURL());
|
||||||
|
@ -53,7 +54,7 @@ class WarLauncherTests extends AbstractExecutableArchiveLauncherTests {
|
||||||
File jarRoot = createJarArchive("archive.war", "WEB-INF");
|
File jarRoot = createJarArchive("archive.war", "WEB-INF");
|
||||||
try (JarFileArchive archive = new JarFileArchive(jarRoot)) {
|
try (JarFileArchive archive = new JarFileArchive(jarRoot)) {
|
||||||
WarLauncher launcher = new WarLauncher(archive);
|
WarLauncher launcher = new WarLauncher(archive);
|
||||||
List<Archive> classPathArchives = launcher.getClassPathArchives();
|
List<Archive> classPathArchives = CollectionUtils.iteratorToList(launcher.getClassPathArchivesIterator());
|
||||||
assertThat(classPathArchives).hasSize(2);
|
assertThat(classPathArchives).hasSize(2);
|
||||||
assertThat(getUrls(classPathArchives)).containsOnly(
|
assertThat(getUrls(classPathArchives)).containsOnly(
|
||||||
new URL("jar:" + jarRoot.toURI().toURL() + "!/WEB-INF/classes!/"),
|
new URL("jar:" + jarRoot.toURI().toURL() + "!/WEB-INF/classes!/"),
|
||||||
|
|
|
@ -74,7 +74,6 @@ class ExplodedArchiveTests {
|
||||||
private void createArchive(String folderName) throws Exception {
|
private void createArchive(String folderName) throws Exception {
|
||||||
File file = new File(this.tempDir, "test.jar");
|
File file = new File(this.tempDir, "test.jar");
|
||||||
TestJarCreator.createTestJar(file);
|
TestJarCreator.createTestJar(file);
|
||||||
|
|
||||||
this.rootFolder = (StringUtils.hasText(folderName) ? new File(this.tempDir, folderName)
|
this.rootFolder = (StringUtils.hasText(folderName) ? new File(this.tempDir, folderName)
|
||||||
: new File(this.tempDir, UUID.randomUUID().toString()));
|
: new File(this.tempDir, UUID.randomUUID().toString()));
|
||||||
JarFile jarFile = new JarFile(file);
|
JarFile jarFile = new JarFile(file);
|
||||||
|
@ -102,7 +101,7 @@ class ExplodedArchiveTests {
|
||||||
@Test
|
@Test
|
||||||
void getEntries() {
|
void getEntries() {
|
||||||
Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
|
Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
|
||||||
assertThat(entries.size()).isEqualTo(12);
|
assertThat(entries).hasSize(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -121,7 +120,7 @@ class ExplodedArchiveTests {
|
||||||
Entry entry = getEntriesMap(this.archive).get("nested.jar");
|
Entry entry = getEntriesMap(this.archive).get("nested.jar");
|
||||||
Archive nested = this.archive.getNestedArchive(entry);
|
Archive nested = this.archive.getNestedArchive(entry);
|
||||||
assertThat(nested.getUrl().toString()).isEqualTo(this.rootFolder.toURI() + "nested.jar");
|
assertThat(nested.getUrl().toString()).isEqualTo(this.rootFolder.toURI() + "nested.jar");
|
||||||
((JarFileArchive) nested).close();
|
nested.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue