diff --git a/emjar-maven-plugin/README.md b/emjar-maven-plugin/README.md index 0c96098..9228a25 100644 --- a/emjar-maven-plugin/README.md +++ b/emjar-maven-plugin/README.md @@ -46,7 +46,7 @@ Maven Plugin Mojo for building bundling jars that contain dependency artifact ja com.comoyo.commons emjar-maven-plugin - 1.0.0 + 1.0.33 @@ -67,7 +67,7 @@ Maven Plugin Mojo for building bundling jars that contain dependency artifact ja com.comoyo.commons emjar-maven-plugin - 1.0.0 + 1.0.33 diff --git a/emjar/pom.xml b/emjar/pom.xml index d6b1c52..f083ff2 100644 --- a/emjar/pom.xml +++ b/emjar/pom.xml @@ -47,5 +47,11 @@ 4.11 test + + com.google.guava + guava + ${google.guava.version} + test + diff --git a/emjar/src/main/java/com/comoyo/emjar/EmJarClassLoader.java b/emjar/src/main/java/com/comoyo/emjar/EmJarClassLoader.java index 5812a5d..dff0a80 100644 --- a/emjar/src/main/java/com/comoyo/emjar/EmJarClassLoader.java +++ b/emjar/src/main/java/com/comoyo/emjar/EmJarClassLoader.java @@ -17,6 +17,8 @@ package com.comoyo.emjar; import java.net.JarURLConnection; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; @@ -29,6 +31,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; import java.io.File; import java.io.IOException; @@ -67,6 +71,9 @@ public class EmJarClassLoader extends URLClassLoader { + public final static String SEPARATOR = "!/"; + + private final static Logger logger = Logger.getLogger(EmJarClassLoader.class.getName()); private final static HandlerFactory factory = new HandlerFactory(); static { @@ -79,17 +86,21 @@ public class EmJarClassLoader public EmJarClassLoader() { - super(getClassPath(), null, factory); + super(getClassPath(System.getProperties()), null, factory); } public EmJarClassLoader(ClassLoader parent) { - super(getClassPath(), parent, factory); + super(getClassPath(System.getProperties()), parent, factory); } - private static URL[] getClassPath() + protected EmJarClassLoader(Properties props) + { + super(getClassPath(props), null, factory); + } + + private static URL[] getClassPath(final Properties props) { - final Properties props = System.getProperties(); final String classPath = props.getProperty("java.class.path"); final String pathSep = props.getProperty("path.separator"); final String fileSep = props.getProperty("file.separator"); @@ -102,19 +113,20 @@ private static URL[] getClassPath() } final String full = elem.startsWith(fileSep) ? elem : userDir + fileSep + elem; try { - urls.add(new URL("file:" + full)); + urls.add(new URI("file", full, null).toURL()); final JarFile jar = new JarFile(elem); - Enumeration embedded = jar.entries(); + final Enumeration embedded = jar.entries(); while (embedded.hasMoreElements()) { final JarEntry entry = embedded.nextElement(); if (entry.getName().endsWith(".jar")) { - urls.add(new URL("jar:file:" + full + "!/" + entry.getName())); + urls.add(new URI("jar:file", full + SEPARATOR + entry.getName(), null).toURL()); } } jar.close(); } - catch (IOException e) { - e.printStackTrace(); + catch (IOException|URISyntaxException e) { + logger.log(Level.SEVERE, "Unable to process classpath entry " + elem, e); + // Trying to get by on the classpath entries we can process. } } return urls.toArray(new URL[0]); @@ -151,23 +163,45 @@ private static class Handler protected URLConnection openConnection(URL url) throws IOException { - final String spec = url.getFile(); - if (!spec.startsWith("jar:file:")) { - throw new IOException("Unable to handle " + spec); + final URI bundle; + final URI file; + try { + final URI nested = url.toURI(); + if (!"jar".equals(nested.getScheme())) { + throw new IOException( + "Unexpected nested scheme passed to openConnection (expeced jar): " + + nested.getScheme()); + } + bundle = new URI(nested.getRawSchemeSpecificPart()); + if (!"jar".equals(bundle.getScheme())) { + throw new IOException( + "Unexpected bundle scheme passed to openConnection (expected jar): " + + bundle.getScheme()); + } + file = new URI(bundle.getRawSchemeSpecificPart()); + if (!"file".equals(file.getScheme())) { + throw new IOException( + "Unexpected location scheme passed to openConnection (expected file): " + + file.getScheme()); + } + } + catch (URISyntaxException e) { + throw new IOException(e); } - JarURLConnection conn = connections.get(spec); + final String path = file.getSchemeSpecificPart(); + JarURLConnection conn = connections.get(path); if (conn == null) { synchronized (connections) { - conn = connections.get(spec); + conn = connections.get(path); if (conn == null) { - final int i = spec.indexOf("!/"); - final int j = spec.indexOf("!/", i + 1); + final int i = path.indexOf(SEPARATOR); + final int j = path.indexOf(SEPARATOR, i + 1); if (i < 0 || j < 0) { - throw new IOException("Unable to parse " + spec); + throw new IOException("Unable to parse " + path); } - final String root = spec.substring(9, i); - final String nested = spec.substring(i + 2, j); - final String entry = spec.substring(j + 2); + final String root = path.substring(0, i); + final String nested = path.substring(i + SEPARATOR.length(), j); + final String entry = path.substring(j + SEPARATOR.length()); Map> rootJar = rootJars.get(root); @@ -186,13 +220,13 @@ protected URLConnection openConnection(URL url) = rootJar.get(nested); if (descriptors != null) { conn = new OndemandEmbeddedJar.Connection( - new URL(spec), root, descriptors, entry); + bundle.toURL(), root, descriptors, entry); } else { conn = new PreloadedEmbeddedJar.Connection( - new URL(spec), root, nested, entry); + bundle.toURL(), root, nested, entry); } - connections.put(spec, conn); + connections.put(path, conn); } } } diff --git a/emjar/src/test/java/com/comoyo/emjar/EmJarClassLoaderTest.java b/emjar/src/test/java/com/comoyo/emjar/EmJarClassLoaderTest.java new file mode 100644 index 0000000..9bab4ab --- /dev/null +++ b/emjar/src/test/java/com/comoyo/emjar/EmJarClassLoaderTest.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2014 Telenor Digital AS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.comoyo.emjar; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Properties; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import com.google.common.base.Joiner; + +import static org.junit.Assert.*; + +@RunWith(JUnit4.class) +public class EmJarClassLoaderTest extends EmJarTest +{ + @Test + public void testClassPathQuoting() + throws Exception + { + final FilenameFilter jarFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }; + final File bundle = getResourceFile("bundle-s-large.jar"); + final File allJars[] = bundle.getParentFile().listFiles(jarFilter); + + final Properties props = new Properties(); + props.setProperty("java.class.path", Joiner.on(":").join(allJars)); + props.setProperty("path.separator", ":"); + props.setProperty("file.separator", "/"); + props.setProperty("user.dir", "/tmp"); + final EmJarClassLoader loader = new EmJarClassLoader(props); + final InputStream is = loader.getResourceAsStream("entry-" + WEIRD + ".txt"); + final BufferedReader entry = new BufferedReader(new InputStreamReader(is)); + assertEquals("Contents mismatch for weird entry", WEIRD, entry.readLine()); + } +} diff --git a/emjar/src/test/java/com/comoyo/emjar/EmJarTest.java b/emjar/src/test/java/com/comoyo/emjar/EmJarTest.java index e46edcb..afe9fca 100644 --- a/emjar/src/test/java/com/comoyo/emjar/EmJarTest.java +++ b/emjar/src/test/java/com/comoyo/emjar/EmJarTest.java @@ -17,18 +17,22 @@ package com.comoyo.emjar; import java.io.File; -import java.net.URL; -import java.net.URLDecoder; +import java.net.URI; +import java.net.URISyntaxException; public abstract class EmJarTest { - protected File getResourceFile(String name) { + public static final String WEIRD = "æøå😱 %&;*+`\"\\-weird"; + + protected File getResourceFile(String name) + throws URISyntaxException + { final ClassLoader cl = getClass().getClassLoader(); - final URL url = cl.getResource("com/comoyo/emjar/" + name); - if (!"file".equals(url.getProtocol())) { + final URI uri = cl.getResource("com/comoyo/emjar/" + name).toURI(); + if (!"file".equals(uri.getScheme())) { throw new IllegalArgumentException( - "Resource " + name + " not present as file (" + url + ")"); + "Resource " + name + " not present as file (" + uri + ")"); } - return new File(URLDecoder.decode(url.getPath())); + return new File(uri.getSchemeSpecificPart()); } } diff --git a/emjar/src/test/java/com/comoyo/emjar/JarTest.java b/emjar/src/test/java/com/comoyo/emjar/JarTest.java index f25bf60..0c245a6 100644 --- a/emjar/src/test/java/com/comoyo/emjar/JarTest.java +++ b/emjar/src/test/java/com/comoyo/emjar/JarTest.java @@ -70,4 +70,11 @@ public void testAllBundles() } } } + + @Test + public void testQuoting() + throws Exception + { + testJarBundle(WEIRD); + } } diff --git a/emjar/src/test/java/com/comoyo/emjar/OndemandEmbeddedJarTest.java b/emjar/src/test/java/com/comoyo/emjar/OndemandEmbeddedJarTest.java index 12a127f..f06e669 100644 --- a/emjar/src/test/java/com/comoyo/emjar/OndemandEmbeddedJarTest.java +++ b/emjar/src/test/java/com/comoyo/emjar/OndemandEmbeddedJarTest.java @@ -19,7 +19,7 @@ import java.io.File; import java.io.InputStream; import java.net.JarURLConnection; -import java.net.URL; +import java.net.URI; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -42,7 +42,7 @@ public JarURLConnection getJarUrlConnection(File root, String jarName, String en = scanner.scan(); assertNotNull("Descriptor returned from ZipScanner was null", desc); return new OndemandEmbeddedJar.Connection( - new URL("jar:file:" + root.getPath() + "!/" + jarName + "!/"), + new URI("jar:file", root.getPath() + "!/" + jarName + "!/", null).toURL(), root.getPath(), desc.get(jarName), entryName); diff --git a/emjar/src/test/java/com/comoyo/emjar/PreloadedEmbeddedJarTest.java b/emjar/src/test/java/com/comoyo/emjar/PreloadedEmbeddedJarTest.java index 94f93b6..09b6e37 100644 --- a/emjar/src/test/java/com/comoyo/emjar/PreloadedEmbeddedJarTest.java +++ b/emjar/src/test/java/com/comoyo/emjar/PreloadedEmbeddedJarTest.java @@ -19,7 +19,7 @@ import java.io.File; import java.io.InputStream; import java.net.JarURLConnection; -import java.net.URL; +import java.net.URI; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -37,7 +37,7 @@ public JarURLConnection getJarUrlConnection(File root, String jarName, String en throws Exception { return new PreloadedEmbeddedJar.Connection( - new URL("jar:file:" + root.getPath() + "!/" + jarName + "!/"), + new URI("jar:file", root.getPath() + "!/" + jarName + "!/", null).toURL(), root.getPath(), jarName, entryName); diff --git "a/emjar/src/test/resources/com/comoyo/emjar/bundle-\303\246\303\270\303\245\360\237\230\261 %&;*+`\"\\-weird.jar" "b/emjar/src/test/resources/com/comoyo/emjar/bundle-\303\246\303\270\303\245\360\237\230\261 %&;*+`\"\\-weird.jar" new file mode 100644 index 0000000..10454d1 Binary files /dev/null and "b/emjar/src/test/resources/com/comoyo/emjar/bundle-\303\246\303\270\303\245\360\237\230\261 %&;*+`\"\\-weird.jar" differ diff --git a/emjar/src/test/resources/com/comoyo/emjar/create-zips.pl b/emjar/src/test/resources/com/comoyo/emjar/create-zips.pl index c4f8da9..3f1a051 100755 --- a/emjar/src/test/resources/com/comoyo/emjar/create-zips.pl +++ b/emjar/src/test/resources/com/comoyo/emjar/create-zips.pl @@ -55,3 +55,4 @@ sub createZip { createZip("s-large", {Stream => 0}, {}, 1); createZip("S-large", {Stream => 1}, {}, 1); createZip("Z-large", {}, {}, 1, ZIP_CM_DEFLATE); +createZip("æøå😱 %&;*+`\"\\-weird", {}, {});