/* * The Apache Software License, Version 1.1 * * Copyright (c) 1999 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 * . */ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.*; import org.apache.tools.ant.types.ZipFileSet; import org.apache.tools.zip.*; import java.io.*; import java.util.Enumeration; /** * Creates a JAR archive. * * @author James Davidson duncan@x180.com */ public class Jar extends Zip { private File manifestFile; private Manifest manifest; private Manifest execManifest; private boolean buildFileManifest = false; public Jar() { super(); archiveType = "jar"; emptyBehavior = "create"; setEncoding("UTF8"); } public void setJarfile(File jarFile) { super.setZipfile(jarFile); } public void addConfiguredManifest(Manifest newManifest) throws ManifestException { if (manifest == null) { manifest = getDefaultManifest(); } manifest.merge(newManifest); buildFileManifest = true; } public void setManifest(File manifestFile) { if (!manifestFile.exists()) { throw new BuildException("Manifest file: " + manifestFile + " does not exist.", getLocation()); } this.manifestFile = manifestFile; InputStream is = null; try { is = new FileInputStream(manifestFile); Manifest newManifest = new Manifest(is); if (manifest == null) { manifest = getDefaultManifest(); } 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); } finally { if (is != null) { try { is.close(); } catch (IOException e) { // do nothing } } } } public void addMetainf(ZipFileSet fs) { // We just set the prefix for this fileset, and pass it up. fs.setPrefix("META-INF/"); super.addFileset(fs); } protected void initZipOutputStream(ZipOutputStream zOut) throws IOException, BuildException { 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()); } } 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 !!"); } catch (IOException e) { throw new BuildException("Unable to read default manifest", e); } } protected void finalizeZipOutputStream(ZipOutputStream zOut) throws IOException, BuildException { 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(); PrintWriter writer = new PrintWriter(baos); execManifest.write(writer); writer.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); super.zipFile(bais, zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis()); super.finalizeZipOutputStream(zOut); } /** * Handle situation when we encounter a manifest file * * If we haven't been given one, we use this one. * * If we have, we merge the manifest in, provided it is a new file * and not the old one from the JAR we are updating */ private void zipManifestEntry(InputStream is) throws IOException { try { if (execManifest == null) { execManifest = 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()); } } protected void zipFile(File file, ZipOutputStream zOut, String vPath) throws IOException { // If the file being added is META-INF/MANIFEST.MF, we merge it with the // current manifest if (vPath.equalsIgnoreCase("META-INF/MANIFEST.MF")) { InputStream is = null; try { is = new FileInputStream(file); zipManifestEntry(is); } catch (IOException e) { throw new BuildException("Unable to read manifest file: " + file, e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { // do nothing } } } } else { super.zipFile(file, zOut, vPath); } } protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath, long lastModified) throws IOException { // If the file being added is META-INF/MANIFEST.MF, we merge it with the // current manifest if (vPath.equalsIgnoreCase("META-INF/MANIFEST.MF")) { try { zipManifestEntry(is); } catch (IOException e) { throw new BuildException("Unable to read manifest file: ", e); } } else { super.zipFile(is, zOut, vPath, lastModified); } } /** * 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 (buildFileManifest || manifestFile == null) { java.util.zip.ZipFile theZipFile = null; try { theZipFile = new java.util.zip.ZipFile(zipFile); java.util.zip.ZipEntry entry = theZipFile.getEntry("META-INF/MANIFEST.MF"); if (entry == null) { return false; } Manifest currentManifest = new Manifest(theZipFile.getInputStream(entry)); if (!currentManifest.equals(manifest)) { return false; } } catch (Exception e) { // any problems and we will rebuild return false; } finally { if (theZipFile != null) { try { theZipFile.close(); } catch (IOException e) { //ignore } } } } else if (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. */ protected void cleanUp() { super.cleanUp(); } }