Commit 7654259f authored by Phillip Webb's avatar Phillip Webb

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
parent 1f36d465
...@@ -70,7 +70,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -70,7 +70,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
private final RandomAccessDataFile rootFile; private final RandomAccessDataFile rootFile;
private final String name; private final String pathFromRoot;
private final RandomAccessData data; private final RandomAccessData data;
...@@ -99,32 +99,33 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -99,32 +99,33 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
* @throws IOException * @throws IOException
*/ */
JarFile(RandomAccessDataFile file) 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 * Private constructor used to create a new {@link JarFile} either directly or from a
* nested entry. * nested entry.
* @param rootFile the root jar file * @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 * @param data the underlying data
* @throws IOException * @throws IOException
*/ */
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data) private JarFile(RandomAccessDataFile rootFile, String pathFromRoot,
throws IOException { RandomAccessData data) throws IOException {
super(rootFile.getFile()); super(rootFile.getFile());
CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data); CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data);
this.rootFile = rootFile; this.rootFile = rootFile;
this.name = name; this.pathFromRoot = pathFromRoot;
this.data = getArchiveData(endRecord, data); this.data = getArchiveData(endRecord, data);
this.entries = loadJarEntries(endRecord); this.entries = loadJarEntries(endRecord);
} }
private JarFile(RandomAccessDataFile rootFile, String name, RandomAccessData data, private JarFile(RandomAccessDataFile rootFile, String pathFromRoot,
List<JarEntryData> entries, JarEntryFilter... filters) throws IOException { RandomAccessData data, List<JarEntryData> entries, JarEntryFilter... filters)
throws IOException {
super(rootFile.getFile()); super(rootFile.getFile());
this.rootFile = rootFile; this.rootFile = rootFile;
this.name = name; this.pathFromRoot = pathFromRoot;
this.data = data; this.data = data;
this.entries = filterEntries(entries, filters); this.entries = filterEntries(entries, filters);
} }
...@@ -364,7 +365,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -364,7 +365,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
return null; return null;
} }
}; };
return new JarFile(this.rootFile, getName() + "!/" return new JarFile(this.rootFile, this.pathFromRoot + "!/"
+ sourceEntry.getName().substring(0, sourceName.length() - 1), this.data, + sourceEntry.getName().substring(0, sourceName.length() - 1), this.data,
this.entries, filter); this.entries, filter);
} }
...@@ -375,8 +376,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -375,8 +376,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
throw new IllegalStateException("Unable to open nested compressed entry " throw new IllegalStateException("Unable to open nested compressed entry "
+ sourceEntry.getName()); + sourceEntry.getName());
} }
return new JarFile(this.rootFile, getName() + "!/" + sourceEntry.getName(), return new JarFile(this.rootFile, this.pathFromRoot + "!/"
sourceEntry.getData()); + sourceEntry.getName(), sourceEntry.getData());
} }
/** /**
...@@ -387,7 +388,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -387,7 +388,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
*/ */
public synchronized JarFile getFilteredJarFile(JarEntryFilter... filters) public synchronized JarFile getFilteredJarFile(JarEntryFilter... filters)
throws IOException { 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 { private JarEntry getContainedEntry(ZipEntry zipEntry) throws IOException {
...@@ -416,7 +418,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -416,7 +418,7 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
*/ */
public URL getUrl() throws MalformedURLException { public URL getUrl() throws MalformedURLException {
Handler handler = new Handler(this); 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); return new URL("jar", "", -1, file, handler);
} }
...@@ -427,15 +429,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -427,15 +429,8 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD
@Override @Override
public String getName() { public String getName() {
return getName(PathForm.SYSTEM_DEPENDENT); String path = this.pathFromRoot;
} return this.rootFile.getFile() + path;
private String getName(PathForm pathForm) {
if (pathForm == PathForm.SYSTEM_INDEPENDENT && File.separatorChar != '/') {
return this.name.replace(File.separatorChar, '/');
}
return this.name;
} }
/** /**
...@@ -463,20 +458,4 @@ public class JarFile extends java.util.jar.JarFile implements Iterable<JarEntryD ...@@ -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
}
} }
...@@ -113,9 +113,8 @@ public class ExplodedArchiveTests { ...@@ -113,9 +113,8 @@ public class ExplodedArchiveTests {
public void getNestedArchive() throws Exception { public void getNestedArchive() throws Exception {
Entry entry = getEntriesMap(this.archive).get("nested.jar"); Entry entry = getEntriesMap(this.archive).get("nested.jar");
Archive nested = this.archive.getNestedArchive(entry); Archive nested = this.archive.getNestedArchive(entry);
assertThat(nested.getUrl().toString(), assertThat(nested.getUrl().toString(), equalTo("jar:" + this.rootFolder.toURI()
equalTo("jar:file:" + this.rootFolder.getPath() + File.separator + "nested.jar!/"));
+ "nested.jar!/"));
} }
@Test @Test
......
...@@ -48,6 +48,8 @@ public class JarFileArchiveTests { ...@@ -48,6 +48,8 @@ public class JarFileArchiveTests {
private JarFileArchive archive; private JarFileArchive archive;
private String rootJarFileUrl;
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
setup(false); setup(false);
...@@ -55,6 +57,8 @@ public class JarFileArchiveTests { ...@@ -55,6 +57,8 @@ 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();
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);
} }
...@@ -74,7 +78,7 @@ public class JarFileArchiveTests { ...@@ -74,7 +78,7 @@ public class JarFileArchiveTests {
@Test @Test
public void getUrl() throws Exception { public void getUrl() throws Exception {
URL url = this.archive.getUrl(); 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 { ...@@ -83,7 +87,7 @@ public class JarFileArchiveTests {
Entry entry = getEntriesMap(this.archive).get("nested.jar"); Entry entry = getEntriesMap(this.archive).get("nested.jar");
Archive nested = this.archive.getNestedArchive(entry); Archive nested = this.archive.getNestedArchive(entry);
assertThat(nested.getUrl().toString(), assertThat(nested.getUrl().toString(),
equalTo("jar:file:" + this.rootJarFile.getPath() + "!/nested.jar!/")); equalTo("jar:" + this.rootJarFileUrl + "!/nested.jar!/"));
} }
@Test @Test
......
...@@ -97,6 +97,7 @@ public class JarFileTests { ...@@ -97,6 +97,7 @@ public class JarFileTests {
assertThat(urlClassLoader.getResource("special/\u00EB.dat"), notNullValue()); assertThat(urlClassLoader.getResource("special/\u00EB.dat"), notNullValue());
assertThat(urlClassLoader.getResource("d/9.dat"), notNullValue()); assertThat(urlClassLoader.getResource("d/9.dat"), notNullValue());
jarFile.close(); jarFile.close();
} }
@Test @Test
...@@ -180,23 +181,21 @@ public class JarFileTests { ...@@ -180,23 +181,21 @@ public class JarFileTests {
@Test @Test
public void getUrl() throws Exception { public void getUrl() throws Exception {
URL url = this.jarFile.getUrl(); 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(); JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile(), sameInstance(this.jarFile)); assertThat(jarURLConnection.getJarFile(), sameInstance(this.jarFile));
assertThat(jarURLConnection.getJarEntry(), nullValue()); assertThat(jarURLConnection.getJarEntry(), nullValue());
assertThat(jarURLConnection.getContentLength(), greaterThan(1)); assertThat(jarURLConnection.getContentLength(), greaterThan(1));
assertThat(jarURLConnection.getContent(), sameInstance((Object) this.jarFile)); assertThat(jarURLConnection.getContent(), sameInstance((Object) this.jarFile));
assertThat(jarURLConnection.getContentType(), equalTo("x-java/jar")); assertThat(jarURLConnection.getContentType(), equalTo("x-java/jar"));
assertThat(jarURLConnection.getJarFileURL().toString(), equalTo("file:" assertThat(jarURLConnection.getJarFileURL().toURI(),
+ this.rootJarFile)); equalTo(this.rootJarFile.toURI()));
} }
@Test @Test
public void createEntryUrl() throws Exception { public void createEntryUrl() throws Exception {
URL url = new URL(this.jarFile.getUrl(), "1.dat"); URL url = new URL(this.jarFile.getUrl(), "1.dat");
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath() assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI() + "!/1.dat"));
+ "!/1.dat"));
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
assertThat(jarURLConnection.getJarFile(), sameInstance(this.jarFile)); assertThat(jarURLConnection.getJarFile(), sameInstance(this.jarFile));
assertThat(jarURLConnection.getJarEntry(), assertThat(jarURLConnection.getJarEntry(),
...@@ -209,7 +208,7 @@ public class JarFileTests { ...@@ -209,7 +208,7 @@ public class JarFileTests {
@Test @Test
public void getMissingEntryUrl() throws Exception { public void getMissingEntryUrl() throws Exception {
URL url = new URL(this.jarFile.getUrl(), "missing.dat"); 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")); + "!/missing.dat"));
this.thrown.expect(FileNotFoundException.class); this.thrown.expect(FileNotFoundException.class);
((JarURLConnection) url.openConnection()).getJarEntry(); ((JarURLConnection) url.openConnection()).getJarEntry();
...@@ -251,12 +250,12 @@ public class JarFileTests { ...@@ -251,12 +250,12 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(-1)); assertThat(inputStream.read(), equalTo(-1));
URL url = nestedJarFile.getUrl(); URL url = nestedJarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath() assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI()
+ "!/nested.jar!/")); + "!/nested.jar!/"));
JarURLConnection conn = (JarURLConnection) url.openConnection(); JarURLConnection conn = (JarURLConnection) url.openConnection();
assertThat(conn.getJarFile(), sameInstance(nestedJarFile)); assertThat(conn.getJarFile(), sameInstance(nestedJarFile));
assertThat(conn.getJarFileURL().toString(), equalTo("jar:file:" assertThat(conn.getJarFileURL().toString(),
+ this.rootJarFile.getPath() + "!/nested.jar")); equalTo("jar:" + this.rootJarFile.toURI() + "!/nested.jar"));
} }
@Test @Test
...@@ -274,8 +273,7 @@ public class JarFileTests { ...@@ -274,8 +273,7 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(-1)); assertThat(inputStream.read(), equalTo(-1));
URL url = nestedJarFile.getUrl(); URL url = nestedJarFile.getUrl();
assertThat(url.toString(), equalTo("jar:file:" + this.rootJarFile.getPath() assertThat(url.toString(), equalTo("jar:" + this.rootJarFile.toURI() + "!/d!/"));
+ "!/d!/"));
assertThat(((JarURLConnection) url.openConnection()).getJarFile(), assertThat(((JarURLConnection) url.openConnection()).getJarFile(),
sameInstance(nestedJarFile)); sameInstance(nestedJarFile));
} }
...@@ -285,7 +283,7 @@ public class JarFileTests { ...@@ -285,7 +283,7 @@ public class JarFileTests {
JarFile nestedJarFile = this.jarFile.getNestedJarFile(this.jarFile JarFile nestedJarFile = this.jarFile.getNestedJarFile(this.jarFile
.getEntry("nested.jar")); .getEntry("nested.jar"));
URL url = nestedJarFile.getJarEntry("3.dat").getUrl(); 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")); + "!/nested.jar!/3.dat"));
InputStream inputStream = url.openStream(); InputStream inputStream = url.openStream();
assertThat(inputStream, notNullValue()); assertThat(inputStream, notNullValue());
...@@ -295,7 +293,7 @@ public class JarFileTests { ...@@ -295,7 +293,7 @@ public class JarFileTests {
@Test @Test
public void createUrlFromString() throws Exception { public void createUrlFromString() throws Exception {
JarFile.registerUrlProtocolHandler(); 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); URL url = new URL(spec);
assertThat(url.toString(), equalTo(spec)); assertThat(url.toString(), equalTo(spec));
InputStream inputStream = url.openStream(); InputStream inputStream = url.openStream();
...@@ -303,15 +301,15 @@ public class JarFileTests { ...@@ -303,15 +301,15 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(3)); assertThat(inputStream.read(), equalTo(3));
JarURLConnection connection = (JarURLConnection) url.openConnection(); JarURLConnection connection = (JarURLConnection) url.openConnection();
assertThat(connection.getURL().toString(), equalTo(spec)); assertThat(connection.getURL().toString(), equalTo(spec));
assertThat(connection.getJarFileURL().toString(), equalTo("jar:file:" assertThat(connection.getJarFileURL().toString(), equalTo("jar:"
+ this.rootJarFile.getPath() + "!/nested.jar")); + this.rootJarFile.toURI() + "!/nested.jar"));
assertThat(connection.getEntryName(), equalTo("3.dat")); assertThat(connection.getEntryName(), equalTo("3.dat"));
} }
@Test @Test
public void createNonNestedUrlFromString() throws Exception { public void createNonNestedUrlFromString() throws Exception {
JarFile.registerUrlProtocolHandler(); JarFile.registerUrlProtocolHandler();
String spec = "jar:file:" + this.rootJarFile.getPath() + "!/2.dat"; String spec = "jar:" + this.rootJarFile.toURI() + "!/2.dat";
URL url = new URL(spec); URL url = new URL(spec);
assertThat(url.toString(), equalTo(spec)); assertThat(url.toString(), equalTo(spec));
InputStream inputStream = url.openStream(); InputStream inputStream = url.openStream();
...@@ -319,8 +317,7 @@ public class JarFileTests { ...@@ -319,8 +317,7 @@ public class JarFileTests {
assertThat(inputStream.read(), equalTo(2)); assertThat(inputStream.read(), equalTo(2));
JarURLConnection connection = (JarURLConnection) url.openConnection(); JarURLConnection connection = (JarURLConnection) url.openConnection();
assertThat(connection.getURL().toString(), equalTo(spec)); assertThat(connection.getURL().toString(), equalTo(spec));
assertThat(connection.getJarFileURL().toString(), equalTo("file:" assertThat(connection.getJarFileURL().toURI(), equalTo(this.rootJarFile.toURI()));
+ this.rootJarFile.getPath()));
assertThat(connection.getEntryName(), equalTo("2.dat")); assertThat(connection.getEntryName(), equalTo("2.dat"));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment