PR: 6921 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@273881 13f79535-47bb-0310-9956-ffa450edef68master
@@ -390,6 +390,8 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
/** | /** | ||||
* Set the parent for this class loader. This is the class loader to which | * Set the parent for this class loader. This is the class loader to which | ||||
* this class loader will delegate to load classes | * this class loader will delegate to load classes | ||||
* | |||||
* @param parent the parent class loader. | |||||
*/ | */ | ||||
public void setParent(ClassLoader parent) { | public void setParent(ClassLoader parent) { | ||||
if (parent == null) { | if (parent == null) { | ||||
@@ -400,9 +402,12 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
} | } | ||||
/** | /** | ||||
* Control whether class ookup is delegated to the parent loader first | |||||
* Control whether class lookup is delegated to the parent loader first | |||||
* or after this loader. Use with extreme caution. Setting this to | * or after this loader. Use with extreme caution. Setting this to | ||||
* false violates the class loader hierarchy and can lead to Linkage errors | * false violates the class loader hierarchy and can lead to Linkage errors | ||||
* | |||||
* @param parentFirst if true, delegate initial class search to the parent | |||||
* classloader. | |||||
*/ | */ | ||||
public void setParentFirst(boolean parentFirst) { | public void setParentFirst(boolean parentFirst) { | ||||
this.parentFirst = parentFirst; | this.parentFirst = parentFirst; | ||||
@@ -472,6 +477,22 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
File pathComponent | File pathComponent | ||||
= project != null ? project.resolveFile(pathElement) | = project != null ? project.resolveFile(pathElement) | ||||
: new File(pathElement); | : new File(pathElement); | ||||
try { | |||||
addPathFile(pathComponent); | |||||
} catch (IOException e) { | |||||
throw new BuildException(e); | |||||
} | |||||
} | |||||
/** | |||||
* Add a file to the path | |||||
* | |||||
* @param pathComponent the file which is to be added to the path for | |||||
* this class loader | |||||
* | |||||
* @throws IOException if data needed from the file cannot be read. | |||||
*/ | |||||
protected void addPathFile(File pathComponent) throws IOException { | |||||
pathComponents.addElement(pathComponent); | pathComponents.addElement(pathComponent); | ||||
} | } | ||||
@@ -965,12 +986,12 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
if (isParentFirst(classname)) { | if (isParentFirst(classname)) { | ||||
try { | try { | ||||
theClass = findBaseClass(classname); | theClass = findBaseClass(classname); | ||||
log("Class " + classname + " loaded from parent loader ( parentFirst )", | |||||
Project.MSG_DEBUG); | |||||
log("Class " + classname + " loaded from parent loader " | |||||
+ "(parentFirst)", Project.MSG_DEBUG); | |||||
} catch (ClassNotFoundException cnfe) { | } catch (ClassNotFoundException cnfe) { | ||||
theClass = findClass(classname); | theClass = findClass(classname); | ||||
log("Class " + classname + " loaded from ant loader ( parentFirst )", | |||||
Project.MSG_DEBUG); | |||||
log("Class " + classname + " loaded from ant loader " | |||||
+ "(parentFirst)", Project.MSG_DEBUG); | |||||
} | } | ||||
} else { | } else { | ||||
try { | try { | ||||
@@ -1015,6 +1036,10 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
* | * | ||||
* @param classData the bytecode data for the class | * @param classData the bytecode data for the class | ||||
* @param classname the name of the class | * @param classname the name of the class | ||||
* | |||||
* @return the Class instance created from the given data | |||||
* | |||||
* @throws IOException if the class data cannot be read. | |||||
*/ | */ | ||||
protected Class defineClassFromData(File container, byte[] classData, | protected Class defineClassFromData(File container, byte[] classData, | ||||
String classname) throws IOException { | String classname) throws IOException { | ||||
@@ -1056,6 +1081,7 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
* @param classname The name of the class in the stream. | * @param classname The name of the class in the stream. | ||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
* @param container the file or directory containing the class. | |||||
* | * | ||||
* @return the Class object read from the stream. | * @return the Class object read from the stream. | ||||
* | * | ||||
@@ -1096,6 +1122,23 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
return findClassInComponents(name); | return findClassInComponents(name); | ||||
} | } | ||||
/** | |||||
* Indicate if the given file is in this loader's path | |||||
* | |||||
* @param component the file which is to be checked | |||||
* | |||||
* @return true if the file is in the class path | |||||
*/ | |||||
protected boolean isInPath(File component) { | |||||
for (Enumeration e = pathComponents.elements(); e.hasMoreElements();) { | |||||
File pathComponent = (File) e.nextElement(); | |||||
if (pathComponent.equals(component)) { | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
/** | /** | ||||
* Finds a class on the given classpath. | * Finds a class on the given classpath. | ||||
@@ -1121,8 +1164,8 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
try { | try { | ||||
stream = getResourceStream(pathComponent, classFilename); | stream = getResourceStream(pathComponent, classFilename); | ||||
if (stream != null) { | if (stream != null) { | ||||
log("Loaded from " + pathComponent + " " + classFilename, | |||||
Project.MSG_DEBUG ); | |||||
log("Loaded from " + pathComponent + " " | |||||
+ classFilename, Project.MSG_DEBUG); | |||||
return getClassFromStream(stream, name, pathComponent); | return getClassFromStream(stream, name, pathComponent); | ||||
} | } | ||||
} catch (SecurityException se) { | } catch (SecurityException se) { | ||||
@@ -1173,7 +1216,7 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
*/ | */ | ||||
public synchronized void cleanup() { | public synchronized void cleanup() { | ||||
for (Enumeration e = zipFiles.elements(); e.hasMoreElements();) { | for (Enumeration e = zipFiles.elements(); e.hasMoreElements();) { | ||||
ZipFile zipFile = (ZipFile)e.nextElement(); | |||||
ZipFile zipFile = (ZipFile) e.nextElement(); | |||||
try { | try { | ||||
zipFile.close(); | zipFile.close(); | ||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
@@ -1248,10 +1291,10 @@ public class AntClassLoader extends ClassLoader implements BuildListener { | |||||
* here | * here | ||||
*/ | */ | ||||
public void addJavaLibraries() { | public void addJavaLibraries() { | ||||
Vector packages=JavaEnvUtils.getJrePackages(); | |||||
Enumeration e=packages.elements(); | |||||
while(e.hasMoreElements()) { | |||||
String packageName=(String)e.nextElement(); | |||||
Vector packages = JavaEnvUtils.getJrePackages(); | |||||
Enumeration e = packages.elements(); | |||||
while (e.hasMoreElements()) { | |||||
String packageName = (String) e.nextElement(); | |||||
addSystemPackageRoot(packageName); | addSystemPackageRoot(packageName); | ||||
} | } | ||||
} | } | ||||
@@ -55,6 +55,9 @@ package org.apache.tools.ant.loader; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.io.Reader; | |||||
import org.apache.tools.ant.AntClassLoader; | import org.apache.tools.ant.AntClassLoader; | ||||
import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
import java.util.jar.Manifest; | import java.util.jar.Manifest; | ||||
@@ -63,17 +66,85 @@ import java.util.jar.Attributes; | |||||
import java.util.jar.Attributes.Name; | import java.util.jar.Attributes.Name; | ||||
import java.net.URL; | import java.net.URL; | ||||
import java.net.MalformedURLException; | import java.net.MalformedURLException; | ||||
import java.util.zip.ZipEntry; | |||||
import java.util.StringTokenizer; | |||||
import org.apache.tools.ant.util.FileUtils; | |||||
/** | |||||
* An implementation of the AntClassLoader suitable for use on post JDK 1.1 | |||||
* platforms | |||||
* | |||||
* @author Conor MacNeill | |||||
*/ | |||||
public class AntClassLoader2 extends AntClassLoader { | public class AntClassLoader2 extends AntClassLoader { | ||||
/** Instance of a utility class to use for file operations. */ | |||||
private FileUtils fileUtils; | |||||
/** | |||||
* Constructor | |||||
*/ | |||||
public AntClassLoader2() { | |||||
fileUtils = FileUtils.newFileUtils(); | |||||
} | |||||
/** | |||||
* Define a class given its bytes | |||||
* | |||||
* @param container the container from which the class data has been read | |||||
* may be a directory or a jar/zip file. | |||||
* | |||||
* @param classData the bytecode data for the class | |||||
* @param className the name of the class | |||||
* | |||||
* @return the Class instance created from the given data | |||||
* | |||||
* @throws IOException if the class data cannot be read. | |||||
*/ | |||||
protected Class defineClassFromData(File container, byte[] classData, | protected Class defineClassFromData(File container, byte[] classData, | ||||
String className) throws IOException { | String className) throws IOException { | ||||
definePackage(container, className); | |||||
definePackage(container, className); | |||||
return defineClass(className, classData, 0, classData.length, | return defineClass(className, classData, 0, classData.length, | ||||
Project.class.getProtectionDomain()); | |||||
Project.class.getProtectionDomain()); | |||||
} | } | ||||
/** | |||||
* Get the manifest from the given jar, if it is indeed a jar and it has a | |||||
* manifest | |||||
* | |||||
* @param container the File from which a manifest is required. | |||||
* | |||||
* @return the jar's manifest or null is the container is not a jar or it | |||||
* has no manifest. | |||||
* | |||||
* @exception IOException if the manifest cannot be read. | |||||
*/ | |||||
private Manifest getJarManifest(File container) throws IOException { | |||||
if (container.isDirectory()) { | |||||
return null; | |||||
} | |||||
JarFile jarFile = null; | |||||
try { | |||||
jarFile = new JarFile(container); | |||||
return jarFile.getManifest(); | |||||
} finally { | |||||
if (jarFile != null) { | |||||
jarFile.close(); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Define the package information associated with a class. | |||||
* | |||||
* @param container the file containing the class definition. | |||||
* @param className the class name of for which the package information | |||||
* is to be determined. | |||||
* | |||||
* @exception IOException if the package information cannot be read from the | |||||
* container. | |||||
*/ | |||||
protected void definePackage(File container, String className) | protected void definePackage(File container, String className) | ||||
throws IOException { | throws IOException { | ||||
int classIndex = className.lastIndexOf('.'); | int classIndex = className.lastIndexOf('.'); | ||||
@@ -88,27 +159,26 @@ public class AntClassLoader2 extends AntClassLoader { | |||||
} | } | ||||
// define the package now | // define the package now | ||||
Manifest manifest = null; | |||||
if (!container.isDirectory()) { | |||||
JarFile jarFile = null; | |||||
try { | |||||
jarFile = new JarFile(container); | |||||
manifest = jarFile.getManifest(); | |||||
} finally { | |||||
if (jarFile != null) { | |||||
jarFile.close(); | |||||
} | |||||
} | |||||
} | |||||
Manifest manifest = getJarManifest(container); | |||||
if (manifest == null) { | if (manifest == null) { | ||||
definePackage(packageName, null, null, null, null, null, null, null); | |||||
definePackage(packageName, null, null, null, null, null, | |||||
null, null); | |||||
} else { | } else { | ||||
definePackage(container, packageName, manifest); | definePackage(container, packageName, manifest); | ||||
} | } | ||||
} | } | ||||
protected void definePackage(File container, String packageName, Manifest manifest) { | |||||
/** | |||||
* Define the package information when the class comes from a | |||||
* jar with a manifest | |||||
* | |||||
* @param container the jar file containing the manifest | |||||
* @param packageName the name of the package being defined. | |||||
* @param manifest the jar's manifest | |||||
*/ | |||||
protected void definePackage(File container, String packageName, | |||||
Manifest manifest) { | |||||
String sectionName = packageName.replace('.', '/') + "/"; | String sectionName = packageName.replace('.', '/') + "/"; | ||||
String specificationTitle = null; | String specificationTitle = null; | ||||
@@ -180,7 +250,73 @@ public class AntClassLoader2 extends AntClassLoader { | |||||
definePackage(packageName, specificationTitle, specificationVersion, | definePackage(packageName, specificationTitle, specificationVersion, | ||||
specificationVendor, implementationTitle, | specificationVendor, implementationTitle, | ||||
implementationVersion, implementationVendor, sealBase); | |||||
implementationVersion, implementationVendor, sealBase); | |||||
} | |||||
/** | |||||
* Add a file to the path. This classloader reads the manifest, if | |||||
* available, and adds any additional class path jars specified in the | |||||
* manifest. | |||||
* | |||||
* @param pathComponent the file which is to be added to the path for | |||||
* this class loader | |||||
* | |||||
* @throws IOException if data needed from the file cannot be read. | |||||
*/ | |||||
protected void addPathFile(File pathComponent) throws IOException { | |||||
super.addPathFile(pathComponent); | |||||
if (pathComponent.isDirectory()) { | |||||
return; | |||||
} | |||||
String classpath = null; | |||||
JarFile jarFile = null; | |||||
InputStream manifestStream = null; | |||||
try { | |||||
jarFile = new JarFile(pathComponent); | |||||
manifestStream | |||||
= jarFile.getInputStream(new ZipEntry("META-INF/MANIFEST.MF")); | |||||
if (manifestStream == null) { | |||||
return; | |||||
} | |||||
Reader manifestReader = new InputStreamReader(manifestStream); | |||||
org.apache.tools.ant.taskdefs.Manifest manifest | |||||
= new org.apache.tools.ant.taskdefs.Manifest(manifestReader); | |||||
classpath | |||||
= manifest.getMainSection().getAttributeValue("Class-Path"); | |||||
} catch (org.apache.tools.ant.taskdefs.ManifestException e) { | |||||
// ignore | |||||
} finally { | |||||
if (manifestStream != null) { | |||||
manifestStream.close(); | |||||
} | |||||
if (jarFile != null) { | |||||
jarFile.close(); | |||||
} | |||||
} | |||||
if (classpath != null) { | |||||
URL baseURL = fileUtils.getFileURL(pathComponent); | |||||
StringTokenizer st = new StringTokenizer(classpath); | |||||
while (st.hasMoreTokens()) { | |||||
String classpathElement = st.nextToken(); | |||||
URL libraryURL = new URL(baseURL, classpathElement); | |||||
if (!libraryURL.getProtocol().equals("file")) { | |||||
log("Skipping jar library " + classpathElement | |||||
+ " since only relative URLs are supported by this" | |||||
+ " loader", Project.MSG_VERBOSE); | |||||
continue; | |||||
} | |||||
File libraryFile = new File(libraryURL.getFile()); | |||||
if (libraryFile.exists() && !isInPath(libraryFile)) { | |||||
addPathFile(libraryFile); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||