Fix JarFile issues when running on Windows

Fix 'fat jar' support for windows to correctly deal with URL and path
slash issues. The root cause of the original problem was caused by JAR
URLs not including a root slash (ie `file:C:/Users` vs `file:/C:/Users`)

Fixes gh-1145
This commit is contained in:
Phillip Webb 2014-06-25 14:32:31 -07:00
parent 1f36d4657f
commit 7654259f80
4 changed files with 42 additions and 63 deletions

View File

@ -70,7 +70,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
private final RandomAccessDataFile rootFile;
private final String name;
private final String pathFromRoot;
private final RandomAccessData data;
@ -99,32 +99,33 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
* @throws IOException
*/
JarFile(RandomAccessDataFile file) throws IOException {
this(file, file.getFile().getAbsolutePath(), file);
this(file, "", file);
}
/**
* Private constructor used to create a new {@link JarFile} either directly or from a
* nested entry.
* @param rootFile the root jar file
* @param name the name of this file
* @param pathFromRoot the name of this file
* @param data the underlying data
* @throws IOException
*/
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data)
throws IOException {
private JarFile(RandomAccessDataFile rootFile, String pathFromRoot,
RandomAccessData data) throws IOException {
super(rootFile.getFile());
CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data);
this.rootFile = rootFile;
this.name = name;
this.pathFromRoot = pathFromRoot;
this.data = getArchiveData(endRecord, data);
this.entries = loadJarEntries(endRecord);
}
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data,
List<JarEntryData> entries, JarEntryFilter... filters) throws IOException {
private JarFile(RandomAccessDataFile rootFile, String pathFromRoot,
RandomAccessData data, List<JarEntryData> entries, JarEntryFilter... filters)
throws IOException {
super(rootFile.getFile());
this.rootFile = rootFile;
this.name = name;
this.pathFromRoot = pathFromRoot;
this.data = data;
this.entries = filterEntries(entries, filters);
}
@ -364,7 +365,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
return null;
}
};
return new JarFile(this.rootFile, getName() + "!/"
return new JarFile(this.rootFile, this.pathFromRoot + "!/"
+ sourceEntry.getName().substring(0, sourceName.length() - 1), this.data,
this.entries, filter);
}
@ -375,8 +376,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
throw new IllegalStateException("Unable to open nested compressed entry "
+ sourceEntry.getName());
}
return new JarFile(this.rootFile, getName() + "!/" + sourceEntry.getName(),
sourceEntry.getData());
return new JarFile(this.rootFile, this.pathFromRoot + "!/"
+ sourceEntry.getName(), sourceEntry.getData());
}
/**
@ -387,7 +388,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
*/
public synchronized JarFile getFilteredJarFile(JarEntryFilter... filters)
throws IOException {
return new JarFile(this.rootFile, getName(), this.data, this.entries, filters);
return new JarFile(this.rootFile, this.pathFromRoot, this.data, this.entries,
filters);
}
private JarEntry getContainedEntry(ZipEntry zipEntry) throws IOException {
@ -416,7 +418,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
*/
public URL getUrl() throws MalformedURLException {
Handler handler = new Handler(this);
String file = "file:" + getName(PathForm.SYSTEM_INDEPENDENT) + "!/";
String file = this.rootFile.getFile().toURI() + this.pathFromRoot + "!/";
return new URL("jar", "", -1, file, handler);
}
@ -427,15 +429,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
@Override
public String getName() {
return getName(PathForm.SYSTEM_DEPENDENT);
}
private String getName(PathForm pathForm) {
if (pathForm == PathForm.SYSTEM_INDEPENDENT && File.separatorChar != '/') {
return this.name.replace(File.separatorChar, '/');
}
return this.name;
String path = this.pathFromRoot;
return this.rootFile.getFile() + path;
}
/**
@ -463,20 +458,4 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
}
}
/**
* Different forms that paths can be returned.
*/
private static enum PathForm {
/**
* Use system dependent paths (i.e. include backslashes on Windows)
*/
SYSTEM_DEPENDENT,
/**
* Use system independent paths (i.e. replace backslashes on Windows)
*/
SYSTEM_INDEPENDENT
}
}

View File

