Skip to content

Commit 2d8344d

Browse files
committed
Ensure that JarLauncher doesn't cause root jar to be on class path twice
Closes spring-projectsgh-7595
1 parent 64da63a commit 2d8344d

File tree

4 files changed

+179
-71
lines changed

4 files changed

+179
-71
lines changed

spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616

1717
package org.springframework.boot.loader;
1818

19-
import java.util.List;
20-
2119
import org.springframework.boot.loader.archive.Archive;
2220

2321
/**
2422
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
25-
* included inside a {@code /BOOT-INF/lib} and that application classes are included
26-
* inside a {@code /BOOT-INF/classes} directory.
23+
* included inside a {@code /BOOT-INF/lib} directory and that application classes are
24+
* included inside a {@code /BOOT-INF/classes} directory.
2725
*
2826
* @author Phillip Webb
2927
* @author Andy Wilkinson
@@ -49,11 +47,6 @@ protected boolean isNestedArchive(Archive.Entry entry) {
4947
return entry.getName().startsWith(BOOT_INF_LIB);
5048
}
5149

52-
@Override
53-
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
54-
archives.add(0, getArchive());
55-
}
56-
5750
public static void main(String[] args) throws Exception {
5851
new JarLauncher().launch(args);
5952
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.loader;
18+
19+
import java.io.ByteArrayOutputStream;
20+
import java.io.File;
21+
import java.io.FileOutputStream;
22+
import java.io.IOException;
23+
import java.net.MalformedURLException;
24+
import java.net.URL;
25+
import java.util.Enumeration;
26+
import java.util.HashSet;
27+
import java.util.List;
28+
import java.util.Set;
29+
import java.util.jar.JarEntry;
30+
import java.util.jar.JarFile;
31+
import java.util.jar.JarOutputStream;
32+
import java.util.zip.CRC32;
33+
import java.util.zip.ZipEntry;
34+
35+
import org.junit.Rule;
36+
import org.junit.rules.TemporaryFolder;
37+
38+
import org.springframework.boot.loader.archive.Archive;
39+
import org.springframework.util.FileCopyUtils;
40+
41+
/**
42+
* Base class for testing {@link ExecutableArchiveLauncher} implementations.
43+
*
44+
* @author Andy Wilkinson
45+
*/
46+
public class AbstractExecutableArchiveLauncherTests {
47+
48+
@Rule
49+
public TemporaryFolder temp = new TemporaryFolder();
50+
51+
protected File createJarArchive(String name, String entryPrefix) throws IOException {
52+
File archive = this.temp.newFile(name);
53+
JarOutputStream jarOutputStream = new JarOutputStream(
54+
new FileOutputStream(archive));
55+
jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/"));
56+
jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/classes/"));
57+
jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/lib/"));
58+
JarEntry webInfLibFoo = new JarEntry(entryPrefix + "/lib/foo.jar");
59+
webInfLibFoo.setMethod(ZipEntry.STORED);
60+
ByteArrayOutputStream fooJarStream = new ByteArrayOutputStream();
61+
new JarOutputStream(fooJarStream).close();
62+
webInfLibFoo.setSize(fooJarStream.size());
63+
CRC32 crc32 = new CRC32();
64+
crc32.update(fooJarStream.toByteArray());
65+
webInfLibFoo.setCrc(crc32.getValue());
66+
jarOutputStream.putNextEntry(webInfLibFoo);
67+
jarOutputStream.write(fooJarStream.toByteArray());
68+
jarOutputStream.close();
69+
return archive;
70+
}
71+
72+
protected File explode(File archive) throws IOException {
73+
File exploded = this.temp.newFolder("exploded");
74+
JarFile jarFile = new JarFile(archive);
75+
Enumeration<JarEntry> entries = jarFile.entries();
76+
while (entries.hasMoreElements()) {
77+
JarEntry entry = entries.nextElement();
78+
File entryFile = new File(exploded, entry.getName());
79+
if (entry.isDirectory()) {
80+
entryFile.mkdirs();
81+
}
82+
else {
83+
FileCopyUtils.copy(jarFile.getInputStream(entry),
84+
new FileOutputStream(entryFile));
85+
}
86+
}
87+
jarFile.close();
88+
return exploded;
89+
}
90+
91+
protected Set<URL> getUrls(List<Archive> archives) throws MalformedURLException {
92+
Set<URL> urls = new HashSet<URL>(archives.size());
93+
for (Archive archive : archives) {
94+
urls.add(archive.getUrl());
95+
}
96+
return urls;
97+
}
98+
99+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.loader;
18+
19+
import java.io.File;
20+
import java.net.URL;
21+
import java.util.List;
22+
23+
import org.junit.Test;
24+
25+
import org.springframework.boot.loader.archive.Archive;
26+
import org.springframework.boot.loader.archive.ExplodedArchive;
27+
import org.springframework.boot.loader.archive.JarFileArchive;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
/**
32+
* Tests for {@link JarLauncher}.
33+
*
34+
* @author Andy Wilkinson
35+
*/
36+
public class JarLauncherTests extends AbstractExecutableArchiveLauncherTests {
37+
38+
@Test
39+
public void explodedJarHasOnlyBootInfClassesAndContentsOfBootInfLibOnClasspath()
40+
throws Exception {
41+
File explodedRoot = explode(createJarArchive("archive.jar", "BOOT-INF"));
42+
JarLauncher launcher = new JarLauncher(new ExplodedArchive(explodedRoot, true));
43+
List<Archive> archives = launcher.getClassPathArchives();
44+
assertThat(archives).hasSize(2);
45+
assertThat(getUrls(archives)).containsOnly(
46+
new File(explodedRoot, "BOOT-INF/classes").toURI().toURL(),
47+
new URL("jar:"
48+
+ new File(explodedRoot, "BOOT-INF/lib/foo.jar").toURI().toURL()
49+
+ "!/"));
50+
}
51+
52+
@Test
53+
public void archivedJarHasOnlyBootInfClassesAndContentsOfBootInfLibOnClasspath()
54+
throws Exception {
55+
File jarRoot = createJarArchive("archive.jar", "BOOT-INF");
56+
JarLauncher launcher = new JarLauncher(new JarFileArchive(jarRoot));
57+
List<Archive> archives = launcher.getClassPathArchives();
58+
assertThat(archives).hasSize(2);
59+
assertThat(getUrls(archives)).containsOnly(
60+
new URL("jar:" + jarRoot.toURI().toURL() + "!/BOOT-INF/classes!/"),
61+
new URL("jar:" + jarRoot.toURI().toURL() + "!/BOOT-INF/lib/foo.jar!/"));
62+
}
63+
64+
}

spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/WarLauncherTests.java

Lines changed: 14 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16,96 +16,48 @@
1616

1717
package org.springframework.boot.loader;
1818

19-
import java.io.ByteArrayOutputStream;
2019
import java.io.File;
21-
import java.io.FileNotFoundException;
22-
import java.io.FileOutputStream;
23-
import java.io.IOException;
24-
import java.net.MalformedURLException;
2520
import java.net.URL;
26-
import java.util.HashSet;
2721
import java.util.List;
28-
import java.util.Set;
29-
import java.util.jar.JarEntry;
30-
import java.util.jar.JarOutputStream;
31-
import java.util.zip.CRC32;
32-
import java.util.zip.ZipEntry;
3322

3423
import org.junit.Test;
3524

3625
import org.springframework.boot.loader.archive.Archive;
3726
import org.springframework.boot.loader.archive.ExplodedArchive;
3827
import org.springframework.boot.loader.archive.JarFileArchive;
39-
import org.springframework.util.FileSystemUtils;
4028

4129
import static org.assertj.core.api.Assertions.assertThat;
4230

4331
/**
44-
* Tests for {@link WarLauncher}
32+
* Tests for {@link WarLauncher}.
4533
*
4634
* @author Andy Wilkinson
4735
*/
48-
public class WarLauncherTests {
36+
public class WarLauncherTests extends AbstractExecutableArchiveLauncherTests {
4937

5038
@Test
5139
public void explodedWarHasOnlyWebInfClassesAndContentsOfWebInfLibOnClasspath()
5240
throws Exception {
53-
File warRoot = new File("target/exploded-war");
54-
FileSystemUtils.deleteRecursively(warRoot);
55-
warRoot.mkdirs();
56-
File webInfClasses = new File(warRoot, "WEB-INF/classes");
57-
webInfClasses.mkdirs();
58-
File webInfLib = new File(warRoot, "WEB-INF/lib");
59-
webInfLib.mkdirs();
60-
File webInfLibFoo = new File(webInfLib, "foo.jar");
61-
new JarOutputStream(new FileOutputStream(webInfLibFoo)).close();
62-
WarLauncher launcher = new WarLauncher(new ExplodedArchive(warRoot, true));
41+
File explodedRoot = explode(createJarArchive("archive.war", "WEB-INF"));
42+
WarLauncher launcher = new WarLauncher(new ExplodedArchive(explodedRoot, true));
6343
List<Archive> archives = launcher.getClassPathArchives();
6444
assertThat(archives).hasSize(2);
65-
assertThat(getUrls(archives)).containsOnly(webInfClasses.toURI().toURL(),
66-
new URL("jar:" + webInfLibFoo.toURI().toURL() + "!/"));
45+
assertThat(getUrls(archives)).containsOnly(
46+
new File(explodedRoot, "WEB-INF/classes").toURI().toURL(),
47+
new URL("jar:"
48+
+ new File(explodedRoot, "WEB-INF/lib/foo.jar").toURI().toURL()
49+
+ "!/"));
6750
}
6851

6952
@Test
70-
public void archivedWarHasOnlyWebInfClassesAndContentsOfWebInfLibOnClasspath()
53+
public void archivedWarHasOnlyWebInfClassesAndContentsOWebInfLibOnClasspath()
7154
throws Exception {
72-
File warRoot = createWarArchive();
73-
WarLauncher launcher = new WarLauncher(new JarFileArchive(warRoot));
55+
File jarRoot = createJarArchive("archive.war", "WEB-INF");
56+
WarLauncher launcher = new WarLauncher(new JarFileArchive(jarRoot));
7457
List<Archive> archives = launcher.getClassPathArchives();
7558
assertThat(archives).hasSize(2);
7659
assertThat(getUrls(archives)).containsOnly(
77-
new URL("jar:" + warRoot.toURI().toURL() + "!/WEB-INF/classes!/"),
78-
new URL("jar:" + warRoot.toURI().toURL() + "!/WEB-INF/lib/foo.jar!/"));
79-
}
80-
81-
private Set<URL> getUrls(List<Archive> archives) throws MalformedURLException {
82-
Set<URL> urls = new HashSet<URL>(archives.size());
83-
for (Archive archive : archives) {
84-
urls.add(archive.getUrl());
85-
}
86-
return urls;
60+
new URL("jar:" + jarRoot.toURI().toURL() + "!/WEB-INF/classes!/"),
61+
new URL("jar:" + jarRoot.toURI().toURL() + "!/WEB-INF/lib/foo.jar!/"));
8762
}
88-
89-
private File createWarArchive() throws IOException, FileNotFoundException {
90-
File warRoot = new File("target/archive.war");
91-
warRoot.delete();
92-
JarOutputStream jarOutputStream = new JarOutputStream(
93-
new FileOutputStream(warRoot));
94-
jarOutputStream.putNextEntry(new JarEntry("WEB-INF/"));
95-
jarOutputStream.putNextEntry(new JarEntry("WEB-INF/classes/"));
96-
jarOutputStream.putNextEntry(new JarEntry("WEB-INF/lib/"));
97-
JarEntry webInfLibFoo = new JarEntry("WEB-INF/lib/foo.jar");
98-
webInfLibFoo.setMethod(ZipEntry.STORED);
99-
ByteArrayOutputStream fooJarStream = new ByteArrayOutputStream();
100-
new JarOutputStream(fooJarStream).close();
101-
webInfLibFoo.setSize(fooJarStream.size());
102-
CRC32 crc32 = new CRC32();
103-
crc32.update(fooJarStream.toByteArray());
104-
webInfLibFoo.setCrc(crc32.getValue());
105-
jarOutputStream.putNextEntry(webInfLibFoo);
106-
jarOutputStream.write(fooJarStream.toByteArray());
107-
jarOutputStream.close();
108-
return warRoot;
109-
}
110-
11163
}

0 commit comments

Comments
 (0)