You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Zip.java 33 kB

Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.io.File;
  56. import java.io.IOException;
  57. import java.io.InputStream;
  58. import java.io.FileOutputStream;
  59. import java.io.FileInputStream;
  60. import java.io.OutputStream;
  61. import java.io.ByteArrayOutputStream;
  62. import java.io.ByteArrayInputStream;
  63. import java.util.Enumeration;
  64. import java.util.Hashtable;
  65. import java.util.Stack;
  66. import java.util.Vector;
  67. import java.util.zip.CRC32;
  68. import java.util.zip.ZipInputStream;
  69. import org.apache.tools.ant.BuildException;
  70. import org.apache.tools.ant.FileScanner;
  71. import org.apache.tools.ant.Project;
  72. import org.apache.tools.ant.DirectoryScanner;
  73. import org.apache.tools.ant.types.FileSet;
  74. import org.apache.tools.ant.types.EnumeratedAttribute;
  75. import org.apache.tools.ant.types.PatternSet;
  76. import org.apache.tools.ant.types.ZipFileSet;
  77. import org.apache.tools.ant.types.ZipScanner;
  78. import org.apache.tools.ant.util.FileUtils;
  79. import org.apache.tools.ant.util.SourceFileScanner;
  80. import org.apache.tools.ant.util.MergingMapper;
  81. import org.apache.tools.zip.ZipOutputStream;
  82. import org.apache.tools.zip.ZipEntry;
  83. /**
  84. * Create a ZIP archive.
  85. *
  86. * @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
  87. * @author Jon S. Stevens <a href="mailto:jon@clearink.com">jon@clearink.com</a>
  88. * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  89. *
  90. * @since Ant 1.1
  91. *
  92. * @ant.task category="packaging"
  93. */
  94. public class Zip extends MatchingTask {
  95. protected File zipFile;
  96. private File baseDir;
  97. protected Hashtable entries = new Hashtable();
  98. private Vector groupfilesets = new Vector();
  99. private Vector filesetsFromGroupfilesets = new Vector();
  100. protected String duplicate = "add";
  101. private boolean doCompress = true;
  102. private boolean doUpdate = false;
  103. // shadow of the above if the value is altered in execute
  104. private boolean savedDoUpdate = false;
  105. private boolean doFilesonly = false;
  106. protected String archiveType = "zip";
  107. // For directories:
  108. private final static long EMPTY_CRC = new CRC32 ().getValue ();
  109. protected String emptyBehavior = "skip";
  110. private Vector filesets = new Vector ();
  111. protected Hashtable addedDirs = new Hashtable();
  112. private Vector addedFiles = new Vector();
  113. /**
  114. * true when we are adding new files into the Zip file, as opposed
  115. * to adding back the unchanged files
  116. */
  117. private boolean addingNewFiles = false;
  118. /**
  119. * Encoding to use for filenames, defaults to the platform's
  120. * default encoding.
  121. */
  122. private String encoding;
  123. /**
  124. * This is the name/location of where to
  125. * create the .zip file.
  126. *
  127. * @deprecated Use setDestFile(File) instead.
  128. */
  129. public void setZipfile(File zipFile) {
  130. setDestFile(zipFile);
  131. }
  132. /**
  133. * This is the name/location of where to
  134. * create the .zip file.
  135. * @since 1.5alpha
  136. * @deprecated Use setDestFile(File) instead
  137. */
  138. public void setFile(File file) {
  139. setDestFile(file);
  140. }
  141. /**
  142. * Sets the destfile attribute.
  143. * @since 1.5
  144. * @param destFile The new destination File
  145. */
  146. public void setDestFile(File destFile) {
  147. this.zipFile = destFile;
  148. }
  149. /**
  150. * This is the base directory to look in for
  151. * things to zip.
  152. */
  153. public void setBasedir(File baseDir) {
  154. this.baseDir = baseDir;
  155. }
  156. /**
  157. * Sets whether we want to compress the files or only store them.
  158. */
  159. public void setCompress(boolean c) {
  160. doCompress = c;
  161. }
  162. /**
  163. * Emulate Sun's jar utility by not adding parent dirs
  164. */
  165. public void setFilesonly(boolean f) {
  166. doFilesonly = f;
  167. }
  168. /**
  169. * Sets whether we want to update the file (if it exists)
  170. * or create a new one.
  171. */
  172. public void setUpdate(boolean c) {
  173. doUpdate = c;
  174. savedDoUpdate = c;
  175. }
  176. /**
  177. * Are we updating an existing archive?
  178. */
  179. public boolean isInUpdateMode() {
  180. return doUpdate;
  181. }
  182. /**
  183. * Adds a set of files (nested fileset attribute).
  184. */
  185. public void addFileset(FileSet set) {
  186. filesets.addElement(set);
  187. }
  188. /**
  189. * Adds a set of files (nested zipfileset attribute) that can be
  190. * read from an archive and be given a prefix/fullpath.
  191. */
  192. public void addZipfileset(ZipFileSet set) {
  193. filesets.addElement(set);
  194. }
  195. /**
  196. * Adds a group of zip files (a group of nested filesets).
  197. */
  198. public void addZipGroupFileset(FileSet set) {
  199. groupfilesets.addElement(set);
  200. }
  201. /**
  202. * Sets behavior for when a duplicate file is about to be added
  203. * Possible values are: <code>keep</code> (keep both
  204. * of the files); <code>skip</code> (keep the first version
  205. * of the file found); <code>overwrite</code> overwrite the file
  206. * with the new file
  207. * Default for zip tasks is <code>keep</code>
  208. */
  209. public void setDuplicate(Duplicate df) {
  210. duplicate = df.getValue();
  211. }
  212. /** Possible behaviors when there are no matching files for the task. */
  213. public static class WhenEmpty extends EnumeratedAttribute {
  214. public String[] getValues() {
  215. return new String[] {"fail", "skip", "create"};
  216. }
  217. }
  218. /**
  219. * Sets behavior of the task when no files match.
  220. * Possible values are: <code>fail</code> (throw an exception
  221. * and halt the build); <code>skip</code> (do not create
  222. * any archive, but issue a warning); <code>create</code>
  223. * (make an archive with no entries).
  224. * Default for zip tasks is <code>skip</code>;
  225. * for jar tasks, <code>create</code>.
  226. */
  227. public void setWhenempty(WhenEmpty we) {
  228. emptyBehavior = we.getValue();
  229. }
  230. /**
  231. * Encoding to use for filenames, defaults to the platform's
  232. * default encoding.
  233. *
  234. * <p>For a list of possible values see <a
  235. * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.</p>
  236. */
  237. public void setEncoding(String encoding) {
  238. this.encoding = encoding;
  239. }
  240. public void execute() throws BuildException {
  241. if (baseDir == null && filesets.size() == 0
  242. && groupfilesets.size() == 0 && "zip".equals(archiveType)) {
  243. throw new BuildException("basedir attribute must be set, "
  244. + "or at least "
  245. + "one fileset must be given!");
  246. }
  247. if (zipFile == null) {
  248. throw new BuildException("You must specify the "
  249. + archiveType + " file to create!");
  250. }
  251. // Renamed version of original file, if it exists
  252. File renamedFile = null;
  253. // Whether or not an actual update is required -
  254. // we don't need to update if the original file doesn't exist
  255. addingNewFiles = true;
  256. doUpdate = doUpdate && zipFile.exists();
  257. if (doUpdate)
  258. {
  259. FileUtils fileUtils = FileUtils.newFileUtils();
  260. renamedFile =
  261. fileUtils.createTempFile("zip", ".tmp",
  262. fileUtils.getParentFile(zipFile));
  263. try
  264. {
  265. if (!zipFile.renameTo(renamedFile)) {
  266. throw new BuildException("Unable to rename old file to "
  267. + "temporary file");
  268. }
  269. }
  270. catch (SecurityException e)
  271. {
  272. throw new BuildException("Not allowed to rename old file to "
  273. + "temporary file");
  274. }
  275. }
  276. // Add the files found in groupfileset to fileset
  277. for (int i = 0; i < groupfilesets.size(); i++) {
  278. log("Processing groupfileset ", Project.MSG_VERBOSE);
  279. FileSet fs = (FileSet) groupfilesets.elementAt(i);
  280. FileScanner scanner = fs.getDirectoryScanner(project);
  281. String[] files = scanner.getIncludedFiles();
  282. File basedir = scanner.getBasedir();
  283. for (int j = 0; j < files.length; j++) {
  284. log("Adding file " + files[j] + " to fileset",
  285. Project.MSG_VERBOSE);
  286. ZipFileSet zf = new ZipFileSet();
  287. zf.setSrc(new File(basedir, files[j]));
  288. filesets.addElement(zf);
  289. filesetsFromGroupfilesets.addElement(zf);
  290. }
  291. }
  292. // Create the scanners to pass to isUpToDate().
  293. Vector dss = new Vector();
  294. if (baseDir != null) {
  295. dss.addElement(getDirectoryScanner(baseDir));
  296. }
  297. for (int i = 0; i < filesets.size(); i++) {
  298. FileSet fs = (FileSet) filesets.elementAt(i);
  299. dss.addElement (fs.getDirectoryScanner(project));
  300. }
  301. int dssSize = dss.size();
  302. FileScanner[] scanners = new FileScanner[dssSize];
  303. dss.copyInto(scanners);
  304. boolean success = false;
  305. try {
  306. // quick exit if the target is up to date
  307. // can also handle empty archives
  308. if (isUpToDate(scanners, zipFile)) {
  309. return;
  310. }
  311. String action = doUpdate ? "Updating " : "Building ";
  312. log(action + archiveType + ": " + zipFile.getAbsolutePath());
  313. ZipOutputStream zOut =
  314. new ZipOutputStream(new FileOutputStream(zipFile));
  315. zOut.setEncoding(encoding);
  316. try {
  317. if (doCompress) {
  318. zOut.setMethod(ZipOutputStream.DEFLATED);
  319. } else {
  320. zOut.setMethod(ZipOutputStream.STORED);
  321. }
  322. initZipOutputStream(zOut);
  323. // Add the implicit fileset to the archive.
  324. if (baseDir != null) {
  325. addFiles(getDirectoryScanner(baseDir), zOut, "", "");
  326. }
  327. // Add the explicit filesets to the archive.
  328. addFiles(filesets, zOut);
  329. if (doUpdate) {
  330. addingNewFiles = false;
  331. ZipFileSet oldFiles = new ZipFileSet();
  332. oldFiles.setSrc(renamedFile);
  333. for (int i = 0; i < addedFiles.size(); i++)
  334. {
  335. PatternSet.NameEntry ne = oldFiles.createExclude();
  336. ne.setName((String) addedFiles.elementAt(i));
  337. }
  338. Vector tmp = new Vector(1);
  339. tmp.addElement(oldFiles);
  340. addFiles(tmp, zOut);
  341. }
  342. finalizeZipOutputStream(zOut);
  343. // If we've been successful on an update, delete the
  344. // temporary file
  345. if (doUpdate) {
  346. if (!renamedFile.delete()) {
  347. log ("Warning: unable to delete temporary file " +
  348. renamedFile.getName(), Project.MSG_WARN);
  349. }
  350. }
  351. success = true;
  352. } finally {
  353. // Close the output stream.
  354. try {
  355. if (zOut != null) {
  356. zOut.close();
  357. }
  358. } catch (IOException ex) {
  359. // If we're in this finally clause because of an
  360. // exception, we don't really care if there's an
  361. // exception when closing the stream. E.g. if it
  362. // throws "ZIP file must have at least one entry",
  363. // because an exception happened before we added
  364. // any files, then we must swallow this
  365. // exception. Otherwise, the error that's reported
  366. // will be the close() error, which is not the
  367. // real cause of the problem.
  368. if (success) {
  369. throw ex;
  370. }
  371. }
  372. }
  373. } catch (IOException ioe) {
  374. String msg = "Problem creating " + archiveType + ": "
  375. + ioe.getMessage();
  376. // delete a bogus ZIP file
  377. if (!zipFile.delete()) {
  378. msg += " (and the archive is probably corrupt but I could not "
  379. + "delete it)";
  380. }
  381. if (doUpdate) {
  382. if (!renamedFile.renameTo(zipFile)) {
  383. msg += " (and I couldn't rename the temporary file " +
  384. renamedFile.getName() + " back)";
  385. }
  386. }
  387. throw new BuildException(msg, ioe, location);
  388. } finally {
  389. cleanUp();
  390. }
  391. }
  392. /**
  393. * Indicates if the task is adding new files into the archive as opposed to
  394. * copying back unchanged files from the backup copy
  395. */
  396. protected boolean isAddingNewFiles() {
  397. return addingNewFiles;
  398. }
  399. /**
  400. * Add all files of the given FileScanner to the ZipOutputStream
  401. * prependig the given prefix to each filename.
  402. *
  403. * <p>Ensure parent directories have been added as well.
  404. */
  405. protected void addFiles(FileScanner scanner, ZipOutputStream zOut,
  406. String prefix, String fullpath)
  407. throws IOException {
  408. if (prefix.length() > 0 && fullpath.length() > 0) {
  409. throw new BuildException("Both prefix and fullpath attributes must"
  410. + " not be set on the same fileset.");
  411. }
  412. File thisBaseDir = scanner.getBasedir();
  413. // directories that matched include patterns
  414. String[] dirs = scanner.getIncludedDirectories();
  415. if (dirs.length > 0 && fullpath.length() > 0) {
  416. throw new BuildException("fullpath attribute may only be specified"
  417. + " for filesets that specify a single"
  418. + " file.");
  419. }
  420. for (int i = 0; i < dirs.length; i++) {
  421. if ("".equals(dirs[i])) {
  422. continue;
  423. }
  424. String name = dirs[i].replace(File.separatorChar, '/');
  425. if (!name.endsWith("/")) {
  426. name += "/";
  427. }
  428. addParentDirs(thisBaseDir, name, zOut, prefix);
  429. }
  430. // files that matched include patterns
  431. String[] files = scanner.getIncludedFiles();
  432. if (files.length > 1 && fullpath.length() > 0) {
  433. throw new BuildException("fullpath attribute may only be specified"
  434. + " for filesets that specify a single"
  435. + "file.");
  436. }
  437. for (int i = 0; i < files.length; i++) {
  438. File f = new File(thisBaseDir, files[i]);
  439. if (fullpath.length() > 0)
  440. {
  441. // Add this file at the specified location.
  442. addParentDirs(null, fullpath, zOut, "");
  443. zipFile(f, zOut, fullpath);
  444. }
  445. else
  446. {
  447. // Add this file with the specified prefix.
  448. String name = files[i].replace(File.separatorChar, '/');
  449. addParentDirs(thisBaseDir, name, zOut, prefix);
  450. zipFile(f, zOut, prefix + name);
  451. }
  452. }
  453. }
  454. protected void addZipEntries(ZipFileSet fs, DirectoryScanner ds,
  455. ZipOutputStream zOut, String prefix,
  456. String fullpath)
  457. throws IOException
  458. {
  459. log("adding zip entries: " + fullpath, Project.MSG_VERBOSE);
  460. if (prefix.length() > 0 && fullpath.length() > 0) {
  461. throw new BuildException("Both prefix and fullpath attributes must"
  462. + " not be set on the same fileset.");
  463. }
  464. ZipScanner zipScanner = (ZipScanner) ds;
  465. File zipSrc = fs.getSrc();
  466. ZipEntry entry;
  467. java.util.zip.ZipEntry origEntry;
  468. ZipInputStream in = null;
  469. try {
  470. in = new ZipInputStream(new FileInputStream(zipSrc));
  471. while ((origEntry = in.getNextEntry()) != null) {
  472. entry = new ZipEntry(origEntry);
  473. String vPath = entry.getName();
  474. if (zipScanner.match(vPath)) {
  475. if (fullpath.length() > 0) {
  476. addParentDirs(null, fullpath, zOut, "");
  477. zipFile(in, zOut, fullpath, entry.getTime(), zipSrc);
  478. } else {
  479. addParentDirs(null, vPath, zOut, prefix);
  480. if (!entry.isDirectory()) {
  481. zipFile(in, zOut, prefix + vPath, entry.getTime(),
  482. zipSrc);
  483. }
  484. }
  485. }
  486. }
  487. } finally {
  488. if (in != null) {
  489. in.close();
  490. }
  491. }
  492. }
  493. protected void initZipOutputStream(ZipOutputStream zOut)
  494. throws IOException, BuildException
  495. {
  496. }
  497. protected void finalizeZipOutputStream(ZipOutputStream zOut)
  498. throws IOException, BuildException
  499. {
  500. }
  501. /**
  502. * Create an empty zip file
  503. *
  504. * @return true if the file is then considered up to date.
  505. */
  506. protected boolean createEmptyZip(File zipFile) {
  507. // In this case using java.util.zip will not work
  508. // because it does not permit a zero-entry archive.
  509. // Must create it manually.
  510. log("Note: creating empty " + archiveType + " archive " + zipFile,
  511. Project.MSG_INFO);
  512. OutputStream os = null;
  513. try {
  514. os = new FileOutputStream(zipFile);
  515. // Cf. PKZIP specification.
  516. byte[] empty = new byte[22];
  517. empty[0] = 80; // P
  518. empty[1] = 75; // K
  519. empty[2] = 5;
  520. empty[3] = 6;
  521. // remainder zeros
  522. os.write(empty);
  523. } catch (IOException ioe) {
  524. throw new BuildException("Could not create empty ZIP archive "
  525. + "(" + ioe.getMessage() + ")", ioe,
  526. location);
  527. } finally {
  528. if (os != null) {
  529. try {
  530. os.close();
  531. } catch (IOException e) {
  532. }
  533. }
  534. }
  535. return true;
  536. }
  537. /**
  538. * Check whether the archive is up-to-date; and handle behavior
  539. * for empty archives.
  540. * @param scanners list of prepared scanners containing files to archive
  541. * @param zipFile intended archive file (may or may not exist)
  542. * @return true if nothing need be done (may have done something
  543. * already); false if archive creation should proceed
  544. * @exception BuildException if it likes
  545. */
  546. protected boolean isUpToDate(FileScanner[] scanners, File zipFile)
  547. throws BuildException
  548. {
  549. String[][] fileNames = grabFileNames(scanners);
  550. File[] files = grabFiles(scanners, fileNames);
  551. if (files.length == 0) {
  552. if (emptyBehavior.equals("skip")) {
  553. log("Warning: skipping " + archiveType + " archive " + zipFile +
  554. " because no files were included.", Project.MSG_WARN);
  555. return true;
  556. } else if (emptyBehavior.equals("fail")) {
  557. throw new BuildException("Cannot create " + archiveType
  558. + " archive " + zipFile +
  559. ": no files were included.", location);
  560. } else {
  561. // Create.
  562. return createEmptyZip(zipFile);
  563. }
  564. } else {
  565. for (int i = 0; i < files.length; ++i) {
  566. if (files[i].equals(zipFile)) {
  567. throw new BuildException("A zip file cannot include itself",
  568. location);
  569. }
  570. }
  571. if (!zipFile.exists()) {
  572. return false;
  573. }
  574. SourceFileScanner sfs = new SourceFileScanner(this);
  575. MergingMapper mm = new MergingMapper();
  576. mm.setTo(zipFile.getAbsolutePath());
  577. for (int i = 0; i < scanners.length; i++) {
  578. if (sfs.restrict(fileNames[i], scanners[i].getBasedir(), null,
  579. mm).length > 0) {
  580. return false;
  581. }
  582. }
  583. return true;
  584. }
  585. }
  586. protected static File[] grabFiles(FileScanner[] scanners) {
  587. return grabFiles(scanners, grabFileNames(scanners));
  588. }
  589. protected static File[] grabFiles(FileScanner[] scanners,
  590. String[][] fileNames) {
  591. Vector files = new Vector();
  592. for (int i = 0; i < fileNames.length; i++) {
  593. File thisBaseDir = scanners[i].getBasedir();
  594. for (int j = 0; j < fileNames[i].length; j++) {
  595. files.addElement(new File(thisBaseDir, fileNames[i][j]));
  596. }
  597. }
  598. File[] toret = new File[files.size()];
  599. files.copyInto(toret);
  600. return toret;
  601. }
  602. protected static String[][] grabFileNames(FileScanner[] scanners) {
  603. String[][] result = new String[scanners.length][];
  604. for (int i = 0; i < scanners.length; i++) {
  605. String[] files = scanners[i].getIncludedFiles();
  606. String[] dirs = scanners[i].getIncludedDirectories();
  607. result[i] = new String[files.length + dirs.length];
  608. System.arraycopy(files, 0, result[i], 0, files.length);
  609. System.arraycopy(dirs, 0, result[i], files.length, dirs.length);
  610. }
  611. return result;
  612. }
  613. protected void zipDir(File dir, ZipOutputStream zOut, String vPath)
  614. throws IOException
  615. {
  616. if (addedDirs.get(vPath) != null) {
  617. // don't add directories we've already added.
  618. // no warning if we try, it is harmless in and of itself
  619. return;
  620. }
  621. log("adding directory " + vPath, Project.MSG_VERBOSE);
  622. addedDirs.put(vPath, vPath);
  623. ZipEntry ze = new ZipEntry (vPath);
  624. if (dir != null && dir.exists()) {
  625. ze.setTime(dir.lastModified());
  626. } else {
  627. ze.setTime(System.currentTimeMillis());
  628. }
  629. ze.setSize (0);
  630. ze.setMethod (ZipEntry.STORED);
  631. // This is faintly ridiculous:
  632. ze.setCrc (EMPTY_CRC);
  633. // this is 040775 | MS-DOS directory flag in reverse byte order
  634. ze.setExternalAttributes(0x41FD0010L);
  635. zOut.putNextEntry (ze);
  636. }
  637. protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath,
  638. long lastModified, File file)
  639. throws IOException
  640. {
  641. if (entries.contains(vPath)) {
  642. if (duplicate.equals("preserve"))
  643. {
  644. log(vPath + " already added, skipping", Project.MSG_INFO);
  645. return;
  646. }
  647. else if (duplicate.equals("fail"))
  648. {
  649. throw new BuildException("Duplicate file " + vPath
  650. + " was found and the duplicate "
  651. + "attribute is 'fail'.");
  652. }
  653. else
  654. {
  655. // duplicate equal to add, so we continue
  656. log("duplicate file " + vPath
  657. + " found, adding.", Project.MSG_VERBOSE);
  658. }
  659. }
  660. else
  661. {
  662. log("adding entry " + vPath, Project.MSG_VERBOSE);
  663. }
  664. entries.put(vPath, vPath);
  665. ZipEntry ze = new ZipEntry(vPath);
  666. ze.setTime(lastModified);
  667. /*
  668. * XXX ZipOutputStream.putEntry expects the ZipEntry to know its
  669. * size and the CRC sum before you start writing the data when using
  670. * STORED mode.
  671. *
  672. * This forces us to process the data twice.
  673. *
  674. * I couldn't find any documentation on this, just found out by try
  675. * and error.
  676. */
  677. if (!doCompress) {
  678. long size = 0;
  679. CRC32 cal = new CRC32();
  680. if (!in.markSupported()) {
  681. // Store data into a byte[]
  682. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  683. byte[] buffer = new byte[8 * 1024];
  684. int count = 0;
  685. do {
  686. size += count;
  687. cal.update(buffer, 0, count);
  688. bos.write(buffer, 0, count);
  689. count = in.read(buffer, 0, buffer.length);
  690. } while (count != -1);
  691. in = new ByteArrayInputStream(bos.toByteArray());
  692. } else {
  693. in.mark(Integer.MAX_VALUE);
  694. byte[] buffer = new byte[8 * 1024];
  695. int count = 0;
  696. do {
  697. size += count;
  698. cal.update(buffer, 0, count);
  699. count = in.read(buffer, 0, buffer.length);
  700. } while (count != -1);
  701. in.reset();
  702. }
  703. ze.setSize(size);
  704. ze.setCrc(cal.getValue());
  705. }
  706. zOut.putNextEntry(ze);
  707. byte[] buffer = new byte[8 * 1024];
  708. int count = 0;
  709. do {
  710. if (count != 0) {
  711. zOut.write(buffer, 0, count);
  712. }
  713. count = in.read(buffer, 0, buffer.length);
  714. } while (count != -1);
  715. addedFiles.addElement(vPath);
  716. }
  717. protected void zipFile(File file, ZipOutputStream zOut, String vPath)
  718. throws IOException
  719. {
  720. if (file.equals(zipFile)) {
  721. throw new BuildException("A zip file cannot include itself",
  722. location);
  723. }
  724. FileInputStream fIn = new FileInputStream(file);
  725. try {
  726. zipFile(fIn, zOut, vPath, file.lastModified(), null);
  727. } finally {
  728. fIn.close();
  729. }
  730. }
  731. /**
  732. * Ensure all parent dirs of a given entry have been added.
  733. */
  734. protected void addParentDirs(File baseDir, String entry,
  735. ZipOutputStream zOut, String prefix)
  736. throws IOException {
  737. if (!doFilesonly) {
  738. Stack directories = new Stack();
  739. int slashPos = entry.length();
  740. while ((slashPos = entry.lastIndexOf('/', slashPos - 1)) != -1) {
  741. String dir = entry.substring(0, slashPos + 1);
  742. if (addedDirs.get(prefix + dir) != null) {
  743. break;
  744. }
  745. directories.push(dir);
  746. }
  747. while (!directories.isEmpty()) {
  748. String dir = (String) directories.pop();
  749. File f = null;
  750. if (baseDir != null) {
  751. f = new File(baseDir, dir);
  752. } else {
  753. f = new File(dir);
  754. }
  755. zipDir(f, zOut, prefix + dir);
  756. }
  757. }
  758. }
  759. /**
  760. * Iterate over the given Vector of (zip)filesets and add
  761. * all files to the ZipOutputStream using the given prefix
  762. * or fullpath.
  763. */
  764. protected void addFiles(Vector filesets, ZipOutputStream zOut)
  765. throws IOException {
  766. // Add each fileset in the Vector.
  767. for (int i = 0; i < filesets.size(); i++) {
  768. FileSet fs = (FileSet) filesets.elementAt(i);
  769. DirectoryScanner ds = fs.getDirectoryScanner(project);
  770. String prefix = "";
  771. String fullpath = "";
  772. if (fs instanceof ZipFileSet) {
  773. ZipFileSet zfs = (ZipFileSet) fs;
  774. prefix = zfs.getPrefix();
  775. fullpath = zfs.getFullpath();
  776. }
  777. if (prefix.length() > 0
  778. && !prefix.endsWith("/")
  779. && !prefix.endsWith("\\")) {
  780. prefix += "/";
  781. }
  782. // Need to manually add either fullpath's parent directory, or
  783. // the prefix directory, to the archive.
  784. if (prefix.length() > 0) {
  785. addParentDirs(null, prefix, zOut, "");
  786. zipDir(null, zOut, prefix);
  787. } else if (fullpath.length() > 0) {
  788. addParentDirs(null, fullpath, zOut, "");
  789. }
  790. if (fs instanceof ZipFileSet
  791. && ((ZipFileSet) fs).getSrc() != null) {
  792. addZipEntries((ZipFileSet) fs, ds, zOut, prefix, fullpath);
  793. } else {
  794. // Add the fileset.
  795. addFiles(ds, zOut, prefix, fullpath);
  796. }
  797. }
  798. }
  799. /**
  800. * Do any clean up necessary to allow this instance to be used again.
  801. *
  802. * <p>When we get here, the Zip file has been closed and all we
  803. * need to do is to reset some globals.</p>
  804. *
  805. * <p>This method will only reset globals that have been changed
  806. * during execute(), it will not alter the attributes or nested
  807. * child elements. If you want to reset the instance so that you
  808. * can later zip a completely different set of files, you must use
  809. * the reset method.</p>
  810. *
  811. * @see #reset
  812. */
  813. protected void cleanUp() {
  814. addedDirs.clear();
  815. addedFiles.removeAllElements();
  816. entries.clear();
  817. addingNewFiles = false;
  818. doUpdate = savedDoUpdate;
  819. Enumeration enum = filesetsFromGroupfilesets.elements();
  820. while (enum.hasMoreElements()) {
  821. ZipFileSet zf = (ZipFileSet) enum.nextElement();
  822. filesets.removeElement(zf);
  823. }
  824. filesetsFromGroupfilesets.removeAllElements();
  825. }
  826. /**
  827. * Makes this instance reset all attributes to their default
  828. * values and forget all children.
  829. *
  830. * @since 1.72, Ant 1.5
  831. *
  832. * @see #cleanUp
  833. */
  834. public void reset() {
  835. filesets.removeAllElements();
  836. zipFile = null;
  837. baseDir = null;
  838. groupfilesets.removeAllElements();
  839. duplicate = "add";
  840. archiveType = "zip";
  841. doCompress = true;
  842. emptyBehavior = "skip";
  843. doUpdate = false;
  844. doFilesonly = false;
  845. encoding = null;
  846. }
  847. /** Possible behaviors when a duplicate file is added. */
  848. public static class Duplicate extends EnumeratedAttribute {
  849. public String[] getValues() {
  850. return new String[] {"add", "preserve", "fail"};
  851. }
  852. }
  853. }