Browse Source

#27285: simplify AntClassLoader by removing reflection hacks

(and separate ACL2) only needed for JDK 1.1 support.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@278074 13f79535-47bb-0310-9956-ffa450edef68
master
Jesse N. Glick 20 years ago
parent
commit
67c71a89e9
3 changed files with 230 additions and 339 deletions
  1. +226
    -59
      src/main/org/apache/tools/ant/AntClassLoader.java
  2. +1
    -4
      src/main/org/apache/tools/ant/Project.java
  3. +3
    -276
      src/main/org/apache/tools/ant/loader/AntClassLoader2.java

+ 226
- 59
src/main/org/apache/tools/ant/AntClassLoader.java View File

@@ -14,6 +14,7 @@
* limitations under the License.
*
*/

package org.apache.tools.ant;

import java.io.ByteArrayOutputStream;
@@ -21,14 +22,22 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.tools.ant.types.Path;
@@ -192,6 +201,9 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
*/
private Hashtable zipFiles = new Hashtable();

/** Static map of jar file/time to manifiest class-path entries */
private static Map/*<String,String>*/ pathMap = Collections.synchronizedMap(new HashMap());

/**
* The context loader saved when setting the thread's current
* context loader.
@@ -202,36 +214,6 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
*/
private boolean isContextLoaderSaved = false;

/**
* Reflection method reference for getProtectionDomain;
* used to avoid 1.1-compatibility problems.
*/
private static Method getProtectionDomain = null;

/**
* Reflection method reference for defineClassProtectionDomain;
* used to avoid 1.1-compatibility problems.
*/
private static Method defineClassProtectionDomain = null;


// Set up the reflection-based Java2 methods if possible
static {
try {
getProtectionDomain
= Class.class.getMethod("getProtectionDomain", new Class[0]);
Class protectionDomain
= Class.forName("java.security.ProtectionDomain");
Class[] args = new Class[] {String.class, byte[].class,
Integer.TYPE, Integer.TYPE, protectionDomain};
defineClassProtectionDomain
= ClassLoader.class.getDeclaredMethod("defineClass", args);
} catch (Exception e) {
// ignore failure to get access to 1.2+ methods
}
}


/**
* Create an Ant Class Loader
*/
@@ -452,7 +434,9 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
}

/**
* Add a file to the path
* Add a file to the path.
* 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
@@ -461,6 +445,66 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
*/
protected void addPathFile(File pathComponent) throws IOException {
pathComponents.addElement(pathComponent);
if (pathComponent.isDirectory()) {
return;
}

String absPathPlusTimeAndLength =
pathComponent.getAbsolutePath() + pathComponent.lastModified() + "-"
+ pathComponent.length();
String classpath = (String) pathMap.get(absPathPlusTimeAndLength);
if (classpath == null) {
ZipFile jarFile = null;
InputStream manifestStream = null;
try {
jarFile = new ZipFile(pathComponent);
manifestStream
= jarFile.getInputStream(new ZipEntry("META-INF/MANIFEST.MF"));

if (manifestStream == null) {
return;
}
Reader manifestReader
= new InputStreamReader(manifestStream, "UTF-8");
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) {
classpath = "";
}
pathMap.put(absPathPlusTimeAndLength, classpath);
}

if (!"".equals(classpath)) {
URL baseURL = FILE_UTILS.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);
}
}
}
}

