diff --git a/WHATSNEW b/WHATSNEW index 0ec97ca35..dbc7397c7 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -789,6 +789,14 @@ Other changes: task's (compiler) adapter class. Bugzilla Issue 11143. + * A new subclass org.apache.tools.ant.loader.AntClassLoader5 of + AntClassLoader has been added which overrides getResources + which became non-final in ClassLoader with Java5+ so + this method now behaves as expected. + The new subclass will be used by Ant internally if it is available + and Ant is running on Java5 or more recent. + Bugzilla Issue 46752. + Changes from Ant 1.7.0 TO Ant 1.7.1 ============================================= diff --git a/build.xml b/build.xml index 67baa33a0..47cceb369 100644 --- a/build.xml +++ b/build.xml @@ -175,6 +175,7 @@ + diff --git a/src/main/org/apache/tools/ant/AntClassLoader.java b/src/main/org/apache/tools/ant/AntClassLoader.java index 26464ca6a..b5ca8ccc2 100644 --- a/src/main/org/apache/tools/ant/AntClassLoader.java +++ b/src/main/org/apache/tools/ant/AntClassLoader.java @@ -46,6 +46,7 @@ import org.apache.tools.ant.util.CollectionUtils; 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.VectorSet; import org.apache.tools.ant.launch.Locator; @@ -910,6 +911,19 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return url; } + /** + * Finds all the resources with the given name. A resource is some + * data (images, audio, text, etc) that can be accessed by class + * code in a way that is independent of the location of the code. + * + *

Would override getResources if that wasn't final in Java + * 1.4.

