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 82 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 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
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.ByteArrayInputStream;
  20. import java.io.ByteArrayOutputStream;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.OutputStream;
  25. import java.nio.file.Files;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.Collections;
  29. import java.util.Comparator;
  30. import java.util.Enumeration;
  31. import java.util.HashMap;
  32. import java.util.Hashtable;
  33. import java.util.Map;
  34. import java.util.Stack;
  35. import java.util.Vector;
  36. import java.util.zip.CRC32;
  37. import org.apache.tools.ant.BuildException;
  38. import org.apache.tools.ant.DirectoryScanner;
  39. import org.apache.tools.ant.FileScanner;
  40. import org.apache.tools.ant.Project;
  41. import org.apache.tools.ant.types.ArchiveFileSet;
  42. import org.apache.tools.ant.types.EnumeratedAttribute;
  43. import org.apache.tools.ant.types.FileSet;
  44. import org.apache.tools.ant.types.PatternSet;
  45. import org.apache.tools.ant.types.Resource;
  46. import org.apache.tools.ant.types.ResourceCollection;
  47. import org.apache.tools.ant.types.ZipFileSet;
  48. import org.apache.tools.ant.types.ZipScanner;
  49. import org.apache.tools.ant.types.resources.ArchiveResource;
  50. import org.apache.tools.ant.types.resources.FileProvider;
  51. import org.apache.tools.ant.types.resources.FileResource;
  52. import org.apache.tools.ant.types.resources.Union;
  53. import org.apache.tools.ant.types.resources.ZipResource;
  54. import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
  55. import org.apache.tools.ant.util.FileNameMapper;
  56. import org.apache.tools.ant.util.FileUtils;
  57. import org.apache.tools.ant.util.GlobPatternMapper;
  58. import org.apache.tools.ant.util.IdentityMapper;
  59. import org.apache.tools.ant.util.MergingMapper;
  60. import org.apache.tools.ant.util.ResourceUtils;
  61. import org.apache.tools.zip.UnixStat;
  62. import org.apache.tools.zip.Zip64Mode;
  63. import org.apache.tools.zip.ZipEntry;
  64. import org.apache.tools.zip.ZipExtraField;
  65. import org.apache.tools.zip.ZipFile;
  66. import org.apache.tools.zip.ZipOutputStream;
  67. import org.apache.tools.zip.ZipOutputStream.UnicodeExtraFieldPolicy;
  68. /**
  69. * Create a Zip file.
  70. *
  71. * @since Ant 1.1
  72. *
  73. * @ant.task category="packaging"
  74. */
  75. public class Zip extends MatchingTask {
  76. private static final int BUFFER_SIZE = 8 * 1024;
  77. /**
  78. * The granularity of timestamps inside a ZIP archive.
  79. */
  80. private static final int ZIP_FILE_TIMESTAMP_GRANULARITY = 2000;
  81. private static final int ROUNDUP_MILLIS = ZIP_FILE_TIMESTAMP_GRANULARITY - 1;
  82. // CheckStyle:VisibilityModifier OFF - bc
  83. protected File zipFile;
  84. // use to scan own archive
  85. private ZipScanner zs;
  86. private File baseDir;
  87. protected Hashtable<String, String> entries = new Hashtable<String, String>();
  88. private final Vector<FileSet> groupfilesets = new Vector<FileSet>();
  89. private final Vector<ZipFileSet> filesetsFromGroupfilesets = new Vector<ZipFileSet>();
  90. protected String duplicate = "add";
  91. private boolean doCompress = true;
  92. private boolean doUpdate = false;
  93. // shadow of the above if the value is altered in execute
  94. private boolean savedDoUpdate = false;
  95. private boolean doFilesonly = false;
  96. protected String archiveType = "zip";
  97. // For directories:
  98. private static final long EMPTY_CRC = new CRC32 ().getValue ();
  99. protected String emptyBehavior = "skip";
  100. private final Vector<ResourceCollection> resources = new Vector<ResourceCollection>();
  101. protected Hashtable<String, String> addedDirs = new Hashtable<String, String>();
  102. private final Vector<String> addedFiles = new Vector<String>();
  103. private static final ResourceSelector MISSING_SELECTOR =
  104. new ResourceSelector() {
  105. public boolean isSelected(final Resource target) {
  106. return !target.isExists();
  107. }
  108. };
  109. private static final ResourceUtils.ResourceSelectorProvider
  110. MISSING_DIR_PROVIDER = new ResourceUtils.ResourceSelectorProvider() {
  111. public ResourceSelector
  112. getTargetSelectorForSource(final Resource sr) {
  113. return MISSING_SELECTOR;
  114. }
  115. };
  116. /**
  117. * If this flag is true, execute() will run most operations twice,
  118. * the first time with {@link #skipWriting skipWriting} set to
  119. * true and the second time with setting it to false.
  120. *
  121. * <p>The only situation in Ant's current code base where this is
  122. * ever going to be true is if the jar task has been configured
  123. * with a filesetmanifest other than "skip".</p>
  124. */
  125. protected boolean doubleFilePass = false;
  126. /**
  127. * whether the methods should just perform some sort of dry-run.
  128. *
  129. * <p>Will only ever be true in the first pass if the task
  130. * performs two passes because {@link #doubleFilePass
  131. * doubleFilePass} is true.</p>
  132. */
  133. protected boolean skipWriting = false;
  134. /**
  135. * Whether this is the first time the archive building methods are invoked.
  136. *
  137. * @return true if either {@link #doubleFilePass doubleFilePass}
  138. * is false or {@link #skipWriting skipWriting} is true.
  139. *
  140. * @since Ant 1.8.0
  141. */
  142. protected final boolean isFirstPass() {
  143. return !doubleFilePass || skipWriting;
  144. }
  145. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  146. // CheckStyle:VisibilityModifier ON
  147. // This boolean is set if the task detects that the
  148. // target is outofdate and has written to the target file.
  149. private boolean updatedFile = false;
  150. /**
  151. * true when we are adding new files into the Zip file, as opposed
  152. * to adding back the unchanged files
  153. */
  154. private boolean addingNewFiles = false;
  155. /**
  156. * Encoding to use for filenames, defaults to the platform's
  157. * default encoding.
  158. */
  159. private String encoding;
  160. /**
  161. * Whether the original compression of entries coming from a ZIP
  162. * archive should be kept (for example when updating an archive).
  163. *
  164. * @since Ant 1.6
  165. */
  166. private boolean keepCompression = false;
  167. /**
  168. * Whether the file modification times will be rounded up to the
  169. * next even number of seconds.
  170. *
  171. * @since Ant 1.6.2
  172. */
  173. private boolean roundUp = true;
  174. /**
  175. * Comment for the archive.
  176. * @since Ant 1.6.3
  177. */
  178. private String comment = "";
  179. private int level = ZipOutputStream.DEFAULT_COMPRESSION;
  180. /**
  181. * Assume 0 Unix mode is intentional.
  182. * @since Ant 1.8.0
  183. */
  184. private boolean preserve0Permissions = false;
  185. /**
  186. * Whether to set the language encoding flag when creating the archive.
  187. *
  188. * @since Ant 1.8.0
  189. */
  190. private boolean useLanguageEncodingFlag = true;
  191. /**
  192. * Whether to add unicode extra fields.
  193. *
  194. * @since Ant 1.8.0
  195. */
  196. private UnicodeExtraField createUnicodeExtraFields =
  197. UnicodeExtraField.NEVER;
  198. /**
  199. * Whether to fall back to UTF-8 if a name cannot be encoded using
  200. * the specified encoding.
  201. *
  202. * @since Ant 1.8.0
  203. */
  204. private boolean fallBackToUTF8 = false;
  205. /**
  206. * Whether to enable Zip64 extensions.
  207. *
  208. * @since Ant 1.9.1
  209. */
  210. private Zip64ModeAttribute zip64Mode = Zip64ModeAttribute.AS_NEEDED;
  211. /**
  212. * This is the name/location of where to
  213. * create the .zip file.
  214. * @param zipFile the path of the zipFile
  215. * @deprecated since 1.5.x.
  216. * Use setDestFile(File) instead.
  217. * @ant.attribute ignore="true"
  218. */
  219. @Deprecated
  220. public void setZipfile(final File zipFile) {
  221. setDestFile(zipFile);
  222. }
  223. /**
  224. * This is the name/location of where to
  225. * create the file.
  226. * @param file the path of the zipFile
  227. * @since Ant 1.5
  228. * @deprecated since 1.5.x.
  229. * Use setDestFile(File) instead.
  230. * @ant.attribute ignore="true"
  231. */
  232. @Deprecated
  233. public void setFile(final File file) {
  234. setDestFile(file);
  235. }
  236. /**
  237. * The file to create; required.
  238. * @since Ant 1.5
  239. * @param destFile The new destination File
  240. */
  241. public void setDestFile(final File destFile) {
  242. this.zipFile = destFile;
  243. }
  244. /**
  245. * The file to create.
  246. * @return the destination file
  247. * @since Ant 1.5.2
  248. */
  249. public File getDestFile() {
  250. return zipFile;
  251. }
  252. /**
  253. * Directory from which to archive files; optional.
  254. * @param baseDir the base directory
  255. */
  256. public void setBasedir(final File baseDir) {
  257. this.baseDir = baseDir;
  258. }
  259. /**
  260. * Whether we want to compress the files or only store them;
  261. * optional, default=true;
  262. * @param c if true, compress the files
  263. */
  264. public void setCompress(final boolean c) {
  265. doCompress = c;
  266. }
  267. /**
  268. * Whether we want to compress the files or only store them;
  269. * @return true if the files are to be compressed
  270. * @since Ant 1.5.2
  271. */
  272. public boolean isCompress() {
  273. return doCompress;
  274. }
  275. /**
  276. * If true, emulate Sun's jar utility by not adding parent directories;
  277. * optional, defaults to false.
  278. * @param f if true, emulate sun's jar by not adding parent directories
  279. */
  280. public void setFilesonly(final boolean f) {
  281. doFilesonly = f;
  282. }
  283. /**
  284. * If true, updates an existing file, otherwise overwrite
  285. * any existing one; optional defaults to false.
  286. * @param c if true, updates an existing zip file
  287. */
  288. public void setUpdate(final boolean c) {
  289. doUpdate = c;
  290. savedDoUpdate = c;
  291. }
  292. /**
  293. * Are we updating an existing archive?
  294. * @return true if updating an existing archive
  295. */
  296. public boolean isInUpdateMode() {
  297. return doUpdate;
  298. }
  299. /**
  300. * Adds a set of files.
  301. * @param set the fileset to add
  302. */
  303. public void addFileset(final FileSet set) {
  304. add(set);
  305. }
  306. /**
  307. * Adds a set of files that can be
  308. * read from an archive and be given a prefix/fullpath.
  309. * @param set the zipfileset to add
  310. */
  311. public void addZipfileset(final ZipFileSet set) {
  312. add(set);
  313. }
  314. /**
  315. * Add a collection of resources to be archived.
  316. * @param a the resources to archive
  317. * @since Ant 1.7
  318. */
  319. public void add(final ResourceCollection a) {
  320. resources.add(a);
  321. }
  322. /**
  323. * Adds a group of zip files.
  324. * @param set the group (a fileset) to add
  325. */
  326. public void addZipGroupFileset(final FileSet set) {
  327. groupfilesets.addElement(set);
  328. }
  329. /**
  330. * Sets behavior for when a duplicate file is about to be added -
  331. * one of <code>add</code>, <code>preserve</code> or <code>fail</code>.
  332. * Possible values are: <code>add</code> (keep both
  333. * of the files); <code>preserve</code> (keep the first version
  334. * of the file found); <code>fail</code> halt a problem
  335. * Default for zip tasks is <code>add</code>
  336. * @param df a <code>Duplicate</code> enumerated value
  337. */
  338. public void setDuplicate(final Duplicate df) {
  339. duplicate = df.getValue();
  340. }
  341. /**
  342. * Possible behaviors when there are no matching files for the task:
  343. * "fail", "skip", or "create".
  344. */
  345. public static class WhenEmpty extends EnumeratedAttribute {
  346. /**
  347. * The string values for the enumerated value
  348. * @return the values
  349. */
  350. @Override
  351. public String[] getValues() {
  352. return new String[] {"fail", "skip", "create"};
  353. }
  354. }
  355. /**
  356. * Sets behavior of the task when no files match.
  357. * Possible values are: <code>fail</code> (throw an exception
  358. * and halt the build); <code>skip</code> (do not create
  359. * any archive, but issue a warning); <code>create</code>
  360. * (make an archive with no entries).
  361. * Default for zip tasks is <code>skip</code>;
  362. * for jar tasks, <code>create</code>.
  363. * @param we a <code>WhenEmpty</code> enumerated value
  364. */
  365. public void setWhenempty(final WhenEmpty we) {
  366. emptyBehavior = we.getValue();
  367. }
  368. /**
  369. * Encoding to use for filenames, defaults to the platform's
  370. * default encoding.
  371. *
  372. * <p>For a list of possible values see <a
  373. * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.</p>
  374. * @param encoding the encoding name
  375. */
  376. public void setEncoding(final String encoding) {
  377. this.encoding = encoding;
  378. }
  379. /**
  380. * Encoding to use for filenames.
  381. * @return the name of the encoding to use
  382. * @since Ant 1.5.2
  383. */
  384. public String getEncoding() {
  385. return encoding;
  386. }
  387. /**
  388. * Whether the original compression of entries coming from a ZIP
  389. * archive should be kept (for example when updating an archive).
  390. * Default is false.
  391. * @param keep if true, keep the original compression
  392. * @since Ant 1.6
  393. */
  394. public void setKeepCompression(final boolean keep) {
  395. keepCompression = keep;
  396. }
  397. /**
  398. * Comment to use for archive.
  399. *
  400. * @param comment The content of the comment.
  401. * @since Ant 1.6.3
  402. */
  403. public void setComment(final String comment) {
  404. this.comment = comment;
  405. }
  406. /**
  407. * Comment of the archive
  408. *
  409. * @return Comment of the archive.
  410. * @since Ant 1.6.3
  411. */
  412. public String getComment() {
  413. return comment;
  414. }
  415. /**
  416. * Set the compression level to use. Default is
  417. * ZipOutputStream.DEFAULT_COMPRESSION.
  418. * @param level compression level.
  419. * @since Ant 1.7
  420. */
  421. public void setLevel(final int level) {
  422. this.level = level;
  423. }
  424. /**
  425. * Get the compression level.
  426. * @return compression level.
  427. * @since Ant 1.7
  428. */
  429. public int getLevel() {
  430. return level;
  431. }
  432. /**
  433. * Whether the file modification times will be rounded up to the
  434. * next even number of seconds.
  435. *
  436. * <p>Zip archives store file modification times with a
  437. * granularity of two seconds, so the times will either be rounded
  438. * up or down. If you round down, the archive will always seem
  439. * out-of-date when you rerun the task, so the default is to round
  440. * up. Rounding up may lead to a different type of problems like
  441. * JSPs inside a web archive that seem to be slightly more recent
  442. * than precompiled pages, rendering precompilation useless.</p>
  443. * @param r a <code>boolean</code> value
  444. * @since Ant 1.6.2
  445. */
  446. public void setRoundUp(final boolean r) {
  447. roundUp = r;
  448. }
  449. /**
  450. * Assume 0 Unix mode is intentional.
  451. * @since Ant 1.8.0
  452. */
  453. public void setPreserve0Permissions(final boolean b) {
  454. preserve0Permissions = b;
  455. }
  456. /**
  457. * Assume 0 Unix mode is intentional.
  458. * @since Ant 1.8.0
  459. */
  460. public boolean getPreserve0Permissions() {
  461. return preserve0Permissions;
  462. }
  463. /**
  464. * Whether to set the language encoding flag.
  465. * @since Ant 1.8.0
  466. */
  467. public void setUseLanguageEncodingFlag(final boolean b) {
  468. useLanguageEncodingFlag = b;
  469. }
  470. /**
  471. * Whether the language encoding flag will be used.
  472. * @since Ant 1.8.0
  473. */
  474. public boolean getUseLanguageEnodingFlag() {
  475. return useLanguageEncodingFlag;
  476. }
  477. /**
  478. * Whether Unicode extra fields will be created.
  479. * @since Ant 1.8.0
  480. */
  481. public void setCreateUnicodeExtraFields(final UnicodeExtraField b) {
  482. createUnicodeExtraFields = b;
  483. }
  484. /**
  485. * Whether Unicode extra fields will be created.
  486. * @since Ant 1.8.0
  487. */
  488. public UnicodeExtraField getCreateUnicodeExtraFields() {
  489. return createUnicodeExtraFields;
  490. }
  491. /**
  492. * Whether to fall back to UTF-8 if a name cannot be encoded using
  493. * the specified encoding.
  494. *
  495. * <p>Defaults to false.</p>
  496. *
  497. * @since Ant 1.8.0
  498. */
  499. public void setFallBackToUTF8(final boolean b) {
  500. fallBackToUTF8 = b;
  501. }
  502. /**
  503. * Whether to fall back to UTF-8 if a name cannot be encoded using
  504. * the specified encoding.
  505. *
  506. * @since Ant 1.8.0
  507. */
  508. public boolean getFallBackToUTF8() {
  509. return fallBackToUTF8;
  510. }
  511. /**
  512. * Whether Zip64 extensions should be used.
  513. * @since Ant 1.9.1
  514. */
  515. public void setZip64Mode(final Zip64ModeAttribute b) {
  516. zip64Mode = b;
  517. }
  518. /**
  519. * Whether Zip64 extensions will be used.
  520. * @since Ant 1.9.1
  521. */
  522. public Zip64ModeAttribute getZip64Mode() {
  523. return zip64Mode;
  524. }
  525. /**
  526. * validate and build
  527. * @throws BuildException on error
  528. */
  529. @Override
  530. public void execute() throws BuildException {
  531. if (doubleFilePass) {
  532. skipWriting = true;
  533. executeMain();
  534. skipWriting = false;
  535. executeMain();
  536. } else {
  537. executeMain();
  538. }
  539. }
  540. /**
  541. * Get the value of the updatedFile attribute.
  542. * This should only be called after executeMain has been
  543. * called.
  544. * @return true if executeMain has written to the zip file.
  545. */
  546. protected boolean hasUpdatedFile() {
  547. return updatedFile;
  548. }
  549. /**
  550. * Build the zip file.
  551. * This is called twice if doubleFilePass is true.
  552. * @throws BuildException on error
  553. */
  554. public void executeMain() throws BuildException {
  555. checkAttributesAndElements();
  556. // Renamed version of original file, if it exists
  557. File renamedFile = null;
  558. addingNewFiles = true;
  559. processDoUpdate();
  560. processGroupFilesets();
  561. // collect filesets to pass them to getResourcesToAdd
  562. final Vector<ResourceCollection> vfss = new Vector<ResourceCollection>();
  563. if (baseDir != null) {
  564. final FileSet fs = (FileSet) getImplicitFileSet().clone();
  565. fs.setDir(baseDir);
  566. vfss.addElement(fs);
  567. }
  568. final int size = resources.size();
  569. for (int i = 0; i < size; i++) {
  570. final ResourceCollection rc = resources.elementAt(i);
  571. vfss.addElement(rc);
  572. }
  573. final ResourceCollection[] fss = new ResourceCollection[vfss.size()];
  574. vfss.copyInto(fss);
  575. boolean success = false;
  576. try {
  577. // can also handle empty archives
  578. final ArchiveState state = getResourcesToAdd(fss, zipFile, false);
  579. // quick exit if the target is up to date
  580. if (!state.isOutOfDate()) {
  581. return;
  582. }
  583. final File parent = zipFile.getParentFile();
  584. if (parent != null && !parent.isDirectory()
  585. && !(parent.mkdirs() || parent.isDirectory())) {
  586. throw new BuildException("Failed to create missing parent"
  587. + " directory for " + zipFile);
  588. }
  589. updatedFile = true;
  590. if (!zipFile.exists() && state.isWithoutAnyResources()) {
  591. createEmptyZip(zipFile);
  592. return;
  593. }
  594. final Resource[][] addThem = state.getResourcesToAdd();
  595. if (doUpdate) {
  596. renamedFile = renameFile();
  597. }
  598. final String action = doUpdate ? "Updating " : "Building ";
  599. if (!skipWriting) {
  600. log(action + archiveType + ": " + zipFile.getAbsolutePath());
  601. }
  602. ZipOutputStream zOut = null;
  603. try {
  604. if (!skipWriting) {
  605. zOut = new ZipOutputStream(zipFile);
  606. zOut.setEncoding(encoding);
  607. zOut.setUseLanguageEncodingFlag(useLanguageEncodingFlag);
  608. zOut.setCreateUnicodeExtraFields(createUnicodeExtraFields.
  609. getPolicy());
  610. zOut.setFallbackToUTF8(fallBackToUTF8);
  611. zOut.setMethod(doCompress
  612. ? ZipOutputStream.DEFLATED : ZipOutputStream.STORED);
  613. zOut.setLevel(level);
  614. zOut.setUseZip64(zip64Mode.getMode());
  615. }
  616. initZipOutputStream(zOut);
  617. // Add the explicit resource collections to the archive.
  618. for (int i = 0; i < fss.length; i++) {
  619. if (addThem[i].length != 0) {
  620. addResources(fss[i], addThem[i], zOut);
  621. }
  622. }
  623. if (doUpdate) {
  624. addingNewFiles = false;
  625. final ZipFileSet oldFiles = new ZipFileSet();
  626. oldFiles.setProject(getProject());
  627. oldFiles.setSrc(renamedFile);
  628. oldFiles.setDefaultexcludes(false);
  629. final int addSize = addedFiles.size();
  630. for (int i = 0; i < addSize; i++) {
  631. final PatternSet.NameEntry ne = oldFiles.createExclude();
  632. ne.setName(addedFiles.elementAt(i));
  633. }
  634. final DirectoryScanner ds =
  635. oldFiles.getDirectoryScanner(getProject());
  636. ((ZipScanner) ds).setEncoding(encoding);
  637. final String[] f = ds.getIncludedFiles();
  638. Resource[] r = new Resource[f.length];
  639. for (int i = 0; i < f.length; i++) {
  640. r[i] = ds.getResource(f[i]);
  641. }
  642. if (!doFilesonly) {
  643. final String[] d = ds.getIncludedDirectories();
  644. final Resource[] dr = new Resource[d.length];
  645. for (int i = 0; i < d.length; i++) {
  646. dr[i] = ds.getResource(d[i]);
  647. }
  648. final Resource[] tmp = r;
  649. r = new Resource[tmp.length + dr.length];
  650. System.arraycopy(dr, 0, r, 0, dr.length);
  651. System.arraycopy(tmp, 0, r, dr.length, tmp.length);
  652. }
  653. addResources(oldFiles, r, zOut);
  654. }
  655. if (zOut != null) {
  656. zOut.setComment(comment);
  657. }
  658. finalizeZipOutputStream(zOut);
  659. // If we've been successful on an update, delete the
  660. // temporary file
  661. if (doUpdate) {
  662. if (!renamedFile.delete()) {
  663. log ("Warning: unable to delete temporary file "
  664. + renamedFile.getName(), Project.MSG_WARN);
  665. }
  666. }
  667. success = true;
  668. } finally {
  669. // Close the output stream.
  670. closeZout(zOut, success);
  671. }
  672. } catch (final IOException ioe) {
  673. String msg = "Problem creating " + archiveType + ": "
  674. + ioe.getMessage();
  675. // delete a bogus ZIP file (but only if it's not the original one)
  676. if ((!doUpdate || renamedFile != null) && !zipFile.delete()) {
  677. msg += " (and the archive is probably corrupt but I could not "
  678. + "delete it)";
  679. }
  680. if (doUpdate && renamedFile != null) {
  681. try {
  682. FILE_UTILS.rename(renamedFile, zipFile);
  683. } catch (final IOException e) {
  684. msg += " (and I couldn't rename the temporary file "
  685. + renamedFile.getName() + " back)";
  686. }
  687. }
  688. throw new BuildException(msg, ioe, getLocation());
  689. } finally {
  690. cleanUp();
  691. }
  692. }
  693. /** rename the zip file. */
  694. private File renameFile() {
  695. final File renamedFile = FILE_UTILS.createTempFile(
  696. "zip", ".tmp", zipFile.getParentFile(), true, false);
  697. try {
  698. FILE_UTILS.rename(zipFile, renamedFile);
  699. } catch (final SecurityException e) {
  700. throw new BuildException(
  701. "Not allowed to rename old file ("
  702. + zipFile.getAbsolutePath()
  703. + ") to temporary file");
  704. } catch (final IOException e) {
  705. throw new BuildException(
  706. "Unable to rename old file ("
  707. + zipFile.getAbsolutePath()
  708. + ") to temporary file");
  709. }
  710. return renamedFile;
  711. }
  712. /** Close zout */
  713. private void closeZout(final ZipOutputStream zOut, final boolean success)
  714. throws IOException {
  715. if (zOut == null) {
  716. return;
  717. }
  718. try {
  719. zOut.close();
  720. } catch (final IOException ex) {
  721. // If we're in this finally clause because of an
  722. // exception, we don't really care if there's an
  723. // exception when closing the stream. E.g. if it
  724. // throws "ZIP file must have at least one entry",
  725. // because an exception happened before we added
  726. // any files, then we must swallow this
  727. // exception. Otherwise, the error that's reported
  728. // will be the close() error, which is not the
  729. // real cause of the problem.
  730. if (success) {
  731. throw ex;
  732. }
  733. }
  734. }
  735. /** Check the attributes and elements */
  736. private void checkAttributesAndElements() {
  737. if (baseDir == null && resources.size() == 0
  738. && groupfilesets.size() == 0 && "zip".equals(archiveType)) {
  739. throw new BuildException("basedir attribute must be set, "
  740. + "or at least one "
  741. + "resource collection must be given!");
  742. }
  743. if (zipFile == null) {
  744. throw new BuildException("You must specify the "
  745. + archiveType + " file to create!");
  746. }
  747. if (zipFile.exists() && !zipFile.isFile()) {
  748. throw new BuildException(zipFile + " is not a file.");
  749. }
  750. if (zipFile.exists() && !zipFile.canWrite()) {
  751. throw new BuildException(zipFile + " is read-only.");
  752. }
  753. }
  754. /** Process doupdate */
  755. private void processDoUpdate() {
  756. // Whether or not an actual update is required -
  757. // we don't need to update if the original file doesn't exist
  758. if (doUpdate && !zipFile.exists()) {
  759. doUpdate = false;
  760. logWhenWriting("ignoring update attribute as " + archiveType
  761. + " doesn't exist.", Project.MSG_DEBUG);
  762. }
  763. }
  764. /** Process groupfilesets */
  765. private void processGroupFilesets() {
  766. // Add the files found in groupfileset to fileset
  767. final int size = groupfilesets.size();
  768. for (int i = 0; i < size; i++) {
  769. logWhenWriting("Processing groupfileset ", Project.MSG_VERBOSE);
  770. final FileSet fs = groupfilesets.elementAt(i);
  771. final FileScanner scanner = fs.getDirectoryScanner(getProject());
  772. final String[] files = scanner.getIncludedFiles();
  773. final File basedir = scanner.getBasedir();
  774. for (int j = 0; j < files.length; j++) {
  775. logWhenWriting("Adding file " + files[j] + " to fileset",
  776. Project.MSG_VERBOSE);
  777. final ZipFileSet zf = new ZipFileSet();
  778. zf.setProject(getProject());
  779. zf.setSrc(new File(basedir, files[j]));
  780. add(zf);
  781. filesetsFromGroupfilesets.addElement(zf);
  782. }
  783. }
  784. }
  785. /**
  786. * Indicates if the task is adding new files into the archive as opposed to
  787. * copying back unchanged files from the backup copy
  788. * @return true if adding new files
  789. */
  790. protected final boolean isAddingNewFiles() {
  791. return addingNewFiles;
  792. }
  793. /**
  794. * Add the given resources.
  795. *
  796. * @param fileset may give additional information like fullpath or
  797. * permissions.
  798. * @param resources the resources to add
  799. * @param zOut the stream to write to
  800. * @throws IOException on error
  801. *
  802. * @since Ant 1.5.2
  803. */
  804. protected final void addResources(final FileSet fileset, final Resource[] resources,
  805. final ZipOutputStream zOut)
  806. throws IOException {
  807. String prefix = "";
  808. String fullpath = "";
  809. int dirMode = ArchiveFileSet.DEFAULT_DIR_MODE;
  810. int fileMode = ArchiveFileSet.DEFAULT_FILE_MODE;
  811. ArchiveFileSet zfs = null;
  812. if (fileset instanceof ArchiveFileSet) {
  813. zfs = (ArchiveFileSet) fileset;
  814. prefix = zfs.getPrefix(getProject());
  815. fullpath = zfs.getFullpath(getProject());
  816. dirMode = zfs.getDirMode(getProject());
  817. fileMode = zfs.getFileMode(getProject());
  818. }
  819. if (prefix.length() > 0 && fullpath.length() > 0) {
  820. throw new BuildException("Both prefix and fullpath attributes must"
  821. + " not be set on the same fileset.");
  822. }
  823. if (resources.length != 1 && fullpath.length() > 0) {
  824. throw new BuildException("fullpath attribute may only be specified"
  825. + " for filesets that specify a single"
  826. + " file.");
  827. }
  828. if (prefix.length() > 0) {
  829. if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
  830. prefix += "/";
  831. }
  832. addParentDirs(null, prefix, zOut, "", dirMode);
  833. }
  834. ZipFile zf = null;
  835. try {
  836. boolean dealingWithFiles = false;
  837. File base = null;
  838. if (zfs == null || zfs.getSrc(getProject()) == null) {
  839. dealingWithFiles = true;
  840. base = fileset.getDir(getProject());
  841. } else if (zfs instanceof ZipFileSet) {
  842. zf = new ZipFile(zfs.getSrc(getProject()), encoding);
  843. }
  844. for (int i = 0; i < resources.length; i++) {
  845. String name = null;
  846. if (fullpath.length() > 0) {
  847. name = fullpath;
  848. } else {
  849. name = resources[i].getName();
  850. }
  851. name = name.replace(File.separatorChar, '/');
  852. if ("".equals(name)) {
  853. continue;
  854. }
  855. if (resources[i].isDirectory()) {
  856. if (doFilesonly) {
  857. continue;
  858. }
  859. final int thisDirMode = zfs != null && zfs.hasDirModeBeenSet()
  860. ? dirMode : getUnixMode(resources[i], zf, dirMode);
  861. addDirectoryResource(resources[i], name, prefix,
  862. base, zOut,
  863. dirMode, thisDirMode);
  864. } else { // !isDirectory
  865. addParentDirs(base, name, zOut, prefix, dirMode);
  866. if (dealingWithFiles) {
  867. final File f = FILE_UTILS.resolveFile(base,
  868. resources[i].getName());
  869. zipFile(f, zOut, prefix + name, fileMode);
  870. } else {
  871. final int thisFileMode =
  872. zfs != null && zfs.hasFileModeBeenSet()
  873. ? fileMode : getUnixMode(resources[i], zf,
  874. fileMode);
  875. addResource(resources[i], name, prefix,
  876. zOut, thisFileMode, zf,
  877. zfs == null
  878. ? null : zfs.getSrc(getProject()));
  879. }
  880. }
  881. }
  882. } finally {
  883. if (zf != null) {
  884. zf.close();
  885. }
  886. }
  887. }
  888. /**
  889. * Add a directory entry to the archive using a specified
  890. * Unix-mode and the default mode for its parent directories (if
  891. * necessary).
  892. */
  893. private void addDirectoryResource(final Resource r, String name, final String prefix,
  894. final File base, final ZipOutputStream zOut,
  895. final int defaultDirMode, final int thisDirMode)
  896. throws IOException {
  897. if (!name.endsWith("/")) {
  898. name = name + "/";
  899. }
  900. final int nextToLastSlash = name.lastIndexOf("/", name.length() - 2);
  901. if (nextToLastSlash != -1) {
  902. addParentDirs(base, name.substring(0, nextToLastSlash + 1),
  903. zOut, prefix, defaultDirMode);
  904. }
  905. zipDir(r, zOut, prefix + name, thisDirMode,
  906. r instanceof ZipResource
  907. ? ((ZipResource) r).getExtraFields() : null);
  908. }
  909. /**
  910. * Determine a Resource's Unix mode or return the given default
  911. * value if not available.
  912. */
  913. private int getUnixMode(final Resource r, final ZipFile zf, final int defaultMode) {
  914. int unixMode = defaultMode;
  915. if (zf != null) {
  916. final ZipEntry ze = zf.getEntry(r.getName());
  917. unixMode = ze.getUnixMode();
  918. if ((unixMode == 0 || unixMode == UnixStat.DIR_FLAG)
  919. && !preserve0Permissions) {
  920. unixMode = defaultMode;
  921. }
  922. } else if (r instanceof ArchiveResource) {
  923. unixMode = ((ArchiveResource) r).getMode();
  924. }
  925. return unixMode;
  926. }
  927. /**
  928. * Add a file entry.
  929. */
  930. private void addResource(final Resource r, final String name, final String prefix,
  931. final ZipOutputStream zOut, final int mode,
  932. final ZipFile zf, final File fromArchive)
  933. throws IOException {
  934. if (zf != null) {
  935. final ZipEntry ze = zf.getEntry(r.getName());
  936. if (ze != null) {
  937. final boolean oldCompress = doCompress;
  938. if (keepCompression) {
  939. doCompress = (ze.getMethod() == ZipEntry.DEFLATED);
  940. }
  941. InputStream is = null;
  942. try {
  943. is = zf.getInputStream(ze);
  944. zipFile(is, zOut, prefix + name, ze.getTime(),
  945. fromArchive, mode, ze.getExtraFields(true));
  946. } finally {
  947. doCompress = oldCompress;
  948. FileUtils.close(is);
  949. }
  950. }
  951. } else {
  952. InputStream is = null;
  953. try {
  954. is = r.getInputStream();
  955. zipFile(is, zOut, prefix + name, r.getLastModified(),
  956. fromArchive, mode, r instanceof ZipResource
  957. ? ((ZipResource) r).getExtraFields() : null);
  958. } finally {
  959. FileUtils.close(is);
  960. }
  961. }
  962. }
  963. /**
  964. * Add the given resources.
  965. *
  966. * @param rc may give additional information like fullpath or
  967. * permissions.
  968. * @param resources the resources to add
  969. * @param zOut the stream to write to
  970. * @throws IOException on error
  971. *
  972. * @since Ant 1.7
  973. */
  974. protected final void addResources(final ResourceCollection rc,
  975. final Resource[] resources,
  976. final ZipOutputStream zOut)
  977. throws IOException {
  978. if (rc instanceof FileSet) {
  979. addResources((FileSet) rc, resources, zOut);
  980. return;
  981. }
  982. for (int i = 0; i < resources.length; i++) {
  983. final Resource resource = resources[i];
  984. String name = resource.getName();
  985. if (name == null) {
  986. continue;
  987. }
  988. name = name.replace(File.separatorChar, '/');
  989. if ("".equals(name)) {
  990. continue;
  991. }
  992. if (resource.isDirectory() && doFilesonly) {
  993. continue;
  994. }
  995. File base = null;
  996. final FileProvider fp = resource.as(FileProvider.class);
  997. if (fp != null) {
  998. base = ResourceUtils.asFileResource(fp).getBaseDir();
  999. }
  1000. if (resource.isDirectory()) {
  1001. addDirectoryResource(resource, name, "", base, zOut,
  1002. ArchiveFileSet.DEFAULT_DIR_MODE,
  1003. ArchiveFileSet.DEFAULT_DIR_MODE);
  1004. } else {
  1005. addParentDirs(base, name, zOut, "",
  1006. ArchiveFileSet.DEFAULT_DIR_MODE);
  1007. if (fp != null) {
  1008. final File f = (fp).getFile();
  1009. zipFile(f, zOut, name, ArchiveFileSet.DEFAULT_FILE_MODE);
  1010. } else {
  1011. addResource(resource, name, "", zOut,
  1012. ArchiveFileSet.DEFAULT_FILE_MODE,
  1013. null, null);
  1014. }
  1015. }
  1016. }
  1017. }
  1018. /**
  1019. * method for subclasses to override
  1020. * @param zOut the zip output stream
  1021. * @throws IOException on output error
  1022. * @throws BuildException on other errors
  1023. */
  1024. protected void initZipOutputStream(final ZipOutputStream zOut)
  1025. throws IOException, BuildException {
  1026. }
  1027. /**
  1028. * method for subclasses to override
  1029. * @param zOut the zip output stream
  1030. * @throws IOException on output error
  1031. * @throws BuildException on other errors
  1032. */
  1033. protected void finalizeZipOutputStream(final ZipOutputStream zOut)
  1034. throws IOException, BuildException {
  1035. }
  1036. /**
  1037. * Create an empty zip file
  1038. * @param zipFile the zip file
  1039. * @return true for historic reasons
  1040. * @throws BuildException on error
  1041. */
  1042. protected boolean createEmptyZip(final File zipFile) throws BuildException {
  1043. // In this case using java.util.zip will not work
  1044. // because it does not permit a zero-entry archive.
  1045. // Must create it manually.
  1046. if (!skipWriting) {
  1047. log("Note: creating empty " + archiveType + " archive " + zipFile,
  1048. Project.MSG_INFO);
  1049. }
  1050. OutputStream os = null;
  1051. try {
  1052. os = Files.newOutputStream(zipFile.toPath());
  1053. // CheckStyle:MagicNumber OFF
  1054. // Cf. PKZIP specification.
  1055. final byte[] empty = new byte[22];
  1056. empty[0] = 80; // P
  1057. empty[1] = 75; // K
  1058. empty[2] = 5;
  1059. empty[3] = 6;
  1060. // remainder zeros
  1061. // CheckStyle:MagicNumber ON
  1062. os.write(empty);
  1063. } catch (final IOException ioe) {
  1064. throw new BuildException("Could not create empty ZIP archive "
  1065. + "(" + ioe.getMessage() + ")", ioe,
  1066. getLocation());
  1067. } finally {
  1068. FileUtils.close(os);
  1069. }
  1070. return true;
  1071. }
  1072. /**
  1073. * @since Ant 1.5.2
  1074. */
  1075. private synchronized ZipScanner getZipScanner() {
  1076. if (zs == null) {
  1077. zs = new ZipScanner();
  1078. zs.setEncoding(encoding);
  1079. zs.setSrc(zipFile);
  1080. }
  1081. return zs;
  1082. }
  1083. /**
  1084. * Collect the resources that are newer than the corresponding
  1085. * entries (or missing) in the original archive.
  1086. *
  1087. * <p>If we are going to recreate the archive instead of updating
  1088. * it, all resources should be considered as new, if a single one
  1089. * is. Because of this, subclasses overriding this method must
  1090. * call <code>super.getResourcesToAdd</code> and indicate with the
  1091. * third arg if they already know that the archive is
  1092. * out-of-date.</p>
  1093. *
  1094. * <p>This method first delegates to getNonFileSetResourcesToAdd
  1095. * and then invokes the FileSet-arg version. All this to keep
  1096. * backwards compatibility for subclasses that don't know how to
  1097. * deal with non-FileSet ResourceCollections.</p>
  1098. *
  1099. * @param rcs The resource collections to grab resources from
  1100. * @param zipFile intended archive file (may or may not exist)
  1101. * @param needsUpdate whether we already know that the archive is
  1102. * out-of-date. Subclasses overriding this method are supposed to
  1103. * set this value correctly in their call to
  1104. * <code>super.getResourcesToAdd</code>.
  1105. * @return an array of resources to add for each fileset passed in as well
  1106. * as a flag that indicates whether the archive is uptodate.
  1107. *
  1108. * @exception BuildException if it likes
  1109. * @since Ant 1.7
  1110. */
  1111. protected ArchiveState getResourcesToAdd(final ResourceCollection[] rcs,
  1112. final File zipFile,
  1113. final boolean needsUpdate)
  1114. throws BuildException {
  1115. final ArrayList<ResourceCollection> filesets = new ArrayList<ResourceCollection>();
  1116. final ArrayList<ResourceCollection> rest = new ArrayList<ResourceCollection>();
  1117. for (int i = 0; i < rcs.length; i++) {
  1118. if (rcs[i] instanceof FileSet) {
  1119. filesets.add(rcs[i]);
  1120. } else {
  1121. rest.add(rcs[i]);
  1122. }
  1123. }
  1124. final ResourceCollection[] rc =
  1125. rest.toArray(new ResourceCollection[rest.size()]);
  1126. ArchiveState as = getNonFileSetResourcesToAdd(rc, zipFile,
  1127. needsUpdate);
  1128. final FileSet[] fs = filesets.toArray(new FileSet[filesets
  1129. .size()]);
  1130. final ArchiveState as2 = getResourcesToAdd(fs, zipFile, as.isOutOfDate());
  1131. if (!as.isOutOfDate() && as2.isOutOfDate()) {
  1132. /*
  1133. * Bad luck.
  1134. *
  1135. * There are resources in the filesets that make the
  1136. * archive out of date, but not in the non-fileset
  1137. * resources. We need to rescan the non-FileSets to grab
  1138. * all of them now.
  1139. */
  1140. as = getNonFileSetResourcesToAdd(rc, zipFile, true);
  1141. }
  1142. final Resource[][] toAdd = new Resource[rcs.length][];
  1143. int fsIndex = 0;
  1144. int restIndex = 0;
  1145. for (int i = 0; i < rcs.length; i++) {
  1146. if (rcs[i] instanceof FileSet) {
  1147. toAdd[i] = as2.getResourcesToAdd()[fsIndex++];
  1148. } else {
  1149. toAdd[i] = as.getResourcesToAdd()[restIndex++];
  1150. }
  1151. }
  1152. return new ArchiveState(as2.isOutOfDate(), toAdd);
  1153. }
  1154. /*
  1155. * This is yet another hacky construct to extend the FileSet[]
  1156. * getResourcesToAdd method so we can pass the information whether
  1157. * non-fileset resources have been available to it without having
  1158. * to move the withEmpty behavior checks (since either would break
  1159. * subclasses in several ways).
  1160. */
  1161. private static final ThreadLocal<Boolean> HAVE_NON_FILE_SET_RESOURCES_TO_ADD = new ThreadLocal<Boolean>() {
  1162. @Override
  1163. protected Boolean initialValue() {
  1164. return Boolean.FALSE;
  1165. }
  1166. };
  1167. /**
  1168. * Collect the resources that are newer than the corresponding
  1169. * entries (or missing) in the original archive.
  1170. *
  1171. * <p>If we are going to recreate the archive instead of updating
  1172. * it, all resources should be considered as new, if a single one
  1173. * is. Because of this, subclasses overriding this method must
  1174. * call <code>super.getResourcesToAdd</code> and indicate with the
  1175. * third arg if they already know that the archive is
  1176. * out-of-date.</p>
  1177. *
  1178. * @param filesets The filesets to grab resources from
  1179. * @param zipFile intended archive file (may or may not exist)
  1180. * @param needsUpdate whether we already know that the archive is
  1181. * out-of-date. Subclasses overriding this method are supposed to
  1182. * set this value correctly in their call to
  1183. * <code>super.getResourcesToAdd</code>.
  1184. * @return an array of resources to add for each fileset passed in as well
  1185. * as a flag that indicates whether the archive is uptodate.
  1186. *
  1187. * @exception BuildException if it likes
  1188. */
  1189. protected ArchiveState getResourcesToAdd(final FileSet[] filesets,
  1190. final File zipFile,
  1191. boolean needsUpdate)
  1192. throws BuildException {
  1193. final Resource[][] initialResources = grabResources(filesets);
  1194. if (isEmpty(initialResources)) {
  1195. if (Boolean.FALSE.equals(HAVE_NON_FILE_SET_RESOURCES_TO_ADD.get())) {
  1196. if (needsUpdate && doUpdate) {
  1197. /*
  1198. * This is a rather hairy case.
  1199. *
  1200. * One of our subclasses knows that we need to
  1201. * update the archive, but at the same time, there
  1202. * are no resources known to us that would need to
  1203. * be added. Only the subclass seems to know
  1204. * what's going on.
  1205. *
  1206. * This happens if <jar> detects that the manifest
  1207. * has changed, for example. The manifest is not
  1208. * part of any resources because of our support
  1209. * for inline <manifest>s.
  1210. *
  1211. * If we invoke createEmptyZip like Ant 1.5.2 did,
  1212. * we'll loose all stuff that has been in the
  1213. * original archive (bugzilla report 17780).
  1214. */
  1215. return new ArchiveState(true, initialResources);
  1216. }
  1217. if (emptyBehavior.equals("skip")) {
  1218. if (doUpdate) {
  1219. logWhenWriting(archiveType + " archive " + zipFile
  1220. + " not updated because no new files were"
  1221. + " included.", Project.MSG_VERBOSE);
  1222. } else {
  1223. logWhenWriting("Warning: skipping " + archiveType
  1224. + " archive " + zipFile
  1225. + " because no files were included.",
  1226. Project.MSG_WARN);
  1227. }
  1228. } else if (emptyBehavior.equals("fail")) {
  1229. throw new BuildException("Cannot create " + archiveType
  1230. + " archive " + zipFile
  1231. + ": no files were included.",
  1232. getLocation());
  1233. } else {
  1234. // Create.
  1235. if (!zipFile.exists()) {
  1236. needsUpdate = true;
  1237. }
  1238. }
  1239. }
  1240. // either there are non-fileset resources or we
  1241. // (re-)create the archive anyway
  1242. return new ArchiveState(needsUpdate, initialResources);
  1243. }
  1244. // initialResources is not empty
  1245. if (!zipFile.exists()) {
  1246. return new ArchiveState(true, initialResources);
  1247. }
  1248. if (needsUpdate && !doUpdate) {
  1249. // we are recreating the archive, need all resources
  1250. return new ArchiveState(true, initialResources);
  1251. }
  1252. final Resource[][] newerResources = new Resource[filesets.length][];
  1253. for (int i = 0; i < filesets.length; i++) {
  1254. if (!(fileset instanceof ZipFileSet)
  1255. || ((ZipFileSet) fileset).getSrc(getProject()) == null) {
  1256. final File base = filesets[i].getDir(getProject());
  1257. for (int j = 0; j < initialResources[i].length; j++) {
  1258. final File resourceAsFile =
  1259. FILE_UTILS.resolveFile(base,
  1260. initialResources[i][j].getName());
  1261. if (resourceAsFile.equals(zipFile)) {
  1262. throw new BuildException("A zip file cannot include "
  1263. + "itself", getLocation());
  1264. }
  1265. }
  1266. }
  1267. }
  1268. for (int i = 0; i < filesets.length; i++) {
  1269. if (initialResources[i].length == 0) {
  1270. newerResources[i] = new Resource[] {};
  1271. continue;
  1272. }
  1273. FileNameMapper myMapper = new IdentityMapper();
  1274. if (filesets[i] instanceof ZipFileSet) {
  1275. final ZipFileSet zfs = (ZipFileSet) filesets[i];
  1276. if (zfs.getFullpath(getProject()) != null
  1277. && !zfs.getFullpath(getProject()).equals("")) {
  1278. // in this case all files from origin map to
  1279. // the fullPath attribute of the zipfileset at
  1280. // destination
  1281. final MergingMapper fm = new MergingMapper();
  1282. fm.setTo(zfs.getFullpath(getProject()));
  1283. myMapper = fm;
  1284. } else if (zfs.getPrefix(getProject()) != null
  1285. && !zfs.getPrefix(getProject()).equals("")) {
  1286. final GlobPatternMapper gm = new GlobPatternMapper();
  1287. gm.setFrom("*");
  1288. String prefix = zfs.getPrefix(getProject());
  1289. if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
  1290. prefix += "/";
  1291. }
  1292. gm.setTo(prefix + "*");
  1293. myMapper = gm;
  1294. }
  1295. }
  1296. newerResources[i] = selectOutOfDateResources(initialResources[i],
  1297. myMapper);
  1298. needsUpdate = needsUpdate || (newerResources[i].length > 0);
  1299. if (needsUpdate && !doUpdate) {
  1300. // we will return initialResources anyway, no reason
  1301. // to scan further.
  1302. break;
  1303. }
  1304. }
  1305. if (needsUpdate && !doUpdate) {
  1306. // we are recreating the archive, need all resources
  1307. return new ArchiveState(true, initialResources);
  1308. }
  1309. return new ArchiveState(needsUpdate, newerResources);
  1310. }
  1311. /**
  1312. * Collect the resources that are newer than the corresponding
  1313. * entries (or missing) in the original archive.
  1314. *
  1315. * <p>If we are going to recreate the archive instead of updating
  1316. * it, all resources should be considered as new, if a single one
  1317. * is. Because of this, subclasses overriding this method must
  1318. * call <code>super.getResourcesToAdd</code> and indicate with the
  1319. * third arg if they already know that the archive is
  1320. * out-of-date.</p>
  1321. *
  1322. * @param rcs The filesets to grab resources from
  1323. * @param zipFile intended archive file (may or may not exist)
  1324. * @param needsUpdate whether we already know that the archive is
  1325. * out-of-date. Subclasses overriding this method are supposed to
  1326. * set this value correctly in their call to
  1327. * <code>super.getResourcesToAdd</code>.
  1328. * @return an array of resources to add for each fileset passed in as well
  1329. * as a flag that indicates whether the archive is uptodate.
  1330. *
  1331. * @exception BuildException if it likes
  1332. */
  1333. protected ArchiveState getNonFileSetResourcesToAdd(final ResourceCollection[] rcs,
  1334. final File zipFile,
  1335. boolean needsUpdate)
  1336. throws BuildException {
  1337. /*
  1338. * Backwards compatibility forces us to repeat the logic of
  1339. * getResourcesToAdd(FileSet[], ...) here once again.
  1340. */
  1341. final Resource[][] initialResources = grabNonFileSetResources(rcs);
  1342. final boolean empty = isEmpty(initialResources);
  1343. HAVE_NON_FILE_SET_RESOURCES_TO_ADD.set(Boolean.valueOf(!empty));
  1344. if (empty) {
  1345. // no emptyBehavior handling since the FileSet version
  1346. // will take care of it.
  1347. return new ArchiveState(needsUpdate, initialResources);
  1348. }
  1349. // initialResources is not empty
  1350. if (!zipFile.exists()) {
  1351. return new ArchiveState(true, initialResources);
  1352. }
  1353. if (needsUpdate && !doUpdate) {
  1354. // we are recreating the archive, need all resources
  1355. return new ArchiveState(true, initialResources);
  1356. }
  1357. final Resource[][] newerResources = new Resource[rcs.length][];
  1358. for (int i = 0; i < rcs.length; i++) {
  1359. if (initialResources[i].length == 0) {
  1360. newerResources[i] = new Resource[] {};
  1361. continue;
  1362. }
  1363. for (int j = 0; j < initialResources[i].length; j++) {
  1364. final FileProvider fp =
  1365. initialResources[i][j].as(FileProvider.class);
  1366. if (fp != null && zipFile.equals(fp.getFile())) {
  1367. throw new BuildException("A zip file cannot include "
  1368. + "itself", getLocation());
  1369. }
  1370. }
  1371. newerResources[i] = selectOutOfDateResources(initialResources[i],
  1372. new IdentityMapper());
  1373. needsUpdate = needsUpdate || (newerResources[i].length > 0);
  1374. if (needsUpdate && !doUpdate) {
  1375. // we will return initialResources anyway, no reason
  1376. // to scan further.
  1377. break;
  1378. }
  1379. }
  1380. if (needsUpdate && !doUpdate) {
  1381. // we are recreating the archive, need all resources
  1382. return new ArchiveState(true, initialResources);
  1383. }
  1384. return new ArchiveState(needsUpdate, newerResources);
  1385. }
  1386. private Resource[] selectOutOfDateResources(final Resource[] initial,
  1387. final FileNameMapper mapper) {
  1388. final Resource[] rs = selectFileResources(initial);
  1389. Resource[] result =
  1390. ResourceUtils.selectOutOfDateSources(this, rs, mapper,
  1391. getZipScanner(),
  1392. ZIP_FILE_TIMESTAMP_GRANULARITY);
  1393. if (!doFilesonly) {
  1394. final Union u = new Union();
  1395. u.addAll(Arrays.asList(selectDirectoryResources(initial)));
  1396. final ResourceCollection rc =
  1397. ResourceUtils.selectSources(this, u, mapper,
  1398. getZipScanner(),
  1399. MISSING_DIR_PROVIDER);
  1400. if (rc.size() > 0) {
  1401. final ArrayList<Resource> newer = new ArrayList<Resource>();
  1402. newer.addAll(Arrays.asList(((Union) rc).listResources()));
  1403. newer.addAll(Arrays.asList(result));
  1404. result = newer.toArray(result);
  1405. }
  1406. }
  1407. return result;
  1408. }
  1409. /**
  1410. * Fetch all included and not excluded resources from the sets.
  1411. *
  1412. * <p>Included directories will precede included files.</p>
  1413. * @param filesets an array of filesets
  1414. * @return the resources included
  1415. * @since Ant 1.5.2
  1416. */
  1417. protected Resource[][] grabResources(final FileSet[] filesets) {
  1418. final Resource[][] result = new Resource[filesets.length][];
  1419. for (int i = 0; i < filesets.length; i++) {
  1420. boolean skipEmptyNames = true;
  1421. if (filesets[i] instanceof ZipFileSet) {
  1422. final ZipFileSet zfs = (ZipFileSet) filesets[i];
  1423. skipEmptyNames = zfs.getPrefix(getProject()).equals("")
  1424. && zfs.getFullpath(getProject()).equals("");
  1425. }
  1426. final DirectoryScanner rs =
  1427. filesets[i].getDirectoryScanner(getProject());
  1428. if (rs instanceof ZipScanner) {
  1429. ((ZipScanner) rs).setEncoding(encoding);
  1430. }
  1431. final Vector<Resource> resources = new Vector<Resource>();
  1432. if (!doFilesonly) {
  1433. final String[] directories = rs.getIncludedDirectories();
  1434. for (int j = 0; j < directories.length; j++) {
  1435. if (!"".equals(directories[j]) || !skipEmptyNames) {
  1436. resources.addElement(rs.getResource(directories[j]));
  1437. }
  1438. }
  1439. }
  1440. final String[] files = rs.getIncludedFiles();
  1441. for (int j = 0; j < files.length; j++) {
  1442. if (!"".equals(files[j]) || !skipEmptyNames) {
  1443. resources.addElement(rs.getResource(files[j]));
  1444. }
  1445. }
  1446. result[i] = new Resource[resources.size()];
  1447. resources.copyInto(result[i]);
  1448. }
  1449. return result;
  1450. }
  1451. /**
  1452. * Fetch all included and not excluded resources from the collections.
  1453. *
  1454. * <p>Included directories will precede included files.</p>
  1455. * @param rcs an array of resource collections
  1456. * @return the resources included
  1457. * @since Ant 1.7
  1458. */
  1459. protected Resource[][] grabNonFileSetResources(final ResourceCollection[] rcs) {
  1460. final Resource[][] result = new Resource[rcs.length][];
  1461. for (int i = 0; i < rcs.length; i++) {
  1462. final ArrayList<Resource> dirs = new ArrayList<Resource>();
  1463. final ArrayList<Resource> files = new ArrayList<Resource>();
  1464. for (final Resource r : rcs[i]) {
  1465. if (r.isExists()) {
  1466. if (r.isDirectory()) {
  1467. dirs.add(r);
  1468. } else {
  1469. files.add(r);
  1470. }
  1471. }
  1472. }
  1473. // make sure directories are in alpha-order - this also
  1474. // ensures parents come before their children
  1475. Collections.sort(dirs, new Comparator<Resource>() {
  1476. public int compare(final Resource r1, final Resource r2) {
  1477. return r1.getName().compareTo(r2.getName());
  1478. }
  1479. });
  1480. final ArrayList<Resource> rs = new ArrayList<Resource>(dirs);
  1481. rs.addAll(files);
  1482. result[i] = rs.toArray(new Resource[rs.size()]);
  1483. }
  1484. return result;
  1485. }
  1486. /**
  1487. * Add a directory to the zip stream.
  1488. * @param dir the directort to add to the archive
  1489. * @param zOut the stream to write to
  1490. * @param vPath the name this entry shall have in the archive
  1491. * @param mode the Unix permissions to set.
  1492. * @throws IOException on error
  1493. * @since Ant 1.5.2
  1494. */
  1495. protected void zipDir(final File dir, final ZipOutputStream zOut, final String vPath,
  1496. final int mode)
  1497. throws IOException {
  1498. zipDir(dir, zOut, vPath, mode, null);
  1499. }
  1500. /**
  1501. * Add a directory to the zip stream.
  1502. * @param dir the directory to add to the archive
  1503. * @param zOut the stream to write to
  1504. * @param vPath the name this entry shall have in the archive
  1505. * @param mode the Unix permissions to set.
  1506. * @param extra ZipExtraFields to add
  1507. * @throws IOException on error
  1508. * @since Ant 1.6.3
  1509. */
  1510. protected void zipDir(final File dir, final ZipOutputStream zOut, final String vPath,
  1511. final int mode, final ZipExtraField[] extra)
  1512. throws IOException {
  1513. zipDir(dir == null ? (Resource) null : new FileResource(dir),
  1514. zOut, vPath, mode, extra);
  1515. }
  1516. /**
  1517. * Add a directory to the zip stream.
  1518. * @param dir the directory to add to the archive
  1519. * @param zOut the stream to write to
  1520. * @param vPath the name this entry shall have in the archive
  1521. * @param mode the Unix permissions to set.
  1522. * @param extra ZipExtraFields to add
  1523. * @throws IOException on error
  1524. * @since Ant 1.8.0
  1525. */
  1526. protected void zipDir(final Resource dir, final ZipOutputStream zOut, final String vPath,
  1527. final int mode, final ZipExtraField[] extra)
  1528. throws IOException {
  1529. if (doFilesonly) {
  1530. logWhenWriting("skipping directory " + vPath
  1531. + " for file-only archive",
  1532. Project.MSG_VERBOSE);
  1533. return;
  1534. }
  1535. if (addedDirs.get(vPath) != null) {
  1536. // don't add directories we've already added.
  1537. // no warning if we try, it is harmless in and of itself
  1538. return;
  1539. }
  1540. logWhenWriting("adding directory " + vPath, Project.MSG_VERBOSE);
  1541. addedDirs.put(vPath, vPath);
  1542. if (!skipWriting) {
  1543. final ZipEntry ze = new ZipEntry (vPath);
  1544. // ZIPs store time with a granularity of 2 seconds, round up
  1545. final int millisToAdd = roundUp ? ROUNDUP_MILLIS : 0;
  1546. if (dir != null && dir.isExists()) {
  1547. ze.setTime(dir.getLastModified() + millisToAdd);
  1548. } else {
  1549. ze.setTime(System.currentTimeMillis() + millisToAdd);
  1550. }
  1551. ze.setSize (0);
  1552. ze.setMethod (ZipEntry.STORED);
  1553. // This is faintly ridiculous:
  1554. ze.setCrc (EMPTY_CRC);
  1555. ze.setUnixMode(mode);
  1556. if (extra != null) {
  1557. ze.setExtraFields(extra);
  1558. }
  1559. zOut.putNextEntry(ze);
  1560. }
  1561. }
  1562. /*
  1563. * This is a hacky construct to extend the zipFile method to
  1564. * support a new parameter (extra fields to preserve) without
  1565. * breaking subclasses that override the old method signature.
  1566. */
  1567. private static final ThreadLocal<ZipExtraField[]> CURRENT_ZIP_EXTRA = new ThreadLocal<ZipExtraField[]>();
  1568. /**
  1569. * Provides the extra fields for the zip entry currently being
  1570. * added to the archive - if any.
  1571. * @since Ant 1.8.0
  1572. */
  1573. protected final ZipExtraField[] getCurrentExtraFields() {
  1574. return CURRENT_ZIP_EXTRA.get();
  1575. }
  1576. /**
  1577. * Sets the extra fields for the zip entry currently being
  1578. * added to the archive - if any.
  1579. * @since Ant 1.8.0
  1580. */
  1581. protected final void setCurrentExtraFields(final ZipExtraField[] extra) {
  1582. CURRENT_ZIP_EXTRA.set(extra);
  1583. }
  1584. /**
  1585. * Adds a new entry to the archive, takes care of duplicates as well.
  1586. *
  1587. * @param in the stream to read data for the entry from. The
  1588. * caller of the method is responsible for closing the stream.
  1589. * @param zOut the stream to write to.
  1590. * @param vPath the name this entry shall have in the archive.
  1591. * @param lastModified last modification time for the entry.
  1592. * @param fromArchive the original archive we are copying this
  1593. * entry from, will be null if we are not copying from an archive.
  1594. * @param mode the Unix permissions to set.
  1595. *
  1596. * @since Ant 1.5.2
  1597. * @throws IOException on error
  1598. */
  1599. protected void zipFile(InputStream in, final ZipOutputStream zOut, final String vPath,
  1600. final long lastModified, final File fromArchive, final int mode)
  1601. throws IOException {
  1602. // fromArchive is used in subclasses overriding this method
  1603. if (entries.containsKey(vPath)) {
  1604. if (duplicate.equals("preserve")) {
  1605. logWhenWriting(vPath + " already added, skipping",
  1606. Project.MSG_INFO);
  1607. return;
  1608. } else if (duplicate.equals("fail")) {
  1609. throw new BuildException("Duplicate file " + vPath
  1610. + " was found and the duplicate "
  1611. + "attribute is 'fail'.");
  1612. } else {
  1613. // duplicate equal to add, so we continue
  1614. logWhenWriting("duplicate file " + vPath
  1615. + " found, adding.", Project.MSG_VERBOSE);
  1616. }
  1617. } else {
  1618. logWhenWriting("adding entry " + vPath, Project.MSG_VERBOSE);
  1619. }
  1620. entries.put(vPath, vPath);
  1621. if (!skipWriting) {
  1622. final ZipEntry ze = new ZipEntry(vPath);
  1623. ze.setTime(lastModified);
  1624. ze.setMethod(doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED);
  1625. /*
  1626. * ZipOutputStream.putNextEntry expects the ZipEntry to
  1627. * know its size and the CRC sum before you start writing
  1628. * the data when using STORED mode - unless it is seekable.
  1629. *
  1630. * This forces us to process the data twice.
  1631. */
  1632. if (!zOut.isSeekable() && !doCompress) {
  1633. long size = 0;
  1634. final CRC32 cal = new CRC32();
  1635. if (!in.markSupported()) {
  1636. // Store data into a byte[]
  1637. final ByteArrayOutputStream bos = new ByteArrayOutputStream();
  1638. final byte[] buffer = new byte[BUFFER_SIZE];
  1639. int count = 0;
  1640. do {
  1641. size += count;
  1642. cal.update(buffer, 0, count);
  1643. bos.write(buffer, 0, count);
  1644. count = in.read(buffer, 0, buffer.length);
  1645. } while (count != -1);
  1646. in = new ByteArrayInputStream(bos.toByteArray());
  1647. } else {
  1648. in.mark(Integer.MAX_VALUE);
  1649. final byte[] buffer = new byte[BUFFER_SIZE];
  1650. int count = 0;
  1651. do {
  1652. size += count;
  1653. cal.update(buffer, 0, count);
  1654. count = in.read(buffer, 0, buffer.length);
  1655. } while (count != -1);
  1656. in.reset();
  1657. }
  1658. ze.setSize(size);
  1659. ze.setCrc(cal.getValue());
  1660. }
  1661. ze.setUnixMode(mode);
  1662. final ZipExtraField[] extra = getCurrentExtraFields();
  1663. if (extra != null) {
  1664. ze.setExtraFields(extra);
  1665. }
  1666. zOut.putNextEntry(ze);
  1667. final byte[] buffer = new byte[BUFFER_SIZE];
  1668. int count = 0;
  1669. do {
  1670. if (count != 0) {
  1671. zOut.write(buffer, 0, count);
  1672. }
  1673. count = in.read(buffer, 0, buffer.length);
  1674. } while (count != -1);
  1675. }
  1676. addedFiles.addElement(vPath);
  1677. }
  1678. /**
  1679. * Adds a new entry to the archive, takes care of duplicates as well.
  1680. *
  1681. * @param in the stream to read data for the entry from. The
  1682. * caller of the method is responsible for closing the stream.
  1683. * @param zOut the stream to write to.
  1684. * @param vPath the name this entry shall have in the archive.
  1685. * @param lastModified last modification time for the entry.
  1686. * @param fromArchive the original archive we are copying this
  1687. * entry from, will be null if we are not copying from an archive.
  1688. * @param mode the Unix permissions to set.
  1689. * @param extra ZipExtraFields to add
  1690. *
  1691. * @since Ant 1.8.0
  1692. * @throws IOException on error
  1693. */
  1694. protected final void zipFile(final InputStream in, final ZipOutputStream zOut,
  1695. final String vPath, final long lastModified,
  1696. final File fromArchive, final int mode,
  1697. final ZipExtraField[] extra)
  1698. throws IOException {
  1699. try {
  1700. setCurrentExtraFields(extra);
  1701. zipFile(in, zOut, vPath, lastModified, fromArchive, mode);
  1702. } finally {
  1703. setCurrentExtraFields(null);
  1704. }
  1705. }
  1706. /**
  1707. * Method that gets called when adding from <code>java.io.File</code> instances.
  1708. *
  1709. * <p>This implementation delegates to the six-arg version.</p>
  1710. *
  1711. * @param file the file to add to the archive
  1712. * @param zOut the stream to write to
  1713. * @param vPath the name this entry shall have in the archive
  1714. * @param mode the Unix permissions to set.
  1715. * @throws IOException on error
  1716. *
  1717. * @since Ant 1.5.2
  1718. */
  1719. protected void zipFile(final File file, final ZipOutputStream zOut, final String vPath,
  1720. final int mode)
  1721. throws IOException {
  1722. if (file.equals(zipFile)) {
  1723. throw new BuildException("A zip file cannot include itself",
  1724. getLocation());
  1725. }
  1726. try (InputStream fIn = Files.newInputStream(file.toPath())) {
  1727. // ZIPs store time with a granularity of 2 seconds, round up
  1728. zipFile(fIn, zOut, vPath,
  1729. file.lastModified() + (roundUp ? ROUNDUP_MILLIS : 0),
  1730. null, mode);
  1731. }
  1732. }
  1733. /**
  1734. * Ensure all parent dirs of a given entry have been added.
  1735. * @param baseDir the base directory to use (may be null)
  1736. * @param entry the entry name to create directories from
  1737. * @param zOut the stream to write to
  1738. * @param prefix a prefix to place on the created entries
  1739. * @param dirMode the directory mode
  1740. * @throws IOException on error
  1741. * @since Ant 1.5.2
  1742. */
  1743. protected final void addParentDirs(final File baseDir, final String entry,
  1744. final ZipOutputStream zOut, final String prefix,
  1745. final int dirMode)
  1746. throws IOException {
  1747. if (!doFilesonly) {
  1748. final Stack<String> directories = new Stack<String>();
  1749. int slashPos = entry.length();
  1750. while ((slashPos = entry.lastIndexOf('/', slashPos - 1)) != -1) {
  1751. final String dir = entry.substring(0, slashPos + 1);
  1752. if (addedDirs.get(prefix + dir) != null) {
  1753. break;
  1754. }
  1755. directories.push(dir);
  1756. }
  1757. while (!directories.isEmpty()) {
  1758. final String dir = directories.pop();
  1759. File f = null;
  1760. if (baseDir != null) {
  1761. f = new File(baseDir, dir);
  1762. } else {
  1763. f = new File(dir);
  1764. }
  1765. zipDir(f, zOut, prefix + dir, dirMode);
  1766. }
  1767. }
  1768. }
  1769. /**
  1770. * Do any clean up necessary to allow this instance to be used again.
  1771. *
  1772. * <p>When we get here, the Zip file has been closed and all we
  1773. * need to do is to reset some globals.</p>
  1774. *
  1775. * <p>This method will only reset globals that have been changed
  1776. * during execute(), it will not alter the attributes or nested
  1777. * child elements. If you want to reset the instance so that you
  1778. * can later zip a completely different set of files, you must use
  1779. * the reset method.</p>
  1780. *
  1781. * @see #reset
  1782. */
  1783. protected void cleanUp() {
  1784. addedDirs.clear();
  1785. addedFiles.removeAllElements();
  1786. entries.clear();
  1787. addingNewFiles = false;
  1788. doUpdate = savedDoUpdate;
  1789. final Enumeration<ZipFileSet> e = filesetsFromGroupfilesets.elements();
  1790. while (e.hasMoreElements()) {
  1791. final ZipFileSet zf = e.nextElement();
  1792. resources.removeElement(zf);
  1793. }
  1794. filesetsFromGroupfilesets.removeAllElements();
  1795. HAVE_NON_FILE_SET_RESOURCES_TO_ADD.set(Boolean.FALSE);
  1796. }
  1797. /**
  1798. * Makes this instance reset all attributes to their default
  1799. * values and forget all children.
  1800. *
  1801. * @since Ant 1.5
  1802. *
  1803. * @see #cleanUp
  1804. */
  1805. public void reset() {
  1806. resources.removeAllElements();
  1807. zipFile = null;
  1808. baseDir = null;
  1809. groupfilesets.removeAllElements();
  1810. duplicate = "add";
  1811. archiveType = "zip";
  1812. doCompress = true;
  1813. emptyBehavior = "skip";
  1814. doUpdate = false;
  1815. doFilesonly = false;
  1816. encoding = null;
  1817. }
  1818. /**
  1819. * Check is the resource arrays are empty.
  1820. * @param r the arrays to check
  1821. * @return true if all individual arrays are empty
  1822. *
  1823. * @since Ant 1.5.2
  1824. */
  1825. protected static final boolean isEmpty(final Resource[][] r) {
  1826. for (int i = 0; i < r.length; i++) {
  1827. if (r[i].length > 0) {
  1828. return false;
  1829. }
  1830. }
  1831. return true;
  1832. }
  1833. /**
  1834. * Drops all non-file resources from the given array.
  1835. * @param orig the resources to filter
  1836. * @return the filters resources
  1837. * @since Ant 1.6
  1838. */
  1839. protected Resource[] selectFileResources(final Resource[] orig) {
  1840. return selectResources(orig,
  1841. new ResourceSelector() {
  1842. public boolean isSelected(final Resource r) {
  1843. if (!r.isDirectory()) {
  1844. return true;
  1845. } else if (doFilesonly) {
  1846. logWhenWriting("Ignoring directory "
  1847. + r.getName()
  1848. + " as only files will"
  1849. + " be added.",
  1850. Project.MSG_VERBOSE);
  1851. }
  1852. return false;
  1853. }
  1854. });
  1855. }
  1856. /**
  1857. * Drops all non-directory resources from the given array.
  1858. * @param orig the resources to filter
  1859. * @return the filters resources
  1860. * @since Ant 1.8.0
  1861. */
  1862. protected Resource[] selectDirectoryResources(final Resource[] orig) {
  1863. return selectResources(orig,
  1864. new ResourceSelector() {
  1865. public boolean isSelected(final Resource r) {
  1866. return r.isDirectory();
  1867. }
  1868. });
  1869. }
  1870. /**
  1871. * Drops all resources from the given array that are not selected
  1872. * @param orig the resources to filter
  1873. * @return the filters resources
  1874. * @since Ant 1.8.0
  1875. */
  1876. protected Resource[] selectResources(final Resource[] orig,
  1877. final ResourceSelector selector) {
  1878. if (orig.length == 0) {
  1879. return orig;
  1880. }
  1881. final ArrayList<Resource> v = new ArrayList<Resource>(orig.length);
  1882. for (int i = 0; i < orig.length; i++) {
  1883. if (selector.isSelected(orig[i])) {
  1884. v.add(orig[i]);
  1885. }
  1886. }
  1887. if (v.size() != orig.length) {
  1888. return v.toArray(new Resource[v.size()]);
  1889. }
  1890. return orig;
  1891. }
  1892. /**
  1893. * Logs a message at the given output level, but only if this is
  1894. * the pass that will actually create the archive.
  1895. *
  1896. * @since Ant 1.8.0
  1897. */
  1898. protected void logWhenWriting(final String msg, final int level) {
  1899. if (!skipWriting) {
  1900. log(msg, level);
  1901. }
  1902. }
  1903. /**
  1904. * Possible behaviors when a duplicate file is added:
  1905. * "add", "preserve" or "fail"
  1906. */
  1907. public static class Duplicate extends EnumeratedAttribute {
  1908. /**
  1909. * @see EnumeratedAttribute#getValues()
  1910. */
  1911. /** {@inheritDoc} */
  1912. @Override
  1913. public String[] getValues() {
  1914. return new String[] {"add", "preserve", "fail"};
  1915. }
  1916. }
  1917. /**
  1918. * Holds the up-to-date status and the out-of-date resources of
  1919. * the original archive.
  1920. *
  1921. * @since Ant 1.5.3
  1922. */
  1923. public static class ArchiveState {
  1924. private final boolean outOfDate;
  1925. private final Resource[][] resourcesToAdd;
  1926. ArchiveState(final boolean state, final Resource[][] r) {
  1927. outOfDate = state;
  1928. resourcesToAdd = r;
  1929. }
  1930. /**
  1931. * Return the outofdate status.
  1932. * @return the outofdate status
  1933. */
  1934. public boolean isOutOfDate() {
  1935. return outOfDate;
  1936. }
  1937. /**
  1938. * Get the resources to add.
  1939. * @return the resources to add
  1940. */
  1941. public Resource[][] getResourcesToAdd() {
  1942. return resourcesToAdd;
  1943. }
  1944. /**
  1945. * find out if there are absolutely no resources to add
  1946. * @since Ant 1.6.3
  1947. * @return true if there are no resources to add
  1948. */
  1949. public boolean isWithoutAnyResources() {
  1950. if (resourcesToAdd == null) {
  1951. return true;
  1952. }
  1953. for (int counter = 0; counter < resourcesToAdd.length; counter++) {
  1954. if (resourcesToAdd[counter] != null) {
  1955. if (resourcesToAdd[counter].length > 0) {
  1956. return false;
  1957. }
  1958. }
  1959. }
  1960. return true;
  1961. }
  1962. }
  1963. /**
  1964. * Policiy for creation of Unicode extra fields: never, always or
  1965. * not-encodeable.
  1966. *
  1967. * @since Ant 1.8.0
  1968. */
  1969. public static final class UnicodeExtraField extends EnumeratedAttribute {
  1970. private static final Map<String, UnicodeExtraFieldPolicy> POLICIES = new HashMap<String, UnicodeExtraFieldPolicy>();
  1971. private static final String NEVER_KEY = "never";
  1972. private static final String ALWAYS_KEY = "always";
  1973. private static final String N_E_KEY = "not-encodeable";
  1974. static {
  1975. POLICIES.put(NEVER_KEY,
  1976. ZipOutputStream.UnicodeExtraFieldPolicy.NEVER);
  1977. POLICIES.put(ALWAYS_KEY,
  1978. ZipOutputStream.UnicodeExtraFieldPolicy.ALWAYS);
  1979. POLICIES.put(N_E_KEY,
  1980. ZipOutputStream.UnicodeExtraFieldPolicy
  1981. .NOT_ENCODEABLE);
  1982. }
  1983. @Override
  1984. public String[] getValues() {
  1985. return new String[] {NEVER_KEY, ALWAYS_KEY, N_E_KEY};
  1986. }
  1987. public static final UnicodeExtraField NEVER =
  1988. new UnicodeExtraField(NEVER_KEY);
  1989. private UnicodeExtraField(final String name) {
  1990. setValue(name);
  1991. }
  1992. public UnicodeExtraField() {
  1993. }
  1994. public ZipOutputStream.UnicodeExtraFieldPolicy getPolicy() {
  1995. return POLICIES.get(getValue());
  1996. }
  1997. }
  1998. /**
  1999. * The choices for Zip64 extensions.
  2000. *
  2001. * <p><b>never</b>: never add any Zip64 extensions. This will
  2002. * cause the task to fail if you try to add entries bigger than
  2003. * 4GB or create an archive bigger than 4GB or holding more that
  2004. * 65535 entries.</p>
  2005. *
  2006. * <p><b>as-needed</b>: create Zip64 extensions only when the
  2007. * entry's size is bigger than 4GB or one of the archive limits is
  2008. * hit. This mode also adds partial Zip64 extensions for all
  2009. * deflated entries written by Ant.</p>
  2010. *
  2011. * <p><b>always</b>: create Zip64 extensions for all entries.</p>
  2012. *
  2013. * <p><b>Note</b> some ZIP implementations don't handle Zip64
  2014. * extensions well and others may fail if the Zip64 extra field
  2015. * data is only present inside the local file header but not the
  2016. * central directory - which is what <em>as-needed</em> may result
  2017. * in. Java5 and Microsoft Visual Studio's Extension loader are
  2018. * known to fconsider the archive broken in such cases. If you
  2019. * are targeting such an archiver uset the value <em>never</em>
  2020. * unless you know you need Zip64 extensions.</p>
  2021. *
  2022. * @since Ant 1.9.1
  2023. */
  2024. public static final class Zip64ModeAttribute extends EnumeratedAttribute {
  2025. private static final Map<String, Zip64Mode> MODES = new HashMap<String, Zip64Mode>();
  2026. private static final String NEVER_KEY = "never";
  2027. private static final String ALWAYS_KEY = "always";
  2028. private static final String A_N_KEY = "as-needed";
  2029. static {
  2030. MODES.put(NEVER_KEY, Zip64Mode.Never);
  2031. MODES.put(ALWAYS_KEY, Zip64Mode.Always);
  2032. MODES.put(A_N_KEY, Zip64Mode.AsNeeded);
  2033. }
  2034. @Override
  2035. public String[] getValues() {
  2036. return new String[] {NEVER_KEY, ALWAYS_KEY, A_N_KEY};
  2037. }
  2038. public static final Zip64ModeAttribute NEVER =
  2039. new Zip64ModeAttribute(NEVER_KEY);
  2040. public static final Zip64ModeAttribute AS_NEEDED =
  2041. new Zip64ModeAttribute(A_N_KEY);
  2042. private Zip64ModeAttribute(final String name) {
  2043. setValue(name);
  2044. }
  2045. public Zip64ModeAttribute() {
  2046. }
  2047. public Zip64Mode getMode() {
  2048. return MODES.get(getValue());
  2049. }
  2050. }
  2051. }