mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Ensure that JarFileArchive unpacks entries to unique location
Previously, JarFileArchive would always unpack any entries marked for unpacking to ${java.io.tmpdir}/spring-boot-libs. This could cause problems if multiple Spring Boot applications were running on the same host: - If the apps are run as different users the first application would create the spring-boot-libs directory and the second and subsequent applications may not have write permissions to that directory - Multiple apps may overwrite each others unpacked libs. At best this will mean one copy of a jar is overwritten with another identical copy. At worst the jars may have different contents so that some of the contents of the original jar disappear unexpectedly. This commit updates JarFileArchive to use an application-specific location when unpacking libs. A directory beneath ${java.io.tmpdir} is still used but it's now named <jar-file-name>-spring-boot-libs-<uuid>. A loop, limited to 1000 attempts, is used to avoid problems caused by the uuid clashing. Closes gh-4124
This commit is contained in:
parent
f7d2bafb3e
commit
888fa90265
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2013 the original author or authors.
|
* Copyright 2012-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -27,6 +27,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ import org.springframework.boot.loader.util.AsciiBytes;
|
|||||||
* {@link Archive} implementation backed by a {@link JarFile}.
|
* {@link Archive} implementation backed by a {@link JarFile}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
public class JarFileArchive extends Archive {
|
public class JarFileArchive extends Archive {
|
||||||
|
|
||||||
@ -53,6 +55,8 @@ public class JarFileArchive extends Archive {
|
|||||||
|
|
||||||
private URL url;
|
private URL url;
|
||||||
|
|
||||||
|
private File tempUnpackFolder;
|
||||||
|
|
||||||
public JarFileArchive(File file) throws IOException {
|
public JarFileArchive(File file) throws IOException {
|
||||||
this(file, null);
|
this(file, null);
|
||||||
}
|
}
|
||||||
@ -123,10 +127,25 @@ public class JarFileArchive extends Archive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private File getTempUnpackFolder() {
|
private File getTempUnpackFolder() {
|
||||||
File tempFolder = new File(System.getProperty("java.io.tmpdir"));
|
if (this.tempUnpackFolder == null) {
|
||||||
File unpackFolder = new File(tempFolder, "spring-boot-libs");
|
File tempFolder = new File(System.getProperty("java.io.tmpdir"));
|
||||||
unpackFolder.mkdirs();
|
this.tempUnpackFolder = createUnpackFolder(tempFolder);
|
||||||
return unpackFolder;
|
}
|
||||||
|
return this.tempUnpackFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createUnpackFolder(File parent) {
|
||||||
|
int attempts = 0;
|
||||||
|
while (attempts++ < 1000) {
|
||||||
|
String fileName = new File(this.jarFile.getName()).getName();
|
||||||
|
File unpackFolder = new File(parent,
|
||||||
|
fileName + "-spring-boot-libs-" + UUID.randomUUID());
|
||||||
|
if (unpackFolder.mkdirs()) {
|
||||||
|
return unpackFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Failed to create unpack folder in directory '" + parent + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unpack(JarEntryData data, File file) throws IOException {
|
private void unpack(JarEntryData data, File file) throws IOException {
|
||||||
|
@ -50,27 +50,33 @@ public abstract class TestJarCreator {
|
|||||||
writeDirEntry(jarOutputStream, "special/");
|
writeDirEntry(jarOutputStream, "special/");
|
||||||
writeEntry(jarOutputStream, "special/\u00EB.dat", '\u00EB');
|
writeEntry(jarOutputStream, "special/\u00EB.dat", '\u00EB');
|
||||||
|
|
||||||
JarEntry nestedEntry = new JarEntry("nested.jar");
|
writeNestedEntry("nested.jar", unpackNested, jarOutputStream);
|
||||||
byte[] nestedJarData = getNestedJarData();
|
writeNestedEntry("another-nested.jar", unpackNested, jarOutputStream);
|
||||||
nestedEntry.setSize(nestedJarData.length);
|
|
||||||
nestedEntry.setCompressedSize(nestedJarData.length);
|
|
||||||
if (unpackNested) {
|
|
||||||
nestedEntry.setComment("UNPACK:0000000000000000000000000000000000000000");
|
|
||||||
}
|
|
||||||
CRC32 crc32 = new CRC32();
|
|
||||||
crc32.update(nestedJarData);
|
|
||||||
nestedEntry.setCrc(crc32.getValue());
|
|
||||||
|
|
||||||
nestedEntry.setMethod(ZipEntry.STORED);
|
|
||||||
jarOutputStream.putNextEntry(nestedEntry);
|
|
||||||
jarOutputStream.write(nestedJarData);
|
|
||||||
jarOutputStream.closeEntry();
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
jarOutputStream.close();
|
jarOutputStream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void writeNestedEntry(String name, boolean unpackNested,
|
||||||
|
JarOutputStream jarOutputStream) throws Exception, IOException {
|
||||||
|
JarEntry nestedEntry = new JarEntry(name);
|
||||||
|
byte[] nestedJarData = getNestedJarData();
|
||||||
|
nestedEntry.setSize(nestedJarData.length);
|
||||||
|
nestedEntry.setCompressedSize(nestedJarData.length);
|
||||||
|
if (unpackNested) {
|
||||||
|
nestedEntry.setComment("UNPACK:0000000000000000000000000000000000000000");
|
||||||
|
}
|
||||||
|
CRC32 crc32 = new CRC32();
|
||||||
|
crc32.update(nestedJarData);
|
||||||
|
nestedEntry.setCrc(crc32.getValue());
|
||||||
|
|
||||||
|
nestedEntry.setMethod(ZipEntry.STORED);
|
||||||
|
jarOutputStream.putNextEntry(nestedEntry);
|
||||||
|
jarOutputStream.write(nestedJarData);
|
||||||
|
jarOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] getNestedJarData() throws Exception {
|
private static byte[] getNestedJarData() throws Exception {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
JarOutputStream jarOutputStream = new JarOutputStream(byteArrayOutputStream);
|
JarOutputStream jarOutputStream = new JarOutputStream(byteArrayOutputStream);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2013 the original author or authors.
|
* Copyright 2012-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -31,6 +31,8 @@ import org.springframework.boot.loader.util.AsciiBytes;
|
|||||||
|
|
||||||
import static org.hamcrest.Matchers.endsWith;
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ import static org.junit.Assert.assertThat;
|
|||||||
* Tests for {@link JarFileArchive}.
|
* Tests for {@link JarFileArchive}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
public class JarFileArchiveTests {
|
public class JarFileArchiveTests {
|
||||||
|
|
||||||
@ -57,8 +60,7 @@ public class JarFileArchiveTests {
|
|||||||
|
|
||||||
private void setup(boolean unpackNested) throws Exception {
|
private void setup(boolean unpackNested) throws Exception {
|
||||||
this.rootJarFile = this.temporaryFolder.newFile();
|
this.rootJarFile = this.temporaryFolder.newFile();
|
||||||
this.rootJarFileUrl = rootJarFile.toURI().toString();
|
this.rootJarFileUrl = this.rootJarFile.toURI().toString();
|
||||||
System.out.println(rootJarFileUrl);
|
|
||||||
TestJarCreator.createTestJar(this.rootJarFile, unpackNested);
|
TestJarCreator.createTestJar(this.rootJarFile, unpackNested);
|
||||||
this.archive = new JarFileArchive(this.rootJarFile);
|
this.archive = new JarFileArchive(this.rootJarFile);
|
||||||
}
|
}
|
||||||
@ -72,7 +74,7 @@ public class JarFileArchiveTests {
|
|||||||
@Test
|
@Test
|
||||||
public void getEntries() throws Exception {
|
public void getEntries() throws Exception {
|
||||||
Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
|
Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
|
||||||
assertThat(entries.size(), equalTo(9));
|
assertThat(entries.size(), equalTo(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -98,6 +100,31 @@ public class JarFileArchiveTests {
|
|||||||
assertThat(nested.getUrl().toString(), endsWith(".jar"));
|
assertThat(nested.getUrl().toString(), endsWith(".jar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unpackedLocationsAreUniquePerArchive() throws Exception {
|
||||||
|
setup(true);
|
||||||
|
Entry entry = getEntriesMap(this.archive).get("nested.jar");
|
||||||
|
URL firstNested = this.archive.getNestedArchive(entry).getUrl();
|
||||||
|
setup(true);
|
||||||
|
entry = getEntriesMap(this.archive).get("nested.jar");
|
||||||
|
URL secondNested = this.archive.getNestedArchive(entry).getUrl();
|
||||||
|
assertThat(secondNested, is(not(equalTo(firstNested))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unpackedLocationsFromSameArchiveShareSameParent() throws Exception {
|
||||||
|
setup(true);
|
||||||
|
File nested = new File(this.archive
|
||||||
|
.getNestedArchive(getEntriesMap(this.archive).get("nested.jar")).getUrl()
|
||||||
|
.toURI());
|
||||||
|
File anotherNested = new File(
|
||||||
|
this.archive
|
||||||
|
.getNestedArchive(
|
||||||
|
getEntriesMap(this.archive).get("another-nested.jar"))
|
||||||
|
.getUrl().toURI());
|
||||||
|
assertThat(nested.getParent(), is(equalTo(anotherNested.getParent())));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFilteredArchive() throws Exception {
|
public void getFilteredArchive() throws Exception {
|
||||||
Archive filteredArchive = this.archive
|
Archive filteredArchive = this.archive
|
||||||
|
Loading…
Reference in New Issue
Block a user