Repository: ant
Updated Branches:
  refs/heads/master ac46ff190 -> 593aff2d2


bz-62952 Make AntClassLoader multi-release jar aware when it deals with 
java.util.jar.JarFile


Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/593aff2d
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/593aff2d
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/593aff2d

Branch: refs/heads/master
Commit: 593aff2d2ea52a025cfe7da32155216719029a7d
Parents: ac46ff1
Author: Jaikiran Pai <jaiki...@apache.org>
Authored: Wed Dec 5 18:26:14 2018 +0530
Committer: Jaikiran Pai <jaiki...@apache.org>
Committed: Wed Dec 5 18:27:45 2018 +0530

----------------------------------------------------------------------
 WHATSNEW                                        |  7 +++
 src/etc/testcases/core/antclassloader.xml       | 48 ++++++++++++++++++++
 .../org/apache/tools/ant/AntClassLoader.java    | 44 ++++++++++++++++--
 .../apache/tools/ant/AntClassLoaderTest.java    | 22 +++++++++
 4 files changed, 118 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant/blob/593aff2d/WHATSNEW
----------------------------------------------------------------------
diff --git a/WHATSNEW b/WHATSNEW
index de54711..a74856e 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -65,6 +65,13 @@ Other changes:
    tasks that must deal with different character encodings in files,
    file names and other string resources.
 
+ * org.apache.tools.ant.AntClassLoader is now multi-release jar aware.
+   Starting Java 9, jar files can be packaged as multi-release jars,
+   AntClassLoader now recognizes such multi-release jar files while
+   loading resources at runtime in Java 9+ runtime environments.
+   Bugzilla Report 62952
+
+
 Changes from Ant 1.10.4 TO Ant 1.10.5
 =====================================
 

http://git-wip-us.apache.org/repos/asf/ant/blob/593aff2d/src/etc/testcases/core/antclassloader.xml
----------------------------------------------------------------------
diff --git a/src/etc/testcases/core/antclassloader.xml 
b/src/etc/testcases/core/antclassloader.xml
index 045428d..db6f0ff 100644
--- a/src/etc/testcases/core/antclassloader.xml
+++ b/src/etc/testcases/core/antclassloader.xml
@@ -73,4 +73,52 @@ public class Foo {}
     <target name="createNonJar">
       <touch file="${tmp.dir}/foo.jar"/>
     </target>
+
+    <target name="testMRJar" description="tests AntClassLoader support for 
multi-release jars.
+                            see 
https://bz.apache.org/bugzilla/show_bug.cgi?id=62952";>
+        <mkdir dir="${tmp.dir}/mrjar/org/example"/>
+        <mkdir dir="${tmp.dir}/mrjar-9/org/example"/>
+        <!-- default version of the class -->
+        <echo file="${tmp.dir}/mrjar/org/example/MRJarTest.java"><![CDATA[
+                package org.example;
+                public class MRJarTest {
+                    public static void main(String[] args) {
+                        System.out.println("mrjar test result = default");
+                    }
+                }
+            ]]>
+        </echo>
+        <!-- Java runtime version 9 of the class -->
+        <echo file="${tmp.dir}/mrjar-9/org/example/MRJarTest.java"><![CDATA[
+                package org.example;
+                public class MRJarTest {
+                    public static void main(String[] args) {
+                        System.out.println("mrjar test result = 9");
+                    }
+                }
+            ]]>
+        </echo>
+        <!-- compile these classes -->
+        <javac srcdir="${tmp.dir}/mrjar" destdir="${tmp.dir}/mrjar"/>
+        <javac srcdir="${tmp.dir}/mrjar-9" destdir="${tmp.dir}/mrjar-9"/>
+
+        <!-- create multi-release jar file -->
+        <jar destfile="${tmp.dir}/mrjar.jar">
+            <manifest>
+                <attribute name="Multi-Release" value="true"/>
+            </manifest>
+            <!-- default classes -->
+            <fileset dir="${tmp.dir}/mrjar" includes="**/*.class"/>
+            <!-- Java 9 specific classes -->
+            <zipfileset prefix="META-INF/versions/9/" dir="${tmp.dir}/mrjar-9" 
includes="**/*.class"/>
+        </jar>
+
+        <!-- now run the class present in the multi-release jar -->
+        <java classname="org.example.MRJarTest" failonerror="true">
+            <classpath>
+                <pathelement location="${tmp.dir}/mrjar.jar"/>
+            </classpath>
+        </java>
+
+    </target>
 </project>

http://git-wip-us.apache.org/repos/asf/ant/blob/593aff2d/src/main/org/apache/tools/ant/AntClassLoader.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/AntClassLoader.java 
b/src/main/org/apache/tools/ant/AntClassLoader.java
index 3ab7c62..59ee175 100644
--- a/src/main/org/apache/tools/ant/AntClassLoader.java
+++ b/src/main/org/apache/tools/ant/AntClassLoader.java
@@ -45,12 +45,14 @@ import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.zip.ZipFile;
 
 import org.apache.tools.ant.launch.Locator;
 import org.apache.tools.ant.types.Path;
 import org.apache.tools.ant.util.FileUtils;
 import org.apache.tools.ant.util.JavaEnvUtils;
 import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.ReflectUtil;
 import org.apache.tools.ant.util.StringUtils;
 import org.apache.tools.ant.util.VectorSet;
 import org.apache.tools.zip.ZipLong;
