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