@ -113,9 +113,8 @@ public class ExplodedArchiveTests {
public void getNestedArchive() throws Exception {
Entry entry = getEntriesMap(this.archive).get("nested.jar");
Archive nested = this.archive.getNestedArchive(entry);
assertThat(nested.getUrl().toString(),
equalTo("jar:file:" + this.rootFolder.getPath() + File.separator
+ "nested.jar!/"));
assertThat(nested.getUrl().toString(), equalTo("jar:" + this.rootFolder.toURI()
+ "nested.jar!/"));
}
@Test

View File

@ -48,6 +48,8 @@ public class JarFileArchiveTests {
private JarFileArchive archive;
private String rootJarFileUrl;
@Before
public void setup() throws Exception {
setup(false);
@ -55,6 +57,8 @@ public class JarFileArchiveTests {
private void setup(boolean unpackNested) throws Exception {
this.rootJarFile = this.temporaryFolder.newFile();
this.rootJarFileUrl = rootJarFile.toURI().toString();
System.out.println(rootJarFileUrl);
TestJarCreator.createTestJar(this.rootJarFile, unpackNested);
this.archive = new JarFileArchive(this.rootJarFile);
}
@ -74,7 +78,7 @@ public class JarFileArchiveTests {
@Test
public void getUrl() throws Exception {
URL url = this.archive.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
assertThat(url.toString(), equalTo("jar:" + this.rootJarFileUrl
+ "!/"));
}
@ -83,7 +87,7 @@ public class JarFileArchiveTests {
Entry entry = getEntriesMap(this.archive).get("nested.jar");
Archive nested = this.archive.getNestedArchive(entry);
assertThat(nested.getUrl().toString(),
equalTo("jar:file:" + this.rootJarFile.getPath() + "!/nested.jar!/"));
equalTo("jar:" + this.rootJarFileUrl + "!/nested.jar!/"));
}
@Test

View File

@ -97,6 +97,7 @@ public class JarFileTests {
assertThat(urlClassLoader.getResource("special/\u00EB.dat"), notNullValue());
assertThat(urlClassLoader.getResource("d/9.dat"), notNullValue());
jarFile.close();
}
@Test
@ -180,23 +181,21 @@ public class JarFileTests {
@Test
public void getUrl() throws Exception {
URL url = this.jarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
+ "!/"));
assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI() + "!/"));
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile(), sameInstance(this.jarFile));
assertThat(jarURLConnection.getJarEntry(), nullValue());
assertThat(jarURLConnection.getContentLength(), greaterThan(1));
assertThat(jarURLConnection.getContent(), sameInstance((Object) this.jarFile));
assertThat(jarURLConnection.getContentType(), equalTo("x-java/jar"));
assertThat(jarURLConnection.getJarFileURL().toString(), equalTo("file:"
+ this.rootJarFile));
assertThat(jarURLConnection.getJarFileURL().toURI(),
equalTo(this.rootJarFile.toURI()));
}
@Test
public void createEntryUrl() throws Exception {
URL url = new URL(this.jarFile.getUrl(), "1.dat");
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
+ "!/1.dat"));
assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI() + "!/1.dat"));
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile(), sameInstance(this.jarFile));
assertThat(jarURLConnection.getJarEntry(),
@ -209,7 +208,7 @@ public class JarFileTests {
@Test
public void getMissingEntryUrl() throws Exception {
URL url = new URL(this.jarFile.getUrl(), "missing.dat");
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI()
+ "!/missing.dat"));
this.thrown.expect(FileNotFoundException.class);
((JarURLConnection) url.openConnection()).getJarEntry();
@ -251,12 +250,12 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(-1));
URL url = nestedJarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI()
+ "!/nested.jar!/"));
JarURLConnection conn = (JarURLConnection) url.openConnection();
assertThat(conn.getJarFile(), sameInstance(nestedJarFile));
assertThat(conn.getJarFileURL().toString(), equalTo("jar:file:"
+ this.rootJarFile.getPath() + "!/nested.jar"));
assertThat(conn.getJarFileURL().toString(),
equalTo("jar:" + this.rootJarFile.toURI() + "!/nested.jar"));
}
@Test
@ -274,8 +273,7 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(-1));
URL url = nestedJarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
+ "!/d!/"));
assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI() + "!/d!/"));
assertThat(((JarURLConnection) url.openConnection()).getJarFile(),
sameInstance(nestedJarFile));
}
@ -285,7 +283,7 @@ public class JarFileTests {
JarFile nestedJarFile = this.jarFile.getNestedJarFile(this.jarFile
.getEntry("nested.jar"));
URL url = nestedJarFile.getJarEntry("3.dat").getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath()
assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI()
+ "!/nested.jar!/3.dat"));
InputStream inputStream = url.openStream();
assertThat(inputStream, notNullValue());
@ -295,7 +293,7 @@ public class JarFileTests {
@Test
public void createUrlFromString() throws Exception {
JarFile.registerUrlProtocolHandler();
String spec = "jar:file:" + this.rootJarFile.getPath() + "!/nested.jar!/3.dat";
String spec = "jar:" + this.rootJarFile.toURI() + "!/nested.jar!/3.dat";
URL url = new URL(spec);
assertThat(url.toString(), equalTo(spec));
InputStream inputStream = url.openStream();
@ -303,15 +301,15 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(3));
JarURLConnection connection = (JarURLConnection) url.openConnection();
assertThat(connection.getURL().toString(), equalTo(spec));
assertThat(connection.getJarFileURL().toString(), equalTo("jar:file:"
+ this.rootJarFile.getPath() + "!/nested.jar"));
assertThat(connection.getJarFileURL().toString(), equalTo("jar:"
+ this.rootJarFile.toURI() + "!/nested.jar"));
assertThat(connection.getEntryName(), equalTo("3.dat"));
}
@Test
public void createNonNestedUrlFromString() throws Exception {
JarFile.registerUrlProtocolHandler();
String spec = "jar:file:" + this.rootJarFile.getPath() + "!/2.dat";
String spec = "jar:" + this.rootJarFile.toURI() + "!/2.dat";
URL url = new URL(spec);
assertThat(url.toString(), equalTo(spec));
InputStream inputStream = url.openStream();
@ -319,8 +317,7 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(2));
JarURLConnection connection = (JarURLConnection) url.openConnection();
assertThat(connection.getURL().toString(), equalTo(spec));
assertThat(connection.getJarFileURL().toString(), equalTo("file:"
+ this.rootJarFile.getPath()));
assertThat(connection.getJarFileURL().toURI(), equalTo(this.rootJarFile.toURI()));
assertThat(connection.getEntryName(), equalTo("2.dat"));
}