From 2d8344d46d09c6c0de0bfe593f31b883a407f622 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 8 Dec 2016 11:34:22 +0000 Subject: [PATCH] Ensure that JarLauncher doesn't cause root jar to be on class path twice Closes gh-7595 --- .../boot/loader/JarLauncher.java | 11 +-- ...bstractExecutableArchiveLauncherTests.java | 99 +++++++++++++++++++ .../boot/loader/JarLauncherTests.java | 64 ++++++++++++ .../boot/loader/WarLauncherTests.java | 84 ++++------------ 4 files changed, 183 insertions(+), 75 deletions(-) create mode 100644 spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java create mode 100644 spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java 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 2776d6f8c10..8a59b4693d9 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 @@ -16,14 +16,12 @@ package org.springframework.boot.loader; -import java.util.List; - import org.springframework.boot.loader.archive.Archive; /** * {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are - * included inside a {@code /BOOT-INF/lib} and that application classes are included - * inside a {@code /BOOT-INF/classes} directory. + * included inside a {@code /BOOT-INF/lib} directory and that application classes are + * included inside a {@code /BOOT-INF/classes} directory. * * @author Phillip Webb * @author Andy Wilkinson @@ -49,11 +47,6 @@ public class JarLauncher extends ExecutableArchiveLauncher { return entry.getName().startsWith(BOOT_INF_LIB); } - @Override - protected void postProcessClassPathArchives(List archives) throws Exception { - archives.add(0, getArchive()); - } - public static void main(String[] args) throws Exception { new JarLauncher().launch(args); } diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java new file mode 100644 index 00000000000..1c34d932205 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java @@ -0,0 +1,99 @@ +/* + * 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. + * 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.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +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.JarOutputStream; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; + +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +import org.springframework.boot.loader.archive.Archive; +import org.springframework.util.FileCopyUtils; + +/** + * Base class for testing {@link ExecutableArchiveLauncher} implementations. + * + * @author Andy Wilkinson + */ +public class AbstractExecutableArchiveLauncherTests { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + protected File createJarArchive(String name, String entryPrefix) throws IOException { + File archive = this.temp.newFile(name); + JarOutputStream jarOutputStream = new JarOutputStream( + new FileOutputStream(archive)); + jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/")); + jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/classes/")); + jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/lib/")); + JarEntry webInfLibFoo = new JarEntry(entryPrefix + "/lib/foo.jar"); + webInfLibFoo.setMethod(ZipEntry.STORED); + ByteArrayOutputStream fooJarStream = new ByteArrayOutputStream(); + new JarOutputStream(fooJarStream).close(); + webInfLibFoo.setSize(fooJarStream.size()); + CRC32 crc32 = new CRC32(); + crc32.update(fooJarStream.toByteArray()); + webInfLibFoo.setCrc(crc32.getValue()); + jarOutputStream.putNextEntry(webInfLibFoo); + jarOutputStream.write(fooJarStream.toByteArray()); + jarOutputStream.close(); + return archive; + } + + protected File explode(File archive) throws IOException { + File exploded = this.temp.newFolder("exploded"); + JarFile jarFile = new JarFile(archive); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + File entryFile = new File(exploded, entry.getName()); + if (entry.isDirectory()) { + entryFile.mkdirs(); + } + else { + FileCopyUtils.copy(jarFile.getInputStream(entry), + new FileOutputStream(entryFile)); + } + } + jarFile.close(); + return exploded; + } + + protected Set getUrls(List archives) throws MalformedURLException { + Set urls = new HashSet(archives.size()); + for (Archive archive : archives) { + urls.add(archive.getUrl()); + } + return urls; + } + +} diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java new file mode 100644 index 00000000000..ecb1c1c06bb --- /dev/null +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/JarLauncherTests.java @@ -0,0 +1,64 @@ +/* + * 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. + * 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.URL; +import java.util.List; + +import org.junit.Test; + +import org.springframework.boot.loader.archive.Archive; +import org.springframework.boot.loader.archive.ExplodedArchive; +import org.springframework.boot.loader.archive.JarFileArchive; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JarLauncher}. + * + * @author Andy Wilkinson + */ +public class JarLauncherTests extends AbstractExecutableArchiveLauncherTests { + + @Test + public void explodedJarHasOnlyBootInfClassesAndContentsOfBootInfLibOnClasspath() + throws Exception { + File explodedRoot = explode(createJarArchive("archive.jar", "BOOT-INF")); + JarLauncher launcher = new JarLauncher(new ExplodedArchive(explodedRoot, true)); + List archives = launcher.getClassPathArchives(); + assertThat(archives).hasSize(2); + assertThat(getUrls(archives)).containsOnly( + new File(explodedRoot, "BOOT-INF/classes").toURI().toURL(), + new URL("jar:" + + new File(explodedRoot, "BOOT-INF/lib/foo.jar").toURI().toURL() + + "!/")); + } + + @Test + public void archivedJarHasOnlyBootInfClassesAndContentsOfBootInfLibOnClasspath() + throws Exception { + File jarRoot = createJarArchive("archive.jar", "BOOT-INF"); + JarLauncher launcher = new JarLauncher(new JarFileArchive(jarRoot)); + List archives = launcher.getClassPathArchives(); + assertThat(archives).hasSize(2); + assertThat(getUrls(archives)).containsOnly( + new URL("jar:" + jarRoot.toURI().toURL() + "!/BOOT-INF/classes!/"), + new URL("jar:" + jarRoot.toURI().toURL() + "!/BOOT-INF/lib/foo.jar!/")); + } + +} diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/WarLauncherTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/WarLauncherTests.java index 3ba31830f6e..74bed70938d 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/WarLauncherTests.java +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/WarLauncherTests.java @@ -16,96 +16,48 @@ package org.springframework.boot.loader; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; import org.junit.Test; import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.ExplodedArchive; import org.springframework.boot.loader.archive.JarFileArchive; -import org.springframework.util.FileSystemUtils; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link WarLauncher} + * Tests for {@link WarLauncher}. * * @author Andy Wilkinson */ -public class WarLauncherTests { +public class WarLauncherTests extends AbstractExecutableArchiveLauncherTests { @Test public void explodedWarHasOnlyWebInfClassesAndContentsOfWebInfLibOnClasspath() throws Exception { - File warRoot = new File("target/exploded-war"); - FileSystemUtils.deleteRecursively(warRoot); - warRoot.mkdirs(); - File webInfClasses = new File(warRoot, "WEB-INF/classes"); - webInfClasses.mkdirs(); - File webInfLib = new File(warRoot, "WEB-INF/lib"); - webInfLib.mkdirs(); - File webInfLibFoo = new File(webInfLib, "foo.jar"); - new JarOutputStream(new FileOutputStream(webInfLibFoo)).close(); - WarLauncher launcher = new WarLauncher(new ExplodedArchive(warRoot, true)); - List archives = launcher.getClassPathArchives(); - assertThat(archives).hasSize(2); - assertThat(getUrls(archives)).containsOnly(webInfClasses.toURI().toURL(), - new URL("jar:" + webInfLibFoo.toURI().toURL() + "!/")); - } - - @Test - public void archivedWarHasOnlyWebInfClassesAndContentsOfWebInfLibOnClasspath() - throws Exception { - File warRoot = createWarArchive(); - WarLauncher launcher = new WarLauncher(new JarFileArchive(warRoot)); + File explodedRoot = explode(createJarArchive("archive.war", "WEB-INF")); + WarLauncher launcher = new WarLauncher(new ExplodedArchive(explodedRoot, true)); List archives = launcher.getClassPathArchives(); assertThat(archives).hasSize(2); assertThat(getUrls(archives)).containsOnly( - new URL("jar:" + warRoot.toURI().toURL() + "!/WEB-INF/classes!/"), - new URL("jar:" + warRoot.toURI().toURL() + "!/WEB-INF/lib/foo.jar!/")); + new File(explodedRoot, "WEB-INF/classes").toURI().toURL(), + new URL("jar:" + + new File(explodedRoot, "WEB-INF/lib/foo.jar").toURI().toURL() + + "!/")); } - private Set getUrls(List archives) throws MalformedURLException { - Set urls = new HashSet(archives.size()); - for (Archive archive : archives) { - urls.add(archive.getUrl()); - } - return urls; + @Test + public void archivedWarHasOnlyWebInfClassesAndContentsOWebInfLibOnClasspath() + throws Exception { + File jarRoot = createJarArchive("archive.war", "WEB-INF"); + WarLauncher launcher = new WarLauncher(new JarFileArchive(jarRoot)); + List archives = launcher.getClassPathArchives(); + assertThat(archives).hasSize(2); + assertThat(getUrls(archives)).containsOnly( + new URL("jar:" + jarRoot.toURI().toURL() + "!/WEB-INF/classes!/"), + new URL("jar:" + jarRoot.toURI().toURL() + "!/WEB-INF/lib/foo.jar!/")); } - - private File createWarArchive() throws IOException, FileNotFoundException { - File warRoot = new File("target/archive.war"); - warRoot.delete(); - JarOutputStream jarOutputStream = new JarOutputStream( - new FileOutputStream(warRoot)); - jarOutputStream.putNextEntry(new JarEntry("WEB-INF/")); - jarOutputStream.putNextEntry(new JarEntry("WEB-INF/classes/")); - jarOutputStream.putNextEntry(new JarEntry("WEB-INF/lib/")); - JarEntry webInfLibFoo = new JarEntry("WEB-INF/lib/foo.jar"); - webInfLibFoo.setMethod(ZipEntry.STORED); - ByteArrayOutputStream fooJarStream = new ByteArrayOutputStream(); - new JarOutputStream(fooJarStream).close(); - webInfLibFoo.setSize(fooJarStream.size()); - CRC32 crc32 = new CRC32(); - crc32.update(fooJarStream.toByteArray()); - webInfLibFoo.setCrc(crc32.getValue()); - jarOutputStream.putNextEntry(webInfLibFoo); - jarOutputStream.write(fooJarStream.toByteArray()); - jarOutputStream.close(); - return warRoot; - } - }