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");
|
||||
* 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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ import org.springframework.boot.loader.util.AsciiBytes;
|
|||
* {@link Archive} implementation backed by a {@link JarFile}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class JarFileArchive extends Archive {
|
||||
|
||||
|
|
@ -53,6 +55,8 @@ public class JarFileArchive extends Archive {
|
|||
|
||||
private URL url;
|
||||
|
||||
private File tempUnpackFolder;
|
||||
|
||||
public JarFileArchive(File file) throws IOException {
|
||||
this(file, null);
|
||||
}
|
||||
|
|
@ -123,10 +127,25 @@ public class JarFileArchive extends Archive {
|
|||
}
|
||||
|
||||
private File getTempUnpackFolder() {
|
||||
File tempFolder = new File(System.getProperty("java.io.tmpdir"));
|
||||
File unpackFolder = new File(tempFolder, "spring-boot-libs");
|
||||
unpackFolder.mkdirs();
|
||||
return unpackFolder;
|
||||
if (this.tempUnpackFolder == null) {
|
||||
File tempFolder = new File(System.getProperty("java.io.tmpdir"));
|
||||
this.tempUnpackFolder = createUnpackFolder(tempFolder);
|
||||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -50,27 +50,33 @@ public abstract class TestJarCreator {
|
|||
writeDirEntry(jarOutputStream, "special/");
|
||||
writeEntry(jarOutputStream, "special/\u00EB.dat", '\u00EB');
|
||||
|
||||
JarEntry nestedEntry = new JarEntry("nested.jar");
|
||||
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();
|
||||
writeNestedEntry("nested.jar", unpackNested, jarOutputStream);
|
||||
writeNestedEntry("another-nested.jar", unpackNested, jarOutputStream);
|
||||
}
|
||||
finally {
|
||||
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 {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new 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");
|
||||
* 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.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
|
|
@ -38,6 +40,7 @@ import static org.junit.Assert.assertThat;
|
|||
* Tests for {@link JarFileArchive}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class JarFileArchiveTests {
|
||||
|
||||
|
|
@ -57,8 +60,7 @@ public class JarFileArchiveTests {
|
|||
|
||||
private void setup(boolean unpackNested) throws Exception {
|
||||
this.rootJarFile = this.temporaryFolder.newFile();
|
||||
this.rootJarFileUrl = rootJarFile.toURI().toString();
|
||||
System.out.println(rootJarFileUrl);
|
||||
this.rootJarFileUrl = this.rootJarFile.toURI().toString();
|
||||
TestJarCreator.createTestJar(this.rootJarFile, unpackNested);
|
||||
this.archive = new JarFileArchive(this.rootJarFile);
|
||||
}
|
||||
|
|
@ -72,7 +74,7 @@ public class JarFileArchiveTests {
|
|||
@Test
|
||||
public void getEntries() throws Exception {
|
||||
Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
|
||||
assertThat(entries.size(), equalTo(9));
|
||||
assertThat(entries.size(), equalTo(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -98,6 +100,31 @@ public class JarFileArchiveTests {
|
|||
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
|
||||
public void getFilteredArchive() throws Exception {
|
||||
Archive filteredArchive = this.archive
|
||||
|
|
|
|||
Loading…
Reference in New Issue