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,
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
=====================================



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

@@ -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>

+ 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.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});
}
}

+ 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.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;


Loading…
Cancel
Save