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 2001/07/30 13:15:17 UTC

cvs commit: jakarta-ant/src/main/org/apache/tools/ant/taskdefs ManifestException.java Jar.java Manifest.java Zip.java

conor       01/07/30 04:15:17

  Modified:    src/main/org/apache/tools/ant/taskdefs Jar.java
                        Manifest.java Zip.java
  Added:       src/main/org/apache/tools/ant/taskdefs
                        ManifestException.java
  Log:
  Manifest is no longer in a fileset and so needs an uptodate check of its own
  Also now log warnings about malformed manifests
  
  Revision  Changes    Path
  1.20      +73 -21    jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Jar.java
  
  Index: Jar.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Jar.java,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- Jar.java	2001/07/28 14:11:41	1.19
  +++ Jar.java	2001/07/30 11:15:17	1.20
  @@ -59,6 +59,7 @@
   import org.apache.tools.zip.*;
   
   import java.io.*;
  +import java.util.Enumeration;
   
   /**
    * Creates a JAR archive.
  @@ -67,6 +68,7 @@
    */
   public class Jar extends Zip {
   
  +    private File manifestFile;
       private Manifest manifest;
       private Manifest execManifest;    
   
  @@ -86,6 +88,8 @@
               throw new BuildException("Manifest file: " + manifestFile + " does not exist.", 
                                        getLocation());
           }
  +
  +        this.manifestFile = manifestFile;
           
           InputStream is = null;
           try {
  @@ -96,6 +100,10 @@
               }
               manifest.merge(newManifest);
           }
  +        catch (ManifestException e) {
  +            log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
  +            throw new BuildException("Invalid Manifest: " + manifestFile, e, getLocation());
  +        }
           catch (IOException e) {
               throw new BuildException("Unable to read manifest file: " + manifestFile, e);
           }
  @@ -120,25 +128,39 @@
       protected void initZipOutputStream(ZipOutputStream zOut)
           throws IOException, BuildException
       {
  -        // If no manifest is specified, add the default one.
  -        if (manifest == null) {
  -            execManifest = null;
  -        }
  -        else {
  -            execManifest = new Manifest();
  -            execManifest.merge(manifest);
  +        try {
  +            // If no manifest is specified, add the default one.
  +            if (manifest == null) {
  +                execManifest = null;
  +            }
  +            else {
  +                execManifest = new Manifest();
  +                execManifest.merge(manifest);
  +            }
  +            zipDir(null, zOut, "META-INF/");
  +            super.initZipOutputStream(zOut);
  +        }
  +        catch (ManifestException e) {
  +                log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
  +                throw new BuildException("Invalid Manifest", e, getLocation());
           }
  -        zipDir(null, zOut, "META-INF/");
  -        super.initZipOutputStream(zOut);
       }
  -
  -    private Manifest getDefaultManifest() throws IOException {
  -        String s = "/org/apache/tools/ant/defaultManifest.mf";
  -        InputStream in = this.getClass().getResourceAsStream(s);
  -        if (in == null) {
  -            throw new BuildException("Could not find: " + s);
  +        
  +    private Manifest getDefaultManifest() {
  +        try {
  +            String s = "/org/apache/tools/ant/defaultManifest.mf";
  +            InputStream in = this.getClass().getResourceAsStream(s);
  +            if (in == null) {
  +                throw new BuildException("Could not find default manifest: " + s);
  +            }
  +            return new Manifest(in);
  +        }
  +        catch (ManifestException e) {
  +            throw new BuildException("Default manifest is invalid !!");
           }
  -        return new Manifest(in);
  +        catch (IOException e) {
  +            throw new BuildException("Unable to read default manifest", e);
  +        }
       }   
       
       protected void finalizeZipOutputStream(ZipOutputStream zOut)
  @@ -147,6 +169,10 @@
           if (execManifest == null) {
               execManifest = getDefaultManifest();
           }
  +
  +        for (Enumeration e = execManifest.getWarnings(); e.hasMoreElements(); ) {
  +            log("Manifest warning: " + (String)e.nextElement(), Project.MSG_WARN);
  +        }
           
           // time to write the manifest
           ByteArrayOutputStream baos = new ByteArrayOutputStream();
  @@ -157,7 +183,6 @@
           ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
           super.zipFile(bais, zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis());
           super.finalizeZipOutputStream(zOut);
  -
       }
   
       /**
  @@ -169,11 +194,17 @@
        * and not the old one from the JAR we are updating
        */
       private void zipManifestEntry(InputStream is) throws IOException {
  -        if (execManifest == null) {
  -            execManifest = new Manifest(is);
  +        try {
  +            if (execManifest == null) {
  +                execManifest = new Manifest(is);
  +            }
  +            else if (isAddingNewFiles()) {
  +                execManifest.merge(new Manifest(is));
  +            }
           }
  -        else if (isAddingNewFiles()) {
  -            execManifest.merge(new Manifest(is));
  +        catch (ManifestException e) {
  +            log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
  +            throw new BuildException("Invalid Manifest", e, getLocation());
           }
       }
       
  @@ -223,6 +254,27 @@
           }
       }
   
  +    /**
  +     * Check whether the archive is up-to-date; 
  +     * @param scanners list of prepared scanners containing files to archive
  +     * @param zipFile intended archive file (may or may not exist)
  +     * @return true if nothing need be done (may have done something already); false if
  +     *         archive creation should proceed
  +     * @exception BuildException if it likes
  +     */
  +    protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException {
  +        // need to handle manifest as a special check
  +        if (manifestFile != null && manifestFile.lastModified() > zipFile.lastModified()) {
  +            return false;
  +        }
  +        return super.isUpToDate(scanners, zipFile);
  +    }
  +        
  +    protected boolean createEmptyZip(File zipFile) {
  +        // Jar files always contain a manifest and can never be empty        
  +        return false;
  +    }
  +    
       /**
        * Make sure we don't think we already have a MANIFEST next time this task
        * gets executed.
  
  
  
  1.3       +226 -42   jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Manifest.java
  
  Index: Manifest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Manifest.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Manifest.java	2001/07/29 13:57:13	1.2
  +++ Manifest.java	2001/07/30 11:15:17	1.3
  @@ -63,64 +63,132 @@
    * @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
    */
   public class Manifest {
  -    static public final String ATTR_MANIFEST_VERSION = "Manifest-Version";
  -    static public final String ATTR_SIGNATURE_VERSION = "Signature-Version";
  -    static public final String ATTR_NAME = "Name";
  -    static public final String ATTR_FROM = "From";
  +    /** The standard manifest version header */
  +    static public final String ATTRIBUTE_MANIFEST_VERSION = "Manifest-Version";
  +    
  +    /** The standard Signature Version header */
  +    static public final String ATTRIBUTE_SIGNATURE_VERSION = "Signature-Version";
  +    
  +    /** The Name Attribute is the first in a named section */
  +    static public final String ATTRIBUTE_NAME = "Name";
  +    
  +    /** THe From Header is disallowed in a Manifest */    
  +    static public final String ATTRIBUTE_FROM = "From";
  +    
  +    /** Default Manifest version if one is not specified */
       static public final String DEFAULT_MANIFEST_VERSION = "1.0";
  +    
  +    /** The max length of a line in a Manifest */
       static public final int MAX_LINE_LENGTH = 70;
       
       /**
        * Class to hold manifest attributes
        */
  -    static private class Attribute {
  +    private class Attribute {
           /** The attribute's name */
           private String name = null;
           
           /** The attribute's value */
           private String value = null;
   
  +        /** 
  +         * Construct an empty attribute */
           public Attribute() {
           }
           
  -        public Attribute(String line) throws IOException {
  +        /**
  +         * Construct an attribute by parsing a line from the Manifest
  +         * 
  +         * @param line the line containing the attribute name and value
  +         *
  +         * @throws ManifestException if the line is not valid 
  +         */
  +        public Attribute(String line) throws ManifestException {
               parse(line);
           }
           
  +        /**
  +         * Construct a manifest by specifying its name and value 
  +         * 
  +         * @param name the attribute's name
  +         * @param value the Attribute's value
  +         */
           public Attribute(String name, String value) {
               this.name = name;
               this.value = value;
           }
           
  -        public void parse(String line) throws IOException {
  +        /**
  +         * Parse a line into name and value pairs
  +         *
  +         * @param line the line to be parsed
  +         *
  +         * @throws ManifestException if the line does not contain a colon
  +         * separating the name and value
  +         */
  +        public void parse(String line) throws ManifestException {
               int index = line.indexOf(": ");
               if (index == -1) {
  -                throw new IOException("Manifest line \"" + line + "\" is not valid");
  +                throw new ManifestException("Manifest line \"" + line + "\" is not valid");
               }
               name = line.substring(0, index);
               value = line.substring(index + 2);
           } 
           
  +        /**
  +         * Set the Attribute's name
  +         * 
  +         * @param name the attribute's name
  +         */
           public void setName(String name) {
               this.name = name;
           }
           
  +        /**
  +         * Get the Attribute's name
  +         *
  +         * @return the attribute's name.
  +         */
           public String getName() {
               return name;
           }
           
  +        /**
  +         * Set the Attribute's value
  +         * 
  +         * @param value the attribute's value
  +         */
           public void setValue(String value) {
               this.value = value;
           }
           
  +        /**
  +         * Get the Attribute's value
  +         *
  +         * @return the attribute's value.
  +         */
           public String getValue() {
               return value;
           }
           
  +        /**
  +         * Add a continuation line from the Manifest file
  +         *
  +         * When lines are too long in a manifest, they are continued on the 
  +         * next line by starting with a space. This method adds the continuation
  +         * data to the attribute value by skipping the first character.
  +         */
           public void addContinuation(String line) {
               value += line.substring(1);
           }
           
  +        /**
  +         * Write the attribute out to a print writer.
  +         *
  +         * @param writer the Writer to which the attribute is written
  +         *
  +         * @throws IOException if the attribte value cannot be written
  +         */
           public void write(PrintWriter writer) throws IOException {
               String line = name + ": " + value;
               while (line.getBytes().length > MAX_LINE_LENGTH) {
  @@ -143,22 +211,46 @@
   
       /** 
        * Class to represent an individual section in the 
  -     * Manifest 
  +     * Manifest. A section consists of a set of attribute values,
  +     * separated from other sections by a blank line.
        */
  -    static private class Section {
  +    private class Section {
  +        /** The section's name if any. The main section in a manifest is unnamed.*/
           private String name = null;
           
  +        /** The section's attributes.*/
           private Hashtable attributes = new Hashtable();
           
  +        /**
  +         * Set the Section's name
  +         * 
  +         * @param name the section's name
  +         */
           public void setName(String name) {
               this.name = name;
           }
           
  +        /**
  +         * Get the Section's name
  +         *
  +         * @return the section's name.
  +         */
           public String getName() {
               return name;
           }
           
  -        public String read(BufferedReader reader) throws IOException {
  +        /**
  +         * Read a section through a reader 
  +         * 
  +         * @param reader the reader from which the section is read
  +         *
  +         * @return the name of the next section if it has been read as part of this 
  +         *         section - This only happens if the Manifest is malformed.
  +         * 
  +         * @throws ManifestException if the section is not valid according to the JAR spec
  +         * @throws IOException if the section cannot be read from the reader.
  +         */
  +        public String read(BufferedReader reader) throws ManifestException, IOException {
               Attribute attribute = null;
               while (true) { 
                   String line = reader.readLine();
  @@ -168,29 +260,31 @@
                   if (line.charAt(0) == ' ') {
                       // continuation line
                       if (attribute == null) {
  -                        throw new IOException("Can't start an attribute with a continuation line " + line);
  +                        throw new ManifestException("Can't start an attribute with a continuation line " + line);
                       }
                       attribute.addContinuation(line);
                   }
                   else {
                       attribute = new Attribute(line);
  -                    if (attribute.getName().equalsIgnoreCase(ATTR_NAME)) {
  -                        return attribute.getValue();
  +                    String nameReadAhead = addAttribute(attribute);
  +                    if (nameReadAhead != null) {
  +                        return nameReadAhead;
                       }
  -                    
  -                    if (attribute.getName().toLowerCase().startsWith(ATTR_FROM.toLowerCase())) {
  -                        throw new IOException("Attribute names may not start with " + ATTR_FROM);
  -                    }
  -                    
  -                    addAttribute(attribute);
                   }
               }
           }
           
  -        public void merge(Section section) throws IOException {
  +        /**
  +         * Merge in another section
  +         *
  +         * @param section the section to be merged with this one.
  +         *
  +         * @throws ManifestException if the sections cannot be merged.
  +         */
  +        public void merge(Section section) throws ManifestException {
               if (name == null && section.getName() != null ||
                       name != null && !(name.equalsIgnoreCase(section.getName()))) {
  -                throw new IOException("Unable to merge sections with different names");
  +                throw new ManifestException("Unable to merge sections with different names");
               }
               
               for (Enumeration e = section.attributes.keys(); e.hasMoreElements();) {
  @@ -200,9 +294,16 @@
               }
           }
           
  +        /**
  +         * Write the section out to a print writer.
  +         *
  +         * @param writer the Writer to which the section is written
  +         *
  +         * @throws IOException if the section cannot be written
  +         */
           public void write(PrintWriter writer) throws IOException {
               if (name != null) {
  -                Attribute nameAttr = new Attribute(ATTR_NAME, name);
  +                Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
                   nameAttr.write(writer);
               }
               for (Enumeration e = attributes.elements(); e.hasMoreElements();) {
  @@ -212,6 +313,14 @@
               writer.println();
           }
       
  +        /**
  +         * Get the value of the attribute with the name given.
  +         *
  +         * @param attributeName the name of the attribute to be returned.
  +         *
  +         * @return the attribute's value or null if the attribute does not exist
  +         *         in the section
  +         */         
           public String getAttributeValue(String attributeName) {
               Attribute attribute = (Attribute)attributes.get(attributeName.toLowerCase());
               if (attribute == null) {
  @@ -219,25 +328,61 @@
               }
               return attribute.getValue();
           }
  -        
  +
  +        /**
  +         * Remove tge given attribute from the section 
  +         *
  +         * @param attributeName the name of the attribute to be removed.
  +         */
           public void removeAttribute(String attributeName) {
               attributes.remove(attributeName.toLowerCase());
           }
           
  -        public void addAttribute(Attribute attribute) throws IOException {
  -            if (attributes.containsKey(attribute.getName().toLowerCase())) {
  -                throw new IOException("The attribute \"" + attribute.getName() + "\" may not occur more than" +
  -                                      " once in the same section");
  +        /**
  +         * Add an attribute to the section
  +         *
  +         * @param attribute the attribute to be added.
  +         *
  +         * @return the value of the attribute if it is a name attribute - null other wise
  +         *
  +         * @throws ManifestException if the attribute already exists in this section.
  +         */
  +        public String addAttribute(Attribute attribute) throws ManifestException {
  +            if (attribute.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
  +                warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes should not occur in the " +
  +                                    "main section and must be the first element in all " + 
  +                                    "other sections: \"" +attribute.getName() + ": " + attribute.getValue() + "\"");  
  +                return attribute.getValue();
  +            }
  +            
  +            if (attribute.getName().toLowerCase().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
  +                warnings.addElement("Manifest attributes should not start with \"" +
  +                                    ATTRIBUTE_FROM + "\" in \"" +attribute.getName() + ": " + attribute.getValue() + "\"");  
  +            }
  +            else if (attributes.containsKey(attribute.getName().toLowerCase())) {
  +                throw new ManifestException("The attribute \"" + attribute.getName() + "\" may not " + 
  +                                            "occur more than once in the same section");
  +            }
  +            else {
  +                attributes.put(attribute.getName().toLowerCase(), attribute);
               }
  -            attributes.put(attribute.getName().toLowerCase(), attribute);
  +            return null;
           }
       }
   
  -    
  +    /** The version of this manifest */
       private String manifestVersion = DEFAULT_MANIFEST_VERSION;
  +    
  +    /** The main section of this manifest */ 
       private Section mainSection = new Section();
  +    
  +    /** The named sections of this manifest */
       private Hashtable sections = new Hashtable();
   
  +    /** Warnings for this manifest file */
  +    private Vector warnings = new Vector();
  +    
  +    /** Construct an empty manifest */
       public Manifest() {
       }
       
  @@ -245,8 +390,11 @@
        * Read a manifest file from the given input stream
        *
        * @param is the input stream from which the Manifest is read 
  +     * 
  +     * @throws ManifestException if the manifest is not valid according to the JAR spec
  +     * @throws IOException if the manifest cannot be read from the reader.
        */
  -    public Manifest(InputStream is) throws IOException {
  +    public Manifest(InputStream is) throws ManifestException, IOException {
           BufferedReader reader = new BufferedReader(new InputStreamReader(is));
           String line = reader.readLine();
           if (line == null) {
  @@ -255,10 +403,10 @@
           
           // This should be the manifest version
           String nextSectionName = mainSection.read(reader);
  -        String readManifestVersion = mainSection.getAttributeValue(ATTR_MANIFEST_VERSION);
  +        String readManifestVersion = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
           if (readManifestVersion != null) {
               manifestVersion = readManifestVersion;
  -            mainSection.removeAttribute(ATTR_MANIFEST_VERSION);
  +            mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
           }
   
           while ((line = reader.readLine()) != null) {
  @@ -269,9 +417,9 @@
               Section section = new Section();
               if (nextSectionName == null) {
                   Attribute sectionName = new Attribute(line);
  -                if (!sectionName.getName().equalsIgnoreCase(ATTR_NAME)) {
  -                    throw new IOException("Manifest sections should start with a \"" + ATTR_NAME + 
  -                                          "\" attribute and not \"" + sectionName.getName() + "\"");
  +                if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
  +                    throw new ManifestException("Manifest sections should start with a \"" + ATTRIBUTE_NAME + 
  +                                                "\" attribute and not \"" + sectionName.getName() + "\"");
                   }
                   nextSectionName = sectionName.getValue();
               }
  @@ -291,8 +439,13 @@
       
       /**
        * Merge the contents of the given manifest into this manifest
  +     *
  +     * @param other the Manifest to be merged with this one.
  +     *
  +     * @throws ManifestException if there is a problem merging the manfest according
  +     *         to the Manifest spec.
        */
  -    public void merge(Manifest other) throws IOException {
  +    public void merge(Manifest other) throws ManifestException {
           manifestVersion = other.manifestVersion;
           mainSection.merge(other.mainSection);
           for (Enumeration e = other.sections.keys(); e.hasMoreElements();) {
  @@ -306,18 +459,35 @@
                   ourSection.merge(otherSection);
               }
           }
  +        
  +        // add in the warnings
  +        for (Enumeration e = other.warnings.elements(); e.hasMoreElements();) {
  +            warnings.addElement(e.nextElement());
  +        }
       }
       
  +    /**
  +    * Write the manifest out to a print writer.
  +    *
  +    * @param writer the Writer to which the manifest is written
  +    *
  +    * @throws IOException if the manifest cannot be written
  +    */
       public void write(PrintWriter writer) throws IOException {
  -        writer.println(ATTR_MANIFEST_VERSION + ": " + manifestVersion);
  -        String signatureVersion = mainSection.getAttributeValue(ATTR_SIGNATURE_VERSION);
  +        writer.println(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion);
  +        String signatureVersion = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
           if (signatureVersion != null) {
  -            writer.println(ATTR_SIGNATURE_VERSION + ": " + signatureVersion);
  -            mainSection.removeAttribute(ATTR_SIGNATURE_VERSION);
  +            writer.println(ATTRIBUTE_SIGNATURE_VERSION + ": " + signatureVersion);
  +            mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
           }
           mainSection.write(writer);
           if (signatureVersion != null) {
  -            mainSection.addAttribute(new Attribute(ATTR_SIGNATURE_VERSION, signatureVersion));
  +            try {
  +                mainSection.addAttribute(new Attribute(ATTRIBUTE_SIGNATURE_VERSION, signatureVersion));
  +            }
  +            catch (ManifestException e) {
  +                // shouldn't happen - ignore
  +            }
           }
           
           for (Enumeration e = sections.elements(); e.hasMoreElements();) {
  @@ -326,6 +496,11 @@
           }
       }
       
  +    /**
  +     * Convert the manifest to its string representation
  +     *
  +     * @return a multiline string with the Manifest as it appears in a Manifest file.
  +     */
       public String toString() {
           StringWriter sw = new StringWriter();
           try {
  @@ -335,5 +510,14 @@
               return null;
           }
           return sw.toString();
  +    }
  +    
  +    /**
  +     * Get the warnings for this manifest.
  +     *
  +     * @return an enumeration of warning strings
  +     */
  +    public Enumeration getWarnings() {
  +        return warnings.elements();
       }
   }
  
  
  
  1.44      +33 -23    jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Zip.java
  
  Index: Zip.java
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Zip.java,v
  retrieving revision 1.43
  retrieving revision 1.44
  diff -u -r1.43 -r1.44
  --- Zip.java	2001/07/28 14:11:41	1.43
  +++ Zip.java	2001/07/30 11:15:17	1.44
  @@ -431,7 +431,39 @@
           throws IOException, BuildException
       {
       }
  +
       /**
  +     * Create an empty zip file
  +     *
  +     * @return true if the file is then considered up to date.
  +     */
  +    protected boolean createEmptyZip(File zipFile) {
  +        // In this case using java.util.zip will not work
  +        // because it does not permit a zero-entry archive.
  +        // Must create it manually.
  +        log("Note: creating empty "+archiveType+" archive " + zipFile, Project.MSG_INFO);
  +        try {
  +            OutputStream os = new FileOutputStream(zipFile);
  +            try {
  +                // Cf. PKZIP specification.
  +                byte[] empty = new byte[22];
  +                empty[0] = 80; // P
  +                empty[1] = 75; // K
  +                empty[2] = 5;
  +                empty[3] = 6;
  +                // remainder zeros
  +                os.write(empty);
  +            } finally {
  +                os.close();
  +            }
  +        } catch (IOException ioe) {
  +            throw new BuildException("Could not create empty ZIP archive", ioe, location);
  +        }
  +        return true;
  +    }
  +    
  +    
  +    /**
        * Check whether the archive is up-to-date; and handle behavior for empty archives.
        * @param scanners list of prepared scanners containing files to archive
        * @param zipFile intended archive file (may or may not exist)
  @@ -453,29 +485,7 @@
                                            ": no files were included.", location);
               } else {
                   // Create.
  -                if (zipFile.exists()) return true;
  -                // In this case using java.util.zip will not work
  -                // because it does not permit a zero-entry archive.
  -                // Must create it manually.
  -                log("Note: creating empty "+archiveType+" archive " + zipFile, Project.MSG_INFO);
  -                try {
  -                    OutputStream os = new FileOutputStream(zipFile);
  -                    try {
  -                        // Cf. PKZIP specification.
  -                        byte[] empty = new byte[22];
  -                        empty[0] = 80; // P
  -                        empty[1] = 75; // K
  -                        empty[2] = 5;
  -                        empty[3] = 6;
  -                        // remainder zeros
  -                        os.write(empty);
  -                    } finally {
  -                        os.close();
  -                    }
  -                } catch (IOException ioe) {
  -                    throw new BuildException("Could not create empty ZIP archive", ioe, location);
  -                }
  -                return true;
  +                return createEmptyZip(zipFile);
               }
           } else {
               for (int i = 0; i < files.length; ++i) {
  
  
  
  1.1                  jakarta-ant/src/main/org/apache/tools/ant/taskdefs/ManifestException.java
  
  Index: ManifestException.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Ant", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.tools.ant.taskdefs;
  
  
  import java.io.*;
  
  /**
   * Exception thrown indicating problems in a JAR Manifest
   *
   * @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
   */
  public class ManifestException extends Exception {
  
      /**
       * Constructs an exception with the given descriptive message.
       * @param msg Description of or information about the exception.
       */
      public ManifestException(String msg) {
          super(msg);
      }
  }