/**
@@ -1029,37 +1073,160 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
*/
protected Class defineClassFromData(File container, byte[] classData,
String classname) throws IOException {
// Simply put:
// defineClass(classname, classData, 0, classData.length,
// Project.class.getProtectionDomain());
// Made more elaborate to be 1.1-safe.
if (defineClassProtectionDomain != null) {
definePackage(container, classname);
// XXX should instead make a new ProtectionDomain with a CodeSource
// corresponding to container.toURI().toURL() and the same
// PermissionCollection as Project.class.protectionDomain had
return defineClass(classname, classData, 0, classData.length,
Project.class.getProtectionDomain());
}

/**
* 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)
throws IOException {
int classIndex = className.lastIndexOf('.');
if (classIndex == -1) {
return;
}

String packageName = className.substring(0, classIndex);
if (getPackage(packageName) != null) {
// already defined
return;
}

// define the package now
Manifest manifest = getJarManifest(container);

if (manifest == null) {
definePackage(packageName, null, null, null, null, null,
null, null);
} else {
definePackage(container, packageName, manifest);
}
}

/**
* 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 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 specificationTitle = null;
String specificationVendor = null;
String specificationVersion = null;
String implementationTitle = null;
String implementationVendor = null;
String implementationVersion = null;
String sealedString = null;
URL sealBase = null;

Attributes sectionAttributes = manifest.getAttributes(sectionName);
if (sectionAttributes != null) {
specificationTitle
= sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
specificationVendor
= sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
specificationVersion
= sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
implementationTitle
= sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
implementationVendor
= sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
implementationVersion
= sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
sealedString
= sectionAttributes.getValue(Name.SEALED);
}

Attributes mainAttributes = manifest.getMainAttributes();
if (mainAttributes != null) {
if (specificationTitle == null) {
specificationTitle
= mainAttributes.getValue(Name.SPECIFICATION_TITLE);
}
if (specificationVendor == null) {
specificationVendor
= mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
}
if (specificationVersion == null) {
specificationVersion
= mainAttributes.getValue(Name.SPECIFICATION_VERSION);
}
if (implementationTitle == null) {
implementationTitle
= mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
}
if (implementationVendor == null) {
implementationVendor
= mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
}
if (implementationVersion == null) {
implementationVersion
= mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
}
if (sealedString == null) {
sealedString
= mainAttributes.getValue(Name.SEALED);
}
}

if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
try {
Object domain
= getProtectionDomain.invoke(Project.class, new Object[0]);
Object[] args
= new Object[] {classname, classData, new Integer(0),
new Integer(classData.length), domain};
return (Class) defineClassProtectionDomain.invoke(this, args);
} catch (InvocationTargetException ite) {
Throwable t = ite.getTargetException();
if (t instanceof ClassFormatError) {
throw (ClassFormatError) t;
} else if (t instanceof NoClassDefFoundError) {
throw (NoClassDefFoundError) t;
} else if (t instanceof SecurityException) {
throw (SecurityException) t;
} else {
throw new IOException(t.toString());
}
} catch (Exception e) {
throw new IOException(e.toString());
// XXX should be using FileUtils!
sealBase = new URL("file:" + container.getPath());
} catch (MalformedURLException e) {
// ignore
}
} else {
return defineClass(classname, classData, 0, classData.length);
}

definePackage(packageName, specificationTitle, specificationVersion,
specificationVendor, implementationTitle,
implementationVersion, implementationVendor, sealBase);
}


/**
* Reads a class definition from a stream.
*


+ 1
- 4
src/main/org/apache/tools/ant/Project.java View File

@@ -33,7 +33,6 @@ import java.util.Set;
import java.util.HashSet;
import org.apache.tools.ant.input.DefaultInputHandler;
import org.apache.tools.ant.input.InputHandler;
import org.apache.tools.ant.loader.AntClassLoader2;
import org.apache.tools.ant.helper.DefaultExecutor;
import org.apache.tools.ant.helper.KeepGoingExecutor;
import org.apache.tools.ant.types.FilterSet;
@@ -44,7 +43,6 @@ import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.JavaEnvUtils;
import org.apache.tools.ant.util.StringUtils;


/**
* Central representation of an Ant project. This class defines an
* Ant project with all of its targets, tasks and various other
@@ -56,7 +54,6 @@ import org.apache.tools.ant.util.StringUtils;
* file paths at runtime.
*
*/

public class Project {
/** Message priority of &quot;error&quot;. */
public static final int MSG_ERR = 0;
@@ -270,7 +267,7 @@ public class Project {
* @return an appropriate classloader.
*/
public AntClassLoader createClassLoader(Path path) {
AntClassLoader loader = new AntClassLoader2();
AntClassLoader loader = new AntClassLoader();
loader.setProject(this);
loader.setClassPath(path);
return loader;


+ 3
- 276
src/main/org/apache/tools/ant/loader/AntClassLoader2.java View File

@@ -14,287 +14,14 @@
* limitations under the License.
*
*/

package org.apache.tools.ant.loader;

import java.io.File;
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.Project;
import java.util.jar.Manifest;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.zip.ZipEntry;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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
*
* @deprecated Just use {@link AntClassLoader} itself.
*/
public class AntClassLoader2 extends AntClassLoader {
/** Instance of a utility class to use for file operations. */
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();

/** Static map of jar file/time to manifiest class-path entries */
private static Map pathMap = Collections.synchronizedMap(new HashMap());

/**
* Constructor
*/
public AntClassLoader2() {
}

/**
* 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,
String className) throws IOException {

definePackage(container, className);
return defineClass(className, classData, 0, classData.length,
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)
throws IOException {
int classIndex = className.lastIndexOf('.');
if (classIndex == -1) {
return;
}

String packageName = className.substring(0, classIndex);
if (getPackage(packageName) != null) {
// already defined
return;
}

// define the package now
Manifest manifest = getJarManifest(container);

if (manifest == null) {
definePackage(packageName, null, null, null, null, null,
null, null);
} else {
definePackage(container, packageName, 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 specificationTitle = null;
String specificationVendor = null;
String specificationVersion = null;
String implementationTitle = null;
String implementationVendor = null;
String implementationVersion = null;
String sealedString = null;
URL sealBase = null;

Attributes sectionAttributes = manifest.getAttributes(sectionName);
if (sectionAttributes != null) {
specificationTitle
= sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
specificationVendor
= sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
specificationVersion
= sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
implementationTitle
= sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
implementationVendor
= sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
implementationVersion
= sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
sealedString
= sectionAttributes.getValue(Name.SEALED);
}

Attributes mainAttributes = manifest.getMainAttributes();
if (mainAttributes != null) {
if (specificationTitle == null) {
specificationTitle
= mainAttributes.getValue(Name.SPECIFICATION_TITLE);
}
if (specificationVendor == null) {
specificationVendor
= mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
}
if (specificationVersion == null) {
specificationVersion
= mainAttributes.getValue(Name.SPECIFICATION_VERSION);
}
if (implementationTitle == null) {
implementationTitle
= mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
}
if (implementationVendor == null) {
implementationVendor
= mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
}
if (implementationVersion == null) {
implementationVersion
= mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
}
if (sealedString == null) {
sealedString
= mainAttributes.getValue(Name.SEALED);
}
}

if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
try {
sealBase = new URL("file:" + container.getPath());
} catch (MalformedURLException e) {
// ignore
}
}

definePackage(packageName, specificationTitle, specificationVersion,
specificationVendor, implementationTitle,
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 absPathPlusTimeAndLength =
pathComponent.getAbsolutePath() + pathComponent.lastModified() + "-"
+ pathComponent.length();
String classpath = (String) pathMap.get(absPathPlusTimeAndLength);
if (classpath == null) {
ZipFile jarFile = null;
InputStream manifestStream = null;
try {
jarFile = new ZipFile(pathComponent);
manifestStream
= jarFile.getInputStream(new ZipEntry("META-INF/MANIFEST.MF"));

if (manifestStream == null) {
return;
}
Reader manifestReader
= new InputStreamReader(manifestStream, "UTF-8");
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) {
classpath = "";
}
pathMap.put(absPathPlusTimeAndLength, classpath);
}

if (!"".equals(classpath)) {
URL baseURL = FILE_UTILS.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);
}
}
}
}
public AntClassLoader2() {}
}


Loading…
Cancel
Save