@@ -76,8 +78,29 @@ public class AntClassLoader extends ClassLoader implements 
SubBuildListener, Clo
 
     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
 
+    private static final boolean IS_ATLEAST_JAVA9 = 
JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_9);
+    // constructs needed to create (via reflection) a java.util.jar.JarFile 
instance when Java runtime version is >= 9
+    private static final Class[] MR_JARFILE_CTOR_ARGS;
+    private static final Object MR_JARFILE_CTOR_RUNTIME_VERSION_VAL;
+
     static {
         registerAsParallelCapable();
+        if (IS_ATLEAST_JAVA9) {
+            Class[] ctorArgs = null;
+            Object runtimeVersionVal = null;
+            try {
+                final Class<?> runtimeVersionClass = 
Class.forName("java.lang.Runtime$Version");
+                ctorArgs = new Class[] {File.class, boolean.class, int.class, 
runtimeVersionClass};
+                runtimeVersionVal = 
Runtime.class.getDeclaredMethod("version").invoke(null);
+            } catch (Exception e) {
+                // ignore - we consider this as multi-release jar unsupported
+            }
+            MR_JARFILE_CTOR_ARGS = ctorArgs;
+            MR_JARFILE_CTOR_RUNTIME_VERSION_VAL = runtimeVersionVal;
+        } else {
+            MR_JARFILE_CTOR_ARGS = null;
+            MR_JARFILE_CTOR_RUNTIME_VERSION_VAL = null;
+        }
     }
 
     /**
@@ -500,7 +523,7 @@ public class AntClassLoader extends ClassLoader implements 
SubBuildListener, Clo
                 + pathComponent.lastModified() + "-" + pathComponent.length();
         String classpath = pathMap.get(absPathPlusTimeAndLength);
         if (classpath == null) {
-            try (JarFile jarFile = new JarFile(pathComponent)) {
+            try (JarFile jarFile = newJarFile(pathComponent)) {
                 final Manifest manifest = jarFile.getManifest();
                 if (manifest == null) {
                     return;
@@ -785,7 +808,7 @@ public class AntClassLoader extends ClassLoader implements 
SubBuildListener, Clo
             } else {
                 if (jarFile == null) {
                     if (file.exists()) {
-                        jarFile = new JarFile(file);
+                        jarFile = newJarFile(file);
                         jarFiles.put(file, jarFile);
                     } else {
                         return null;
@@ -1005,7 +1028,7 @@ public class AntClassLoader extends ClassLoader 
implements SubBuildListener, Clo
                             log(msg, Project.MSG_WARN);
                             return null;
                         }
-                        jarFile = new JarFile(file);
+                        jarFile = newJarFile(file);
                         jarFiles.put(file, jarFile);
                     } else {
                         return null;
@@ -1570,4 +1593,19 @@ public class AntClassLoader extends ClassLoader 
implements SubBuildListener, Clo
         }
     }
 
+    /**
+     *
+     * @param file The file representing the jar
+     * @return Returns a {@link JarFile} instance, which is constructed based 
upon the Java runtime version.
+     *         Depending on the Java runtime version, the returned instance 
may or may not be {@code multi-release}
+     *         aware (a feature introduced in Java 9)
+     * @throws IOException
+     */
+    private static JarFile newJarFile(final File file) throws IOException {
+        if (!IS_ATLEAST_JAVA9 || MR_JARFILE_CTOR_ARGS == null || 
MR_JARFILE_CTOR_RUNTIME_VERSION_VAL == null) {
+            return new JarFile(file);
+        }
+        return ReflectUtil.newInstance(JarFile.class, MR_JARFILE_CTOR_ARGS,
+                new Object[] {file, true, ZipFile.OPEN_READ, 
MR_JARFILE_CTOR_RUNTIME_VERSION_VAL});
+    }
 }

http://git-wip-us.apache.org/repos/asf/ant/blob/593aff2d/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java
----------------------------------------------------------------------
diff --git a/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java 
b/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java
index 9adde4a..029d786 100644
--- a/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java
+++ b/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java
@@ -33,7 +33,9 @@ import java.util.Enumeration;
 
 import org.apache.tools.ant.types.Path;
 import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -233,6 +235,26 @@ public class AntClassLoaderTest {
         
assertFalse(acl.getResources("META-INF/MANIFEST.MF").hasMoreElements());
     }
 
+    /**
+     * Tests that {@link AntClassLoader} supports multi-release jar files 
while dealing with
+     * runtime resources in Java 9+ runtime environments.
+     *
+     * @see <a 
href="bz-62952">https://bz.apache.org/bugzilla/show_bug.cgi?id=62952</a>
+     */
+    @Test
+    public void testMultiReleaseJar() {
+        buildRule.executeTarget("testMRJar");
+        final boolean atleastJava9 = 
JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_9);
+        final String targetOutput = buildRule.getOutput();
+        Assert.assertNotNull("Multi-release jar test did not generate any 
output", targetOutput);
+        if (atleastJava9) {
+            Assert.assertTrue("Unexpected output from multi-release jar test 
for Java runtime >= 9",
+                    targetOutput.contains("mrjar test result = 9"));
+        } else {
+            Assert.assertTrue("Unexpected output from multi-release jar test", 
targetOutput.contains("mrjar test result = default"));
+        }
+    }
+
     private static class EmptyLoader extends ClassLoader {
         public URL getResource(String n) {
             return null;

Reply via email to