+ */ + public Enumeration/**/ getNamedResources(String name) + throws IOException { + return findResources(name, false); + } + /** * Returns an enumeration of URLs representing all the resources with the * given name by searching the class loader's classpath. @@ -920,14 +934,34 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception IOException if I/O errors occurs (can't happen) */ protected Enumeration/**/ findResources(String name) throws IOException { + return findResources(name, true); + } + + /** + * Returns an enumeration of URLs representing all the resources with the + * given name by searching the class loader's classpath. + * + * @param name The resource name to search for. + * Must not be null. + * @param parentHasBeenSearched whether ClassLoader.this.parent + * has been searched - will be true if the method is (indirectly) + * called from ClassLoader.getResources + * @return an enumeration of URLs for the resources + * @exception IOException if I/O errors occurs (can't happen) + */ + protected Enumeration/**/ findResources(String name, + boolean parentHasBeenSearched) + throws IOException { Enumeration/**/ mine = new ResourceEnumeration(name); Enumeration/**/ base; - if (parent != null && parent != getParent()) { + if (parent != null && (!parentHasBeenSearched || parent != getParent())) { // Delegate to the parent: base = parent.getResources(name); - // Note: could cause overlaps in case ClassLoader.this.parent has matches. + // Note: could cause overlaps in case + // ClassLoader.this.parent has matches and + // parentHasBeenSearched is true } else { - // ClassLoader.this.parent is already delegated to from + // ClassLoader.this.parent is already delegated to for example from // ClassLoader.getResources, no need: base = new CollectionUtils.EmptyEnumeration(); } @@ -1456,6 +1490,22 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return "AntClassLoader[" + getClasspath() + "]"; } + private static Class subClassToLoad = null; + private static final Class[] CONSTRUCTOR_ARGS = new Class[] { + ClassLoader.class, Project.class, Path.class, Boolean.TYPE + }; + + static { + if (JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_5)) { + try { + subClassToLoad = + Class.forName("org.apache.tools.ant.loader.AntClassLoader5"); + } catch (ClassNotFoundException e) { + // this is Java5 but the installation is lacking our subclass + } + } + } + /** * Factory method */ @@ -1463,6 +1513,15 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { Project project, Path path, boolean parentFirst) { + if (subClassToLoad != null) { + return (AntClassLoader) + ReflectUtil.newInstance(subClassToLoad, + CONSTRUCTOR_ARGS, + new Object[] { + parent, project, path, + Boolean.valueOf(parentFirst) + }); + } return new AntClassLoader(parent, project, path, parentFirst); } diff --git a/src/main/org/apache/tools/ant/loader/AntClassLoader5.java b/src/main/org/apache/tools/ant/loader/AntClassLoader5.java new file mode 100644 index 000000000..232fb4186 --- /dev/null +++ b/src/main/org/apache/tools/ant/loader/AntClassLoader5.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.tools.ant.loader; + +import java.util.Enumeration; +import java.io.IOException; +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.Path; + +/** + * Overrides getResources which became non-final in Java5 + */ +public class AntClassLoader5 extends AntClassLoader { + /** + * Creates a classloader for the given project using the classpath given. + * + * @param parent The parent classloader to which unsatisfied loading + * attempts are delegated. May be null, + * in which case the classloader which loaded this + * class is used as the parent. + * @param project The project to which this classloader is to belong. + * Must not be null. + * @param classpath the classpath to use to load the classes. + * May be null, in which case no path + * elements are set up to start with. + * @param parentFirst If true, indicates that the parent + * classloader should be consulted before trying to + * load the a class through this loader. + */ + public AntClassLoader5(ClassLoader parent, Project project, + Path classpath, boolean parentFirst) { + super(parent, project, classpath, parentFirst); + } + + /** {@inheritDoc} */ + public Enumeration getResources(String name) throws IOException { + return getNamedResources(name); + } +} diff --git a/src/main/org/apache/tools/ant/util/ReflectUtil.java b/src/main/org/apache/tools/ant/util/ReflectUtil.java index 74d3e6553..53adb28e9 100644 --- a/src/main/org/apache/tools/ant/util/ReflectUtil.java +++ b/src/main/org/apache/tools/ant/util/ReflectUtil.java @@ -17,6 +17,7 @@ */ package org.apache.tools.ant.util; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.tools.ant.BuildException; @@ -35,6 +36,23 @@ public class ReflectUtil { private ReflectUtil() { } + /** + * Create an instance of a class using the constructor matching + * the given arguments. + * @since Ant 1.8.0 + */ + public static Object newInstance(Class ofClass, + Class[] argTypes, + Object[] args) { + try { + Constructor con = ofClass.getConstructor(argTypes); + return con.newInstance(args); + } catch (Exception t) { + throwBuildException(t); + return null; // NotReached + } + } + /** * Call a method on the object with no parameters. * @param obj the object to invoke the method on. diff --git a/src/tests/junit/org/apache/tools/ant/AntClassLoaderDelegationTest.java b/src/tests/junit/org/apache/tools/ant/AntClassLoaderDelegationTest.java index 4ee679443..6d483ecf3 100644 --- a/src/tests/junit/org/apache/tools/ant/AntClassLoaderDelegationTest.java +++ b/src/tests/junit/org/apache/tools/ant/AntClassLoaderDelegationTest.java @@ -99,12 +99,7 @@ public class AntClassLoaderDelegationTest extends TestCase { } private static List enum2List(Enumeration e) { - // JDK 1.4: return Collections.list(e); - List l = new ArrayList(); - while (e.hasMoreElements()) { - l.add(e.nextElement()); - } - return l; + return Collections.list(e); } /** Special loader that just knows how to find TEST_RESOURCE. */ diff --git a/src/tests/junit/org/apache/tools/ant/loader/AntClassLoader5Test.java b/src/tests/junit/org/apache/tools/ant/loader/AntClassLoader5Test.java new file mode 100644 index 000000000..d06d26358 --- /dev/null +++ b/src/tests/junit/org/apache/tools/ant/loader/AntClassLoader5Test.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.tools.ant.loader; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import junit.framework.TestCase; +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.util.CollectionUtils; + +public class AntClassLoader5Test extends TestCase { + + /** + * Asserts that getResources won't return resources that cannot be + * seen by AntClassLoader but by ClassLoader.this.parent. + * + * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=46752 + */ + public void testGetResources() throws IOException { + AntClassLoader acl = new AntClassLoader5(new EmptyLoader(), null, + new Path(null), true); + assertNull(acl.getResource("META-INF/MANIFEST.MF")); + assertFalse(acl.getResources("META-INF/MANIFEST.MF").hasMoreElements()); + + // double check using system classloader as parent + acl = new AntClassLoader5(null, null, new Path(null), true); + assertNotNull(acl.getResource("META-INF/MANIFEST.MF")); + assertTrue(acl.getResources("META-INF/MANIFEST.MF").hasMoreElements()); + } + + public void testGetResourcesUsingFactory() throws IOException { + AntClassLoader acl = + AntClassLoader.newAntClassLoader(new EmptyLoader(), null, + new Path(null), true); + assertNull(acl.getResource("META-INF/MANIFEST.MF")); + assertFalse(acl.getResources("META-INF/MANIFEST.MF").hasMoreElements()); + } + + private static class EmptyLoader extends ClassLoader { + public URL getResource(String n) { + return null; + } + public Enumeration getResources(String n) { + return new CollectionUtils.EmptyEnumeration(); + } + } +} \ No newline at end of file