diff --git a/spring-boot-cli/pom.xml b/spring-boot-cli/pom.xml
index 7716e22a43f..e7ee726736e 100644
--- a/spring-boot-cli/pom.xml
+++ b/spring-boot-cli/pom.xml
@@ -220,7 +220,7 @@
copy-dependencies
- ${project.build.directory}/assembly/lib
+ ${project.build.directory}/assembly/BOOT-INF/lib
runtime
diff --git a/spring-boot-cli/src/main/assembly/jar-with-dependencies.xml b/spring-boot-cli/src/main/assembly/jar-with-dependencies.xml
index e58cf8cf218..b0687983c09 100644
--- a/spring-boot-cli/src/main/assembly/jar-with-dependencies.xml
+++ b/spring-boot-cli/src/main/assembly/jar-with-dependencies.xml
@@ -15,6 +15,7 @@
${project.groupId}:${project.artifactId}
true
+ BOOT-INF/classes/
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.java
index e05c6d12c9a..abc07ef8f22 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/archive/PackagedSpringApplicationLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -52,7 +52,7 @@ public final class PackagedSpringApplicationLauncher {
}
private Object[] getSources(URLClassLoader classLoader) throws Exception {
- Enumeration urls = classLoader.findResources("META-INF/MANIFEST.MF");
+ Enumeration urls = classLoader.getResources("META-INF/MANIFEST.MF");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Manifest manifest = new Manifest(url.openStream());
diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java
index ac83d90e310..b8296184e34 100644
--- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java
+++ b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/MultiProjectRepackagingTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -42,8 +42,9 @@ public class MultiProjectRepackagingTests {
File buildLibs = new File(
"target/multi-project-transitive-file-dependency/main/build/libs");
JarFile jarFile = new JarFile(new File(buildLibs, "main.jar"));
- assertThat(jarFile.getEntry("lib/commons-logging-1.1.3.jar")).isNotNull();
- assertThat(jarFile.getEntry("lib/foo.jar")).isNotNull();
+ assertThat(jarFile.getEntry("BOOT-INF/lib/commons-logging-1.1.3.jar"))
+ .isNotNull();
+ assertThat(jarFile.getEntry("BOOT-INF/lib/foo.jar")).isNotNull();
jarFile.close();
}
@@ -57,7 +58,7 @@ public class MultiProjectRepackagingTests {
"target/multi-project-common-file-dependency/build/libs");
JarFile jarFile = new JarFile(
new File(buildLibs, "multi-project-common-file-dependency.jar"));
- assertThat(jarFile.getEntry("lib/foo.jar")).isNotNull();
+ assertThat(jarFile.getEntry("BOOT-INF/lib/foo.jar")).isNotNull();
jarFile.close();
}
@@ -70,7 +71,7 @@ public class MultiProjectRepackagingTests {
File buildLibs = new File(
"target/multi-project-runtime-project-dependency/projectA/build/libs");
JarFile jarFile = new JarFile(new File(buildLibs, "projectA.jar"));
- assertThat(jarFile.getEntry("lib/projectB.jar")).isNotNull();
+ assertThat(jarFile.getEntry("BOOT-INF/lib/projectB.jar")).isNotNull();
jarFile.close();
}
diff --git a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java
index 87b62ccdaf2..d52cf179fcd 100644
--- a/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java
+++ b/spring-boot-integration-tests/spring-boot-gradle-tests/src/test/java/org/springframework/boot/gradle/RepackagingTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -144,7 +144,7 @@ public class RepackagingTests {
.run();
File buildLibs = new File("target/repackage/build/libs");
JarFile jarFile = new JarFile(new File(buildLibs, "repackage.jar"));
- assertThat(jarFile.getEntry("lib/foo.jar")).isNotNull();
+ assertThat(jarFile.getEntry("BOOT-INF/lib/foo.jar")).isNotNull();
jarFile.close();
}
@@ -166,7 +166,7 @@ public class RepackagingTests {
private boolean isDevToolsJarIncluded(File repackageFile) throws IOException {
JarFile jarFile = new JarFile(repackageFile);
try {
- String name = "lib/spring-boot-devtools-" + BOOT_VERSION + ".jar";
+ String name = "BOOT-INF/lib/spring-boot-devtools-" + BOOT_VERSION + ".jar";
return jarFile.getEntry(name) != null;
}
finally {
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
index 0643a4afc96..81c055a9039 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -120,6 +120,11 @@ public class JarWriter {
* @throws IOException if the entries cannot be written
*/
public void writeEntries(JarFile jarFile) throws IOException {
+ this.writeEntries(jarFile, new IdentityEntryTransformer());
+ }
+
+ void writeEntries(JarFile jarFile, EntryTransformer entryTransformer)
+ throws IOException {
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
@@ -133,7 +138,7 @@ public class JarWriter {
jarFile.getInputStream(entry));
}
EntryWriter entryWriter = new InputStreamEntryWriter(inputStream, true);
- writeEntry(entry, entryWriter);
+ writeEntry(entryTransformer.transform(entry), entryWriter);
}
finally {
inputStream.close();
@@ -377,4 +382,25 @@ public class JarWriter {
}
}
+ /**
+ * An {@code EntryTransformer} enables the transformation of {@link JarEntry jar
+ * entries} during the writing process.
+ */
+ interface EntryTransformer {
+
+ JarEntry transform(JarEntry jarEntry);
+ }
+
+ /**
+ * An {@code EntryTransformer} that returns the entry unchanged.
+ */
+ private static final class IdentityEntryTransformer implements EntryTransformer {
+
+ @Override
+ public JarEntry transform(JarEntry jarEntry) {
+ return jarEntry;
+ }
+
+ }
+
}
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
index 8eb4bf091ba..3cebdc7a05c 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2013 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -60,7 +60,7 @@ public final class Layouts {
/**
* Executable JAR layout.
*/
- public static class Jar implements Layout {
+ public static class Jar implements RepackagingLayout {
@Override
public String getLauncherClassName() {
@@ -69,7 +69,7 @@ public final class Layouts {
@Override
public String getLibraryDestination(String libraryName, LibraryScope scope) {
- return "lib/";
+ return "BOOT-INF/lib/";
}
@Override
@@ -77,6 +77,11 @@ public final class Layouts {
return "";
}
+ @Override
+ public String getRepackagedClassesLocation() {
+ return "BOOT-INF/classes/";
+ }
+
@Override
public boolean isExecutable() {
return true;
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
index bbe27db7e42..c9c59376ad4 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -24,9 +24,12 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import org.springframework.boot.loader.tools.JarWriter.EntryTransformer;
+
/**
* Utility class that can be used to repackage an archive so that it can be executed using
* '{@literal java -jar}'.
@@ -189,7 +192,14 @@ public class Repackager {
writer.writeManifest(buildManifest(sourceJar));
Set seen = new HashSet();
writeNestedLibraries(unpackLibraries, seen, writer);
- writer.writeEntries(sourceJar);
+ if (this.layout instanceof RepackagingLayout) {
+ writer.writeEntries(sourceJar,
+ new RenamingEntryTransformer(((RepackagingLayout) this.layout)
+ .getRepackagedClassesLocation()));
+ }
+ else {
+ writer.writeEntries(sourceJar);
+ }
writeNestedLibraries(standardLibraries, seen, writer);
if (this.layout.isExecutable()) {
writer.writeLoaderClasses();
@@ -293,4 +303,47 @@ public class Repackager {
}
}
+ /**
+ * An {@code EntryTransformer} that renames entries by applying a prefix.
+ */
+ private static final class RenamingEntryTransformer implements EntryTransformer {
+
+ private final String namePrefix;
+
+ private RenamingEntryTransformer(String namePrefix) {
+ this.namePrefix = namePrefix;
+ }
+
+ @Override
+ public JarEntry transform(JarEntry entry) {
+ if (entry.getName().startsWith("META-INF/")
+ || entry.getName().startsWith("BOOT-INF/")) {
+ return entry;
+ }
+ JarEntry renamedEntry = new JarEntry(this.namePrefix + entry.getName());
+ renamedEntry.setTime(entry.getTime());
+ renamedEntry.setSize(entry.getSize());
+ renamedEntry.setMethod(entry.getMethod());
+ if (entry.getComment() != null) {
+ renamedEntry.setComment(entry.getComment());
+ }
+ renamedEntry.setCompressedSize(entry.getCompressedSize());
+ renamedEntry.setCrc(entry.getCrc());
+ if (entry.getCreationTime() != null) {
+ renamedEntry.setCreationTime(entry.getCreationTime());
+ }
+ if (entry.getExtra() != null) {
+ renamedEntry.setExtra(entry.getExtra());
+ }
+ if (entry.getLastAccessTime() != null) {
+ renamedEntry.setLastAccessTime(entry.getLastAccessTime());
+ }
+ if (entry.getLastModifiedTime() != null) {
+ renamedEntry.setLastModifiedTime(entry.getLastModifiedTime());
+ }
+ return renamedEntry;
+ }
+
+ }
+
}
diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JavaAgentDetector.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RepackagingLayout.java
similarity index 58%
rename from spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JavaAgentDetector.java
rename to spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RepackagingLayout.java
index 8fa5b9abb74..e14def52b0a 100644
--- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JavaAgentDetector.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RepackagingLayout.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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,24 +14,21 @@
* limitations under the License.
*/
-package org.springframework.boot.loader;
-
-import java.net.URL;
+package org.springframework.boot.loader.tools;
/**
- * A strategy for detecting Java agents.
+ * A specialization of {@link Layout} that repackages an existing archive by moving its
+ * content to a new location.
*
* @author Andy Wilkinson
- * @since 1.1.0
+ * @since 1.4.0
*/
-public interface JavaAgentDetector {
+public interface RepackagingLayout extends Layout {
/**
- * Returns {@code true} if {@code url} points to a Java agent jar file, otherwise
- * {@code false} is returned.
- * @param url The url to examine
- * @return if the URL points to a Java agent
+ * Returns the location to which classes should be moved.
+ * @return the repackaged classes location
*/
- boolean isJavaAgentJar(URL url);
+ String getRepackagedClassesLocation();
}
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutsTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutsTests.java
index 42ee8fad23a..e7a32fed129 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutsTests.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/LayoutsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2013 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -64,13 +64,13 @@ public class LayoutsTests {
public void jarLayout() throws Exception {
Layout layout = new Layouts.Jar();
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.COMPILE))
- .isEqualTo("lib/");
+ .isEqualTo("BOOT-INF/lib/");
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.CUSTOM))
- .isEqualTo("lib/");
+ .isEqualTo("BOOT-INF/lib/");
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.PROVIDED))
- .isEqualTo("lib/");
+ .isEqualTo("BOOT-INF/lib/");
assertThat(layout.getLibraryDestination("lib.jar", LibraryScope.RUNTIME))
- .isEqualTo("lib/");
+ .isEqualTo("BOOT-INF/lib/");
}
@Test
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java
index 8d0e2707f4d..83f92b98bc0 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/RepackagerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -291,7 +291,7 @@ public class RepackagerTests {
final File libNonJarFile = this.temporaryFolder.newFile();
FileCopyUtils.copy(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, libNonJarFile);
this.testJarFile.addClass("a/b/C.class", ClassWithMainMethod.class);
- this.testJarFile.addFile("lib/" + libJarFileToUnpack.getName(),
+ this.testJarFile.addFile("BOOT-INF/lib/" + libJarFileToUnpack.getName(),
libJarFileToUnpack);
File file = this.testJarFile.getFile();
libJarFile.setLastModified(JAN_1_1980);
@@ -305,12 +305,13 @@ public class RepackagerTests {
callback.library(new Library(libNonJarFile, LibraryScope.COMPILE));
}
});
- assertThat(hasEntry(file, "lib/" + libJarFile.getName())).isTrue();
- assertThat(hasEntry(file, "lib/" + libJarFileToUnpack.getName())).isTrue();
- assertThat(hasEntry(file, "lib/" + libNonJarFile.getName())).isFalse();
- JarEntry entry = getEntry(file, "lib/" + libJarFile.getName());
+ assertThat(hasEntry(file, "BOOT-INF/lib/" + libJarFile.getName())).isTrue();
+ assertThat(hasEntry(file, "BOOT-INF/lib/" + libJarFileToUnpack.getName()))
+ .isTrue();
+ assertThat(hasEntry(file, "BOOT-INF/lib/" + libNonJarFile.getName())).isFalse();
+ JarEntry entry = getEntry(file, "BOOT-INF/lib/" + libJarFile.getName());
assertThat(entry.getTime()).isEqualTo(JAN_1_1985);
- entry = getEntry(file, "lib/" + libJarFileToUnpack.getName());
+ entry = getEntry(file, "BOOT-INF/lib/" + libJarFileToUnpack.getName());
assertThat(entry.getComment()).startsWith("UNPACK:");
assertThat(entry.getComment().length()).isEqualTo(47);
}
@@ -395,9 +396,10 @@ public class RepackagerTests {
});
JarFile jarFile = new JarFile(file);
try {
- assertThat(jarFile.getEntry("lib/" + nestedFile.getName()).getMethod())
- .isEqualTo(ZipEntry.STORED);
- assertThat(jarFile.getEntry("test/nested.jar").getMethod())
+ assertThat(
+ jarFile.getEntry("BOOT-INF/lib/" + nestedFile.getName()).getMethod())
+ .isEqualTo(ZipEntry.STORED);
+ assertThat(jarFile.getEntry("BOOT-INF/classes/test/nested.jar").getMethod())
.isEqualTo(ZipEntry.STORED);
}
finally {
@@ -432,7 +434,8 @@ public class RepackagerTests {
TestJarFile nested = new TestJarFile(this.temporaryFolder);
nested.addClass("a/b/C.class", ClassWithoutMainMethod.class);
final File nestedFile = nested.getFile();
- this.testJarFile.addFile("lib/" + nestedFile.getName(), nested.getFile());
+ this.testJarFile.addFile("BOOT-INF/lib/" + nestedFile.getName(),
+ nested.getFile());
this.testJarFile.addClass("A.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
@@ -446,8 +449,9 @@ public class RepackagerTests {
});
JarFile jarFile = new JarFile(file);
try {
- assertThat(jarFile.getEntry("lib/" + nestedFile.getName()).getComment())
- .startsWith("UNPACK:");
+ assertThat(
+ jarFile.getEntry("BOOT-INF/lib/" + nestedFile.getName()).getComment())
+ .startsWith("UNPACK:");
}
finally {
jarFile.close();
@@ -460,7 +464,8 @@ public class RepackagerTests {
TestJarFile nested = new TestJarFile(this.temporaryFolder);
nested.addClass("a/b/C.class", ClassWithoutMainMethod.class);
final File nestedFile = nested.getFile();
- this.testJarFile.addFile("lib/" + nestedFile.getName(), nested.getFile());
+ this.testJarFile.addFile("BOOT-INF/lib/" + nestedFile.getName(),
+ nested.getFile());
this.testJarFile.addClass("A.class", ClassWithMainMethod.class);
File file = this.testJarFile.getFile();
Repackager repackager = new Repackager(file);
@@ -478,7 +483,7 @@ public class RepackagerTests {
});
JarFile jarFile = new JarFile(file);
try {
- assertThat(jarFile.getEntry("lib/" + nestedFile.getName()).getSize())
+ assertThat(jarFile.getEntry("BOOT-INF/lib/" + nestedFile.getName()).getSize())
.isEqualTo(sourceLength);
}
finally {
diff --git a/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml b/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml
index 2bbfe1c7ecb..ac5eaa5184d 100644
--- a/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml
+++ b/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml
@@ -49,7 +49,7 @@
copy-dependencies
- ${project.build.directory}/assembly/lib
+ ${project.build.directory}/assembly/BOOT-INF/lib
diff --git a/spring-boot-tools/spring-boot-loader/src/it/executable-props/src/main/assembly/jar-with-dependencies.xml b/spring-boot-tools/spring-boot-loader/src/it/executable-props/src/main/assembly/jar-with-dependencies.xml
index 44626f91aa1..1120acb7e2f 100644
--- a/spring-boot-tools/spring-boot-loader/src/it/executable-props/src/main/assembly/jar-with-dependencies.xml
+++ b/spring-boot-tools/spring-boot-loader/src/it/executable-props/src/main/assembly/jar-with-dependencies.xml
@@ -14,6 +14,7 @@
${project.groupId}:${project.artifactId}
+ BOOT-INF/classes
true
diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ExecutableArchiveLauncher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ExecutableArchiveLauncher.java
index 6ecb93d3604..38edb926a6d 100644
--- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ExecutableArchiveLauncher.java
+++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/ExecutableArchiveLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -16,13 +16,8 @@
package org.springframework.boot.loader;
-import java.net.URL;
-import java.net.URLClassLoader;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.Manifest;
@@ -40,24 +35,16 @@ public abstract class ExecutableArchiveLauncher extends Launcher {
private final Archive archive;
- private final JavaAgentDetector javaAgentDetector;
-
public ExecutableArchiveLauncher() {
- this(new InputArgumentsJavaAgentDetector());
- }
-
- public ExecutableArchiveLauncher(JavaAgentDetector javaAgentDetector) {
try {
this.archive = createArchive();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
- this.javaAgentDetector = javaAgentDetector;
}
protected ExecutableArchiveLauncher(Archive archive) {
- this.javaAgentDetector = new InputArgumentsJavaAgentDetector();
this.archive = archive;
}
@@ -92,31 +79,6 @@ public abstract class ExecutableArchiveLauncher extends Launcher {
return archives;
}
- @Override
- protected ClassLoader createClassLoader(URL[] urls) throws Exception {
- Set copy = new LinkedHashSet(urls.length);
- ClassLoader loader = getDefaultClassLoader();
- if (loader instanceof URLClassLoader) {
- for (URL url : ((URLClassLoader) loader).getURLs()) {
- if (addDefaultClassloaderUrl(urls, url)) {
- copy.add(url);
- }
- }
- }
- Collections.addAll(copy, urls);
- return super.createClassLoader(copy.toArray(new URL[copy.size()]));
- }
-
- private boolean addDefaultClassloaderUrl(URL[] urls, URL url) {
- String jarUrl = "jar:" + url + "!/";
- for (URL nestedUrl : urls) {
- if (nestedUrl.equals(url) || nestedUrl.toString().equals(jarUrl)) {
- return false;
- }
- }
- return !this.javaAgentDetector.isJavaAgentJar(url);
- }
-
/**
* 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.
@@ -134,20 +96,4 @@ public abstract class ExecutableArchiveLauncher extends Launcher {
protected void postProcessClassPathArchives(List archives) throws Exception {
}
- private static ClassLoader getDefaultClassLoader() {
- ClassLoader classloader = null;
- try {
- classloader = Thread.currentThread().getContextClassLoader();
- }
- catch (Throwable ex) {
- // Cannot access thread context ClassLoader - falling back to system class
- // loader...
- }
- if (classloader == null) {
- // No thread context class loader -> use class loader of this class.
- classloader = ExecutableArchiveLauncher.class.getClassLoader();
- }
- return classloader;
- }
-
}
diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/InputArgumentsJavaAgentDetector.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/InputArgumentsJavaAgentDetector.java
deleted file mode 100644
index cc6fd47cf14..00000000000
--- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/InputArgumentsJavaAgentDetector.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2012-2014 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.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A {@link JavaAgentDetector} that detects jars supplied via the {@code -javaagent} JVM
- * input argument.
- *
- * @author Andy Wilkinson
- * @since 1.1.0
- */
-public class InputArgumentsJavaAgentDetector implements JavaAgentDetector {
-
- private static final String JAVA_AGENT_PREFIX = "-javaagent:";
-
- private final Set javaAgentJars;
-
- public InputArgumentsJavaAgentDetector() {
- this(getInputArguments());
- }
-
- InputArgumentsJavaAgentDetector(List inputArguments) {
- this.javaAgentJars = getJavaAgentJars(inputArguments);
- }
-
- private static List getInputArguments() {
- try {
- return AccessController.doPrivileged(new PrivilegedAction>() {
- @Override
- public List run() {
- return ManagementFactory.getRuntimeMXBean().getInputArguments();
- }
- });
- }
- catch (Exception ex) {
- return Collections.emptyList();
- }
- }
-
- private Set getJavaAgentJars(List inputArguments) {
- Set javaAgentJars = new HashSet();
- for (String argument : inputArguments) {
- String path = getJavaAgentJarPath(argument);
- if (path != null) {
- try {
- javaAgentJars.add(new File(path).getCanonicalFile().toURI().toURL());
- }
- catch (IOException ex) {
- throw new IllegalStateException(
- "Failed to determine canonical path of Java agent at path '"
- + path + "'");
- }
- }
- }
- return javaAgentJars;
- }
-
- private String getJavaAgentJarPath(String arg) {
- if (arg.startsWith(JAVA_AGENT_PREFIX)) {
- String path = arg.substring(JAVA_AGENT_PREFIX.length());
- int equalsIndex = path.indexOf('=');
- if (equalsIndex > -1) {
- path = path.substring(0, equalsIndex);
- }
- return path;
- }
- return null;
- }
-
- @Override
- public boolean isJavaAgentJar(URL url) {
- return this.javaAgentJars.contains(url);
- }
-
-}
diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java
index d004d0175f7..6f19c70c235 100644
--- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java
+++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2013 the original author or authors.
+ * Copyright 2012-2016 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,13 +22,17 @@ import org.springframework.boot.loader.archive.Archive;
/**
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
- * included inside a {@code /lib} directory.
+ * included inside a {@code /BOOT-INF/lib} and that application classes are included
+ * inside a {@code /BOOT-INF/classes} directory.
*
* @author Phillip Webb
+ * @author Andy Wilkinson
*/
public class JarLauncher extends ExecutableArchiveLauncher {
- private static final String LIB = "lib/";
+ static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
+
+ static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {
}
@@ -39,7 +43,10 @@ public class JarLauncher extends ExecutableArchiveLauncher {
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
- return !entry.isDirectory() && entry.getName().startsWith(LIB);
+ if (entry.isDirectory()) {
+ return entry.getName().startsWith(BOOT_INF_CLASSES);
+ }
+ return entry.getName().startsWith(BOOT_INF_LIB);
}
@Override
diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java
index f9bfac9b2b2..1608792e7f8 100644
--- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java
+++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/LaunchedURLClassLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -23,13 +23,10 @@ import java.net.URLClassLoader;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.Enumeration;
import org.springframework.boot.loader.jar.Handler;
import org.springframework.boot.loader.jar.JarFile;
-import org.springframework.lang.UsesJava7;
/**
* {@link ClassLoader} used by the {@link Launcher}.
@@ -40,10 +37,6 @@ import org.springframework.lang.UsesJava7;
*/
public class LaunchedURLClassLoader extends URLClassLoader {
- private static LockProvider LOCK_PROVIDER = setupLockProvider();
-
- private final ClassLoader rootClassLoader;
-
/**
* Create a new {@link LaunchedURLClassLoader} instance.
* @param urls the URLs from which to load classes and resources
@@ -51,57 +44,21 @@ public class LaunchedURLClassLoader extends URLClassLoader {
*/
public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
- this.rootClassLoader = findRootClassLoader(parent);
- }
-
- private ClassLoader findRootClassLoader(ClassLoader classLoader) {
- while (classLoader != null) {
- if (classLoader.getParent() == null) {
- return classLoader;
- }
- classLoader = classLoader.getParent();
- }
- return null;
- }
-
- /**
- * Gets the resource with the given {@code name}. Unlike a standard
- * {@link ClassLoader}, this method will first search the root class loader. If the
- * resource is not found, this method will call {@link #findResource(String)}.
- */
- @Override
- public URL getResource(String name) {
- URL url = null;
- if (this.rootClassLoader != null) {
- url = this.rootClassLoader.getResource(name);
- }
- return (url == null ? findResource(name) : url);
}
@Override
public URL findResource(String name) {
+ Handler.setUseFastConnectionExceptions(true);
try {
- if (name.equals("") && hasURLs()) {
- return getURLs()[0];
- }
- Handler.setUseFastConnectionExceptions(true);
- try {
- return super.findResource(name);
- }
- finally {
- Handler.setUseFastConnectionExceptions(false);
- }
+ return super.findResource(name);
}
- catch (IllegalArgumentException ex) {
- return null;
+ finally {
+ Handler.setUseFastConnectionExceptions(false);
}
}
@Override
public Enumeration findResources(String name) throws IOException {
- if (name.equals("") && hasURLs()) {
- return Collections.enumeration(Arrays.asList(getURLs()));
- }
Handler.setUseFastConnectionExceptions(true);
try {
return super.findResources(name);
@@ -111,106 +68,47 @@ public class LaunchedURLClassLoader extends URLClassLoader {
}
}
- private boolean hasURLs() {
- return getURLs().length > 0;
- }
-
- /**
- * Gets the resources with the given {@code name}. Returns a combination of the
- * resources found by {@link #findResources(String)} and from
- * {@link ClassLoader#getResources(String) getResources(String)} on the root class
- * loader, if any.
- */
- @Override
- public Enumeration getResources(String name) throws IOException {
- if (this.rootClassLoader == null) {
- return findResources(name);
- }
- return new ResourceEnumeration(this.rootClassLoader.getResources(name),
- findResources(name));
- }
-
- /**
- * Attempt to load classes from the URLs before delegating to the parent loader.
- */
@Override
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
- synchronized (LaunchedURLClassLoader.LOCK_PROVIDER.getLock(this, name)) {
- Class> loadedClass = findLoadedClass(name);
- if (loadedClass == null) {
- Handler.setUseFastConnectionExceptions(true);
- try {
- loadedClass = doLoadClass(name);
- }
- finally {
- Handler.setUseFastConnectionExceptions(false);
- }
- }
- if (resolve) {
- resolveClass(loadedClass);
- }
- return loadedClass;
- }
- }
-
- private Class> doLoadClass(String name) throws ClassNotFoundException {
-
- // 1) Try the root class loader
+ Handler.setUseFastConnectionExceptions(true);
try {
- if (this.rootClassLoader != null) {
- return this.rootClassLoader.loadClass(name);
- }
+ definePackageIfNecessary(name);
+ return super.loadClass(name, resolve);
}
- catch (Exception ex) {
- // Ignore and continue
- }
-
- // 2) Try to find locally
- try {
- findPackage(name);
- Class> cls = findClass(name);
- return cls;
- }
- catch (Exception ex) {
- // Ignore and continue
- }
-
- // 3) Use standard loading
- return super.loadClass(name, false);
- }
-
- private void findPackage(final String name) throws ClassNotFoundException {
- int lastDot = name.lastIndexOf('.');
- if (lastDot != -1) {
- String packageName = name.substring(0, lastDot);
- if (getPackage(packageName) == null) {
- try {
- definePackageForFindClass(name, packageName);
- }
- catch (Exception ex) {
- // Swallow and continue
- }
- }
+ finally {
+ Handler.setUseFastConnectionExceptions(false);
}
}
/**
* Define a package before a {@code findClass} call is made. This is necessary to
- * ensure that the appropriate manifest for nested JARs associated with the package.
- * @param name the class name being found
- * @param packageName the package
+ * ensure that the appropriate manifest for nested JARs is associated with the
+ * package.
+ * @param className the class name being found
*/
- private void definePackageForFindClass(final String name, final String packageName) {
+ private void definePackageIfNecessary(String className) {
+ int lastDot = className.lastIndexOf('.');
+ if (lastDot >= 0) {
+ String packageName = className.substring(0, lastDot);
+ if (getPackage(packageName) == null) {
+ definePackage(packageName);
+ }
+ }
+ }
+
+ private void definePackage(final String packageName) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction