From a506341591609c92acc41b9d7e7f852f520f2815 Mon Sep 17 00:00:00 2001 From: Conor MacNeill Date: Sat, 25 Jan 2003 15:03:16 +0000 Subject: [PATCH] Include jars from the manifest's classpath into the classloader PR: 6921 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@273881 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/tools/ant/AntClassLoader.java | 67 +++++-- .../tools/ant/loader/AntClassLoader2.java | 172 ++++++++++++++++-- 2 files changed, 209 insertions(+), 30 deletions(-) diff --git a/src/main/org/apache/tools/ant/AntClassLoader.java b/src/main/org/apache/tools/ant/AntClassLoader.java index 90f449da7..8031f6d62 100644 --- a/src/main/org/apache/tools/ant/AntClassLoader.java +++ b/src/main/org/apache/tools/ant/AntClassLoader.java @@ -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 * this class loader will delegate to load classes + * + * @param parent the parent class loader. */ public void setParent(ClassLoader parent) { 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 * 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) { this.parentFirst = parentFirst; @@ -472,6 +477,22 @@ public class AntClassLoader extends ClassLoader implements BuildListener { File pathComponent = project != null ? project.resolveFile(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); } @@ -965,12 +986,12 @@ public class AntClassLoader extends ClassLoader implements BuildListener { if (isParentFirst(classname)) { try { 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) { 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 { try { @@ -1015,6 +1036,10 @@ public class AntClassLoader extends ClassLoader implements BuildListener { * * @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 { @@ -1056,6 +1081,7 @@ public class AntClassLoader extends ClassLoader implements BuildListener { * Must not be null. * @param classname The name of the class in the stream. * Must not be null. + * @param container the file or directory containing the class. * * @return the Class object read from the stream. * @@ -1096,6 +1122,23 @@ public class AntClassLoader extends ClassLoader implements BuildListener { 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. @@ -1121,8 +1164,8 @@ public class AntClassLoader extends ClassLoader implements BuildListener { try { stream = getResourceStream(pathComponent, classFilename); if (stream != null) { - log("Loaded from " + pathComponent + " " + classFilename, - Project.MSG_DEBUG ); + log("Loaded from " + pathComponent + " " + + classFilename, Project.MSG_DEBUG); return getClassFromStream(stream, name, pathComponent); } } catch (SecurityException se) { @@ -1173,7 +1216,7 @@ public class AntClassLoader extends ClassLoader implements BuildListener { */ public synchronized void cleanup() { for (Enumeration e = zipFiles.elements(); e.hasMoreElements();) { - ZipFile zipFile = (ZipFile)e.nextElement(); + ZipFile zipFile = (ZipFile) e.nextElement(); try { zipFile.close(); } catch (IOException ioe) { @@ -1248,10 +1291,10 @@ public class AntClassLoader extends ClassLoader implements BuildListener { * here */ 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); } } diff --git a/src/main/org/apache/tools/ant/loader/AntClassLoader2.java b/src/main/org/apache/tools/ant/loader/AntClassLoader2.java index ba846bae5..cefac12ad 100644 --- a/src/main/org/apache/tools/ant/loader/AntClassLoader2.java +++ b/src/main/org/apache/tools/ant/loader/AntClassLoader2.java @@ -55,6 +55,9 @@ 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; @@ -63,17 +66,85 @@ 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.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 { + /** 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, String className) throws IOException { - definePackage(container, className); + definePackage(container, className); 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) throws IOException { int classIndex = className.lastIndexOf('.'); @@ -88,27 +159,26 @@ public class AntClassLoader2 extends AntClassLoader { } // 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) { - definePackage(packageName, null, null, null, null, null, null, null); + definePackage(packageName, null, null, null, null, null, + null, null); } else { 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 specificationTitle = null; @@ -180,7 +250,73 @@ public class AntClassLoader2 extends AntClassLoader { definePackage(packageName, specificationTitle, specificationVersion, 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); + } + } + } } }