Browse Source

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

master
Jaikiran Pai 6 years ago
parent
commit
593aff2d2e
4 changed files with 118 additions and 3 deletions
  1. +7
    -0
      WHATSNEW
  2. +48
    -0
      src/etc/testcases/core/antclassloader.xml
  3. +41
    -3
      src/main/org/apache/tools/ant/AntClassLoader.java
  4. +22
    -0
      src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java

+ 7
- 0
WHATSNEW View File

@@ -65,6 +65,13 @@ Other changes:
tasks that must deal with different character encodings in files, tasks that must deal with different character encodings in files,
file names and other string resources. 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 Changes from Ant 1.10.4 TO Ant 1.10.5
===================================== =====================================




+ 48
- 0
src/etc/testcases/core/antclassloader.xml View File

@@ -73,4 +73,52 @@ public class Foo {}
<target name="createNonJar"> <target name="createNonJar">
<touch file="${tmp.dir}/foo.jar"/> <touch file="${tmp.dir}/foo.jar"/>
</target> </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> </project>

+ 41
- 3
src/main/org/apache/tools/ant/AntClassLoader.java View File

@@ -45,12 +45,14 @@ import java.util.jar.JarFile;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.zip.ZipFile;


import org.apache.tools.ant.launch.Locator; import org.apache.tools.ant.launch.Locator;
import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.JavaEnvUtils; import org.apache.tools.ant.util.JavaEnvUtils;
import org.apache.tools.ant.util.LoaderUtils; 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.StringUtils;
import org.apache.tools.ant.util.VectorSet; import org.apache.tools.ant.util.VectorSet;
import org.apache.tools.zip.ZipLong; 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 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 { static {
registerAsParallelCapable(); 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(); + pathComponent.lastModified() + "-" + pathComponent.length();
String classpath = pathMap.get(absPathPlusTimeAndLength); String classpath = pathMap.get(absPathPlusTimeAndLength);
if (classpath == null) { if (classpath == null) {
try (JarFile jarFile = new JarFile(pathComponent)) {
try (JarFile jarFile = newJarFile(pathComponent)) {
final Manifest manifest = jarFile.getManifest(); final Manifest manifest = jarFile.getManifest();
if (manifest == null) { if (manifest == null) {
return; return;
@@ -785,7 +808,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo
} else { } else {
if (jarFile == null) { if (jarFile == null) {
if (file.exists()) { if (file.exists()) {
jarFile = new JarFile(file);
jarFile = newJarFile(file);
jarFiles.put(file, jarFile); jarFiles.put(file, jarFile);
} else { } else {
return null; return null;
@@ -1005,7 +1028,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo
log(msg, Project.MSG_WARN); log(msg, Project.MSG_WARN);
return null; return null;
} }
jarFile = new JarFile(file);
jarFile = newJarFile(file);
jarFiles.put(file, jarFile); jarFiles.put(file, jarFile);
} else { } else {
return null; 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});
}
} }

+ 22
- 0
src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java View File

@@ -33,7 +33,9 @@ import java.util.Enumeration;


import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.JavaEnvUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -233,6 +235,26 @@ public class AntClassLoaderTest {
assertFalse(acl.getResources("META-INF/MANIFEST.MF").hasMoreElements()); 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 { private static class EmptyLoader extends ClassLoader {
public URL getResource(String n) { public URL getResource(String n) {
return null; return null;


Loading…
Cancel
Save