You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by co...@apache.org on 2003/01/25 16:03:16 UTC

cvs commit: jakarta-ant/src/main/org/apache/tools/ant/loader AntClassLoader2.java

conor       2003/01/25 07:03:16

  Modified:    src/main/org/apache/tools/ant AntClassLoader.java
               src/main/org/apache/tools/ant/loader AntClassLoader2.java
  Log:
  Include jars from the manifest's classpath into the classloader
  
  PR:	6921
  
  Revision  Changes    Path
  1.65      +55 -12    jakarta-ant/src/main/org/apache/tools/ant/AntClassLoader.java
  
  Index: AntClassLoader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/AntClassLoader.java,v
  retrieving revision 1.64
  retrieving revision 1.65
  diff -u -w -u -r1.64 -r1.65
  --- AntClassLoader.java	24 Jan 2003 14:18:20 -0000	1.64
  +++ AntClassLoader.java	25 Jan 2003 15:03:15 -0000	1.65
  @@ -390,6 +390,8 @@
       /**
        * 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 @@
       }
   
       /**
  -     * 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 @@
           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 @@
           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 @@
        *
        * @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 @@
        *               Must not be <code>null</code>.
        * @param classname The name of the class in the stream.
        *                  Must not be <code>null</code>.
  +     * @param container the file or directory containing the class.
        *
        * @return the Class object read from the stream.
        *
  @@ -1096,6 +1122,23 @@
           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 @@
                   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) {
  
  
  
  1.2       +154 -18   jakarta-ant/src/main/org/apache/tools/ant/loader/AntClassLoader2.java
  
  Index: AntClassLoader2.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/loader/AntClassLoader2.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -w -u -r1.1 -r1.2
  --- AntClassLoader2.java	24 Jan 2003 14:18:20 -0000	1.1
  +++ AntClassLoader2.java	25 Jan 2003 15:03:16 -0000	1.2
  @@ -55,6 +55,9 @@
   
   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,8 +66,40 @@
   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 {
   
  @@ -74,6 +109,42 @@
                                               
       }
       
  +    /**
  +     * 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 @@
           }
           
           // 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;
  @@ -181,6 +251,72 @@
           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 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);
  +                }
  +            }
  +        }
       }
   }
   
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>