1 | /*
|
---|
2 | * Copyright 2000-2005 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 | package org.apache.tools.ant.taskdefs;
|
---|
18 |
|
---|
19 | import java.io.ByteArrayInputStream;
|
---|
20 | import java.io.ByteArrayOutputStream;
|
---|
21 | import java.io.File;
|
---|
22 | import java.io.FileInputStream;
|
---|
23 | import java.io.FileOutputStream;
|
---|
24 | import java.io.IOException;
|
---|
25 | import java.io.InputStream;
|
---|
26 | import java.io.OutputStream;
|
---|
27 | import java.util.Enumeration;
|
---|
28 | import java.util.Hashtable;
|
---|
29 | import java.util.Stack;
|
---|
30 | import java.util.Vector;
|
---|
31 | import java.util.zip.CRC32;
|
---|
32 |
|
---|
33 | import org.apache.tools.ant.BuildException;
|
---|
34 | import org.apache.tools.ant.DirectoryScanner;
|
---|
35 | import org.apache.tools.ant.FileScanner;
|
---|
36 | import org.apache.tools.ant.Project;
|
---|
37 | import org.apache.tools.ant.types.EnumeratedAttribute;
|
---|
38 | import org.apache.tools.ant.types.FileSet;
|
---|
39 | import org.apache.tools.ant.types.PatternSet;
|
---|
40 | import org.apache.tools.ant.types.Resource;
|
---|
41 | import org.apache.tools.ant.types.ZipFileSet;
|
---|
42 | import org.apache.tools.ant.types.ZipScanner;
|
---|
43 | import org.apache.tools.ant.util.FileNameMapper;
|
---|
44 | import org.apache.tools.ant.util.FileUtils;
|
---|
45 | import org.apache.tools.ant.util.GlobPatternMapper;
|
---|
46 | import org.apache.tools.ant.util.IdentityMapper;
|
---|
47 | import org.apache.tools.ant.util.MergingMapper;
|
---|
48 | import org.apache.tools.ant.util.ResourceUtils;
|
---|
49 | import org.apache.tools.zip.ZipEntry;
|
---|
50 | import org.apache.tools.zip.ZipExtraField;
|
---|
51 | import org.apache.tools.zip.ZipFile;
|
---|
52 | import org.apache.tools.zip.ZipOutputStream;
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * Create a Zip file.
|
---|
56 | *
|
---|
57 | * @since Ant 1.1
|
---|
58 | *
|
---|
59 | * @ant.task category="packaging"
|
---|
60 | */
|
---|
61 | public class Zip extends MatchingTask {
|
---|
62 |
|
---|
63 | protected File zipFile;
|
---|
64 | // use to scan own archive
|
---|
65 | private ZipScanner zs;
|
---|
66 | private File baseDir;
|
---|
67 | protected Hashtable entries = new Hashtable();
|
---|
68 | private Vector groupfilesets = new Vector();
|
---|
69 | private Vector filesetsFromGroupfilesets = new Vector();
|
---|
70 | protected String duplicate = "add";
|
---|
71 | private boolean doCompress = true;
|
---|
72 | private boolean doUpdate = false;
|
---|
73 | // shadow of the above if the value is altered in execute
|
---|
74 | private boolean savedDoUpdate = false;
|
---|
75 | private boolean doFilesonly = false;
|
---|
76 | protected String archiveType = "zip";
|
---|
77 |
|
---|
78 | // For directories:
|
---|
79 | private static final long EMPTY_CRC = new CRC32 ().getValue ();
|
---|
80 | protected String emptyBehavior = "skip";
|
---|
81 | private Vector filesets = new Vector ();
|
---|
82 | protected Hashtable addedDirs = new Hashtable();
|
---|
83 | private Vector addedFiles = new Vector();
|
---|
84 |
|
---|
85 | protected boolean doubleFilePass = false;
|
---|
86 | protected boolean skipWriting = false;
|
---|
87 |
|
---|
88 | private static FileUtils fileUtils = FileUtils.newFileUtils();
|
---|
89 |
|
---|
90 | /**
|
---|
91 | * true when we are adding new files into the Zip file, as opposed
|
---|
92 | * to adding back the unchanged files
|
---|
93 | */
|
---|
94 | private boolean addingNewFiles = false;
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * Encoding to use for filenames, defaults to the platform's
|
---|
98 | * default encoding.
|
---|
99 | */
|
---|
100 | private String encoding;
|
---|
101 |
|
---|
102 | /**
|
---|
103 | * Whether the original compression of entries coming from a ZIP
|
---|
104 | * archive should be kept (for example when updating an archive).
|
---|
105 | *
|
---|
106 | * @since Ant 1.6
|
---|
107 | */
|
---|
108 | private boolean keepCompression = false;
|
---|
109 |
|
---|
110 | /**
|
---|
111 | * Whether the file modification times will be rounded up to the
|
---|
112 | * next even number of seconds.
|
---|
113 | *
|
---|
114 | * @since Ant 1.6.2
|
---|
115 | */
|
---|
116 | private boolean roundUp = true;
|
---|
117 |
|
---|
118 | /**
|
---|
119 | * Comment for the archive.
|
---|
120 | * @since Ant 1.6.3
|
---|
121 | */
|
---|
122 | private String comment = "";
|
---|
123 |
|
---|
124 | /**
|
---|
125 | * This is the name/location of where to
|
---|
126 | * create the .zip file.
|
---|
127 | *
|
---|
128 | * @deprecated Use setDestFile(File) instead.
|
---|
129 | * @ant.attribute ignore="true"
|
---|
130 | */
|
---|
131 | public void setZipfile(File zipFile) {
|
---|
132 | setDestFile(zipFile);
|
---|
133 | }
|
---|
134 |
|
---|
135 | /**
|
---|
136 | * This is the name/location of where to
|
---|
137 | * create the file.
|
---|
138 | * @since Ant 1.5
|
---|
139 | * @deprecated Use setDestFile(File) instead
|
---|
140 | * @ant.attribute ignore="true"
|
---|
141 | */
|
---|
142 | public void setFile(File file) {
|
---|
143 | setDestFile(file);
|
---|
144 | }
|
---|
145 |
|
---|
146 |
|
---|
147 | /**
|
---|
148 | * The file to create; required.
|
---|
149 | * @since Ant 1.5
|
---|
150 | * @param destFile The new destination File
|
---|
151 | */
|
---|
152 | public void setDestFile(File destFile) {
|
---|
153 | this.zipFile = destFile;
|
---|
154 | }
|
---|
155 |
|
---|
156 | /**
|
---|
157 | * The file to create.
|
---|
158 | * @since Ant 1.5.2
|
---|
159 | */
|
---|
160 | public File getDestFile() {
|
---|
161 | return zipFile;
|
---|
162 | }
|
---|
163 |
|
---|
164 |
|
---|
165 | /**
|
---|
166 | * Directory from which to archive files; optional.
|
---|
167 | */
|
---|
168 | public void setBasedir(File baseDir) {
|
---|
169 | this.baseDir = baseDir;
|
---|
170 | }
|
---|
171 |
|
---|
172 | /**
|
---|
173 | * Whether we want to compress the files or only store them;
|
---|
174 | * optional, default=true;
|
---|
175 | */
|
---|
176 | public void setCompress(boolean c) {
|
---|
177 | doCompress = c;
|
---|
178 | }
|
---|
179 |
|
---|
180 | /**
|
---|
181 | * Whether we want to compress the files or only store them;
|
---|
182 | *
|
---|
183 | * @since Ant 1.5.2
|
---|
184 | */
|
---|
185 | public boolean isCompress() {
|
---|
186 | return doCompress;
|
---|
187 | }
|
---|
188 |
|
---|
189 | /**
|
---|
190 | * If true, emulate Sun's jar utility by not adding parent directories;
|
---|
191 | * optional, defaults to false.
|
---|
192 | */
|
---|
193 | public void setFilesonly(boolean f) {
|
---|
194 | doFilesonly = f;
|
---|
195 | }
|
---|
196 |
|
---|
197 | /**
|
---|
198 | * If true, updates an existing file, otherwise overwrite
|
---|
199 | * any existing one; optional defaults to false.
|
---|
200 | */
|
---|
201 | public void setUpdate(boolean c) {
|
---|
202 | doUpdate = c;
|
---|
203 | savedDoUpdate = c;
|
---|
204 | }
|
---|
205 |
|
---|
206 | /**
|
---|
207 | * Are we updating an existing archive?
|
---|
208 | */
|
---|
209 | public boolean isInUpdateMode() {
|
---|
210 | return doUpdate;
|
---|
211 | }
|
---|
212 |
|
---|
213 | /**
|
---|
214 | * Adds a set of files.
|
---|
215 | */
|
---|
216 | public void addFileset(FileSet set) {
|
---|
217 | filesets.addElement(set);
|
---|
218 | }
|
---|
219 |
|
---|
220 | /**
|
---|
221 | * Adds a set of files that can be
|
---|
222 | * read from an archive and be given a prefix/fullpath.
|
---|
223 | */
|
---|
224 | public void addZipfileset(ZipFileSet set) {
|
---|
225 | filesets.addElement(set);
|
---|
226 | }
|
---|
227 |
|
---|
228 | /**
|
---|
229 | * Adds a group of zip files.
|
---|
230 | */
|
---|
231 | public void addZipGroupFileset(FileSet set) {
|
---|
232 | groupfilesets.addElement(set);
|
---|
233 | }
|
---|
234 |
|
---|
235 | /**
|
---|
236 | * Sets behavior for when a duplicate file is about to be added -
|
---|
237 | * one of <code>keep</code>, <code>skip</code> or <code>overwrite</code>.
|
---|
238 | * Possible values are: <code>keep</code> (keep both
|
---|
239 | * of the files); <code>skip</code> (keep the first version
|
---|
240 | * of the file found); <code>overwrite</code> overwrite the file
|
---|
241 | * with the new file
|
---|
242 | * Default for zip tasks is <code>keep</code>
|
---|
243 | */
|
---|
244 | public void setDuplicate(Duplicate df) {
|
---|
245 | duplicate = df.getValue();
|
---|
246 | }
|
---|
247 |
|
---|
248 | /**
|
---|
249 | * Possible behaviors when there are no matching files for the task:
|
---|
250 | * "fail", "skip", or "create".
|
---|
251 | */
|
---|
252 | public static class WhenEmpty extends EnumeratedAttribute {
|
---|
253 | public String[] getValues() {
|
---|
254 | return new String[] {"fail", "skip", "create"};
|
---|
255 | }
|
---|
256 | }
|
---|
257 |
|
---|
258 | /**
|
---|
259 | * Sets behavior of the task when no files match.
|
---|
260 | * Possible values are: <code>fail</code> (throw an exception
|
---|
261 | * and halt the build); <code>skip</code> (do not create
|
---|
262 | * any archive, but issue a warning); <code>create</code>
|
---|
263 | * (make an archive with no entries).
|
---|
264 | * Default for zip tasks is <code>skip</code>;
|
---|
265 | * for jar tasks, <code>create</code>.
|
---|
266 | */
|
---|
267 | public void setWhenempty(WhenEmpty we) {
|
---|
268 | emptyBehavior = we.getValue();
|
---|
269 | }
|
---|
270 |
|
---|
271 | /**
|
---|
272 | * Encoding to use for filenames, defaults to the platform's
|
---|
273 | * default encoding.
|
---|
274 | *
|
---|
275 | * <p>For a list of possible values see <a
|
---|
276 | * 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>
|
---|
277 | */
|
---|
278 | public void setEncoding(String encoding) {
|
---|
279 | this.encoding = encoding;
|
---|
280 | }
|
---|
281 |
|
---|
282 | /**
|
---|
283 | * Encoding to use for filenames.
|
---|
284 | *
|
---|
285 | * @since Ant 1.5.2
|
---|
286 | */
|
---|
287 | public String getEncoding() {
|
---|
288 | return encoding;
|
---|
289 | }
|
---|
290 |
|
---|
291 | /**
|
---|
292 | * Whether the original compression of entries coming from a ZIP
|
---|
293 | * archive should be kept (for example when updating an archive).
|
---|
294 | *
|
---|
295 | * @since Ant 1.6
|
---|
296 | */
|
---|
297 | public void setKeepCompression(boolean keep) {
|
---|
298 | keepCompression = keep;
|
---|
299 | }
|
---|
300 |
|
---|
301 | /**
|
---|
302 | * Comment to use for archive.
|
---|
303 | *
|
---|
304 | * @param comment The content of the comment.
|
---|
305 | * @since Ant 1.6.3
|
---|
306 | */
|
---|
307 | public void setComment(String comment) {
|
---|
308 | this.comment = comment;
|
---|
309 | }
|
---|
310 |
|
---|
311 | /**
|
---|
312 | * Comment of the archive
|
---|
313 | *
|
---|
314 | * @return Comment of the archive.
|
---|
315 | * @since Ant 1.6.3
|
---|
316 | */
|
---|
317 | public String getComment() {
|
---|
318 | return comment;
|
---|
319 | }
|
---|
320 |
|
---|
321 | /**
|
---|
322 | * Whether the file modification times will be rounded up to the
|
---|
323 | * next even number of seconds.
|
---|
324 | *
|
---|
325 | * <p>Zip archives store file modification times with a
|
---|
326 | * granularity of two seconds, so the times will either be rounded
|
---|
327 | * up or down. If you round down, the archive will always seem
|
---|
328 | * out-of-date when you rerun the task, so the default is to round
|
---|
329 | * up. Rounding up may lead to a different type of problems like
|
---|
330 | * JSPs inside a web archive that seem to be slightly more recent
|
---|
331 | * than precompiled pages, rendering precompilation useless.</p>
|
---|
332 | *
|
---|
333 | * @since Ant 1.6.2
|
---|
334 | */
|
---|
335 | public void setRoundUp(boolean r) {
|
---|
336 | roundUp = r;
|
---|
337 | }
|
---|
338 |
|
---|
339 | /**
|
---|
340 | * validate and build
|
---|
341 | */
|
---|
342 | public void execute() throws BuildException {
|
---|
343 |
|
---|
344 | if (doubleFilePass) {
|
---|
345 | skipWriting = true;
|
---|
346 | executeMain();
|
---|
347 | skipWriting = false;
|
---|
348 | executeMain();
|
---|
349 | } else {
|
---|
350 | executeMain();
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 | public void executeMain() throws BuildException {
|
---|
355 |
|
---|
356 | if (baseDir == null && filesets.size() == 0
|
---|
357 | && groupfilesets.size() == 0 && "zip".equals(archiveType)) {
|
---|
358 | throw new BuildException("basedir attribute must be set, "
|
---|
359 | + "or at least "
|
---|
360 | + "one fileset must be given!");
|
---|
361 | }
|
---|
362 |
|
---|
363 | if (zipFile == null) {
|
---|
364 | throw new BuildException("You must specify the "
|
---|
365 | + archiveType + " file to create!");
|
---|
366 | }
|
---|
367 |
|
---|
368 | if (zipFile.exists() && !zipFile.isFile()) {
|
---|
369 | throw new BuildException(zipFile + " is not a file.");
|
---|
370 | }
|
---|
371 |
|
---|
372 | if (zipFile.exists() && !zipFile.canWrite()) {
|
---|
373 | throw new BuildException(zipFile + " is read-only.");
|
---|
374 | }
|
---|
375 |
|
---|
376 | // Renamed version of original file, if it exists
|
---|
377 | File renamedFile = null;
|
---|
378 | // Whether or not an actual update is required -
|
---|
379 | // we don't need to update if the original file doesn't exist
|
---|
380 |
|
---|
381 | addingNewFiles = true;
|
---|
382 | if (doUpdate && !zipFile.exists()) {
|
---|
383 | doUpdate = false;
|
---|
384 | log("ignoring update attribute as " + archiveType
|
---|
385 | + " doesn't exist.", Project.MSG_DEBUG);
|
---|
386 | }
|
---|
387 |
|
---|
388 | // Add the files found in groupfileset to fileset
|
---|
389 | for (int i = 0; i < groupfilesets.size(); i++) {
|
---|
390 |
|
---|
391 | log("Processing groupfileset ", Project.MSG_VERBOSE);
|
---|
392 | FileSet fs = (FileSet) groupfilesets.elementAt(i);
|
---|
393 | FileScanner scanner = fs.getDirectoryScanner(getProject());
|
---|
394 | String[] files = scanner.getIncludedFiles();
|
---|
395 | File basedir = scanner.getBasedir();
|
---|
396 | for (int j = 0; j < files.length; j++) {
|
---|
397 |
|
---|
398 | log("Adding file " + files[j] + " to fileset",
|
---|
399 | Project.MSG_VERBOSE);
|
---|
400 | ZipFileSet zf = new ZipFileSet();
|
---|
401 | zf.setProject(getProject());
|
---|
402 | zf.setSrc(new File(basedir, files[j]));
|
---|
403 | filesets.addElement(zf);
|
---|
404 | filesetsFromGroupfilesets.addElement(zf);
|
---|
405 | }
|
---|
406 | }
|
---|
407 |
|
---|
408 | // collect filesets to pass them to getResourcesToAdd
|
---|
409 | Vector vfss = new Vector();
|
---|
410 | if (baseDir != null) {
|
---|
411 | FileSet fs = (FileSet) getImplicitFileSet().clone();
|
---|
412 | fs.setDir(baseDir);
|
---|
413 | vfss.addElement(fs);
|
---|
414 | }
|
---|
415 | for (int i = 0; i < filesets.size(); i++) {
|
---|
416 | FileSet fs = (FileSet) filesets.elementAt(i);
|
---|
417 | vfss.addElement(fs);
|
---|
418 | }
|
---|
419 |
|
---|
420 | FileSet[] fss = new FileSet[vfss.size()];
|
---|
421 | vfss.copyInto(fss);
|
---|
422 | boolean success = false;
|
---|
423 | try {
|
---|
424 | // can also handle empty archives
|
---|
425 | ArchiveState state = getResourcesToAdd(fss, zipFile, false);
|
---|
426 |
|
---|
427 | // quick exit if the target is up to date
|
---|
428 | if (!state.isOutOfDate()) {
|
---|
429 | return;
|
---|
430 | }
|
---|
431 |
|
---|
432 | if (!zipFile.exists() && state.isWithoutAnyResources()) {
|
---|
433 | createEmptyZip(zipFile);
|
---|
434 | return;
|
---|
435 | }
|
---|
436 | Resource[][] addThem = state.getResourcesToAdd();
|
---|
437 |
|
---|
438 | if (doUpdate) {
|
---|
439 | renamedFile =
|
---|
440 | fileUtils.createTempFile("zip", ".tmp",
|
---|
441 | fileUtils.getParentFile(zipFile));
|
---|
442 | renamedFile.deleteOnExit();
|
---|
443 |
|
---|
444 | try {
|
---|
445 | fileUtils.rename(zipFile, renamedFile);
|
---|
446 | } catch (SecurityException e) {
|
---|
447 | throw new BuildException(
|
---|
448 | "Not allowed to rename old file ("
|
---|
449 | + zipFile.getAbsolutePath()
|
---|
450 | + ") to temporary file");
|
---|
451 | } catch (IOException e) {
|
---|
452 | throw new BuildException(
|
---|
453 | "Unable to rename old file ("
|
---|
454 | + zipFile.getAbsolutePath()
|
---|
455 | + ") to temporary file");
|
---|
456 | }
|
---|
457 | }
|
---|
458 |
|
---|
459 | String action = doUpdate ? "Updating " : "Building ";
|
---|
460 |
|
---|
461 | log(action + archiveType + ": " + zipFile.getAbsolutePath());
|
---|
462 |
|
---|
463 | ZipOutputStream zOut = null;
|
---|
464 | try {
|
---|
465 |
|
---|
466 | if (!skipWriting) {
|
---|
467 | zOut = new ZipOutputStream(zipFile);
|
---|
468 |
|
---|
469 | zOut.setEncoding(encoding);
|
---|
470 | if (doCompress) {
|
---|
471 | zOut.setMethod(ZipOutputStream.DEFLATED);
|
---|
472 | } else {
|
---|
473 | zOut.setMethod(ZipOutputStream.STORED);
|
---|
474 | }
|
---|
475 | }
|
---|
476 | initZipOutputStream(zOut);
|
---|
477 |
|
---|
478 | // Add the explicit filesets to the archive.
|
---|
479 | for (int i = 0; i < fss.length; i++) {
|
---|
480 | if (addThem[i].length != 0) {
|
---|
481 | addResources(fss[i], addThem[i], zOut);
|
---|
482 | }
|
---|
483 | }
|
---|
484 |
|
---|
485 | if (doUpdate) {
|
---|
486 | addingNewFiles = false;
|
---|
487 | ZipFileSet oldFiles = new ZipFileSet();
|
---|
488 | oldFiles.setProject(getProject());
|
---|
489 | oldFiles.setSrc(renamedFile);
|
---|
490 | oldFiles.setDefaultexcludes(false);
|
---|
491 |
|
---|
492 | for (int i = 0; i < addedFiles.size(); i++) {
|
---|
493 | PatternSet.NameEntry ne = oldFiles.createExclude();
|
---|
494 | ne.setName((String) addedFiles.elementAt(i));
|
---|
495 | }
|
---|
496 | DirectoryScanner ds =
|
---|
497 | oldFiles.getDirectoryScanner(getProject());
|
---|
498 | ((ZipScanner) ds).setEncoding(encoding);
|
---|
499 |
|
---|
500 | String[] f = ds.getIncludedFiles();
|
---|
501 | Resource[] r = new Resource[f.length];
|
---|
502 | for (int i = 0; i < f.length; i++) {
|
---|
503 | r[i] = ds.getResource(f[i]);
|
---|
504 | }
|
---|
505 |
|
---|
506 | if (!doFilesonly) {
|
---|
507 | String[] d = ds.getIncludedDirectories();
|
---|
508 | Resource[] dr = new Resource[d.length];
|
---|
509 | for (int i = 0; i < d.length; i++) {
|
---|
510 | dr[i] = ds.getResource(d[i]);
|
---|
511 | }
|
---|
512 | Resource[] tmp = r;
|
---|
513 | r = new Resource[tmp.length + dr.length];
|
---|
514 | System.arraycopy(dr, 0, r, 0, dr.length);
|
---|
515 | System.arraycopy(tmp, 0, r, dr.length, tmp.length);
|
---|
516 | }
|
---|
517 | addResources(oldFiles, r, zOut);
|
---|
518 | }
|
---|
519 | if (zOut != null) {
|
---|
520 | zOut.setComment(comment);
|
---|
521 | }
|
---|
522 | finalizeZipOutputStream(zOut);
|
---|
523 |
|
---|
524 | // If we've been successful on an update, delete the
|
---|
525 | // temporary file
|
---|
526 | if (doUpdate) {
|
---|
527 | if (!renamedFile.delete()) {
|
---|
528 | log ("Warning: unable to delete temporary file "
|
---|
529 | + renamedFile.getName(), Project.MSG_WARN);
|
---|
530 | }
|
---|
531 | }
|
---|
532 | success = true;
|
---|
533 | } finally {
|
---|
534 | // Close the output stream.
|
---|
535 | try {
|
---|
536 | if (zOut != null) {
|
---|
537 | zOut.close();
|
---|
538 | }
|
---|
539 | } catch (IOException ex) {
|
---|
540 | // If we're in this finally clause because of an
|
---|
541 | // exception, we don't really care if there's an
|
---|
542 | // exception when closing the stream. E.g. if it
|
---|
543 | // throws "ZIP file must have at least one entry",
|
---|
544 | // because an exception happened before we added
|
---|
545 | // any files, then we must swallow this
|
---|
546 | // exception. Otherwise, the error that's reported
|
---|
547 | // will be the close() error, which is not the
|
---|
548 | // real cause of the problem.
|
---|
549 | if (success) {
|
---|
550 | throw ex;
|
---|
551 | }
|
---|
552 | }
|
---|
553 | }
|
---|
554 | } catch (IOException ioe) {
|
---|
555 | String msg = "Problem creating " + archiveType + ": "
|
---|
556 | + ioe.getMessage();
|
---|
557 |
|
---|
558 | // delete a bogus ZIP file (but only if it's not the original one)
|
---|
559 | if ((!doUpdate || renamedFile != null) && !zipFile.delete()) {
|
---|
560 | msg += " (and the archive is probably corrupt but I could not "
|
---|
561 | + "delete it)";
|
---|
562 | }
|
---|
563 |
|
---|
564 | if (doUpdate && renamedFile != null) {
|
---|
565 | try {
|
---|
566 | fileUtils.rename(renamedFile, zipFile);
|
---|
567 | } catch (IOException e) {
|
---|
568 | msg += " (and I couldn't rename the temporary file "
|
---|
569 | + renamedFile.getName() + " back)";
|
---|
570 | }
|
---|
571 | }
|
---|
572 |
|
---|
573 | throw new BuildException(msg, ioe, getLocation());
|
---|
574 | } finally {
|
---|
575 | cleanUp();
|
---|
576 | }
|
---|
577 | }
|
---|
578 |
|
---|
579 | /**
|
---|
580 | * Indicates if the task is adding new files into the archive as opposed to
|
---|
581 | * copying back unchanged files from the backup copy
|
---|
582 | */
|
---|
583 | protected final boolean isAddingNewFiles() {
|
---|
584 | return addingNewFiles;
|
---|
585 | }
|
---|
586 |
|
---|
587 | /**
|
---|
588 | * Add the given resources.
|
---|
589 | *
|
---|
590 | * @param fileset may give additional information like fullpath or
|
---|
591 | * permissions.
|
---|
592 | * @param resources the resources to add
|
---|
593 | * @param zOut the stream to write to
|
---|
594 | *
|
---|
595 | * @since Ant 1.5.2
|
---|
596 | */
|
---|
597 | protected final void addResources(FileSet fileset, Resource[] resources,
|
---|
598 | ZipOutputStream zOut)
|
---|
599 | throws IOException {
|
---|
600 |
|
---|
601 | String prefix = "";
|
---|
602 | String fullpath = "";
|
---|
603 | int dirMode = ZipFileSet.DEFAULT_DIR_MODE;
|
---|
604 | int fileMode = ZipFileSet.DEFAULT_FILE_MODE;
|
---|
605 |
|
---|
606 | ZipFileSet zfs = null;
|
---|
607 | if (fileset instanceof ZipFileSet) {
|
---|
608 | zfs = (ZipFileSet) fileset;
|
---|
609 | prefix = zfs.getPrefix(getProject());
|
---|
610 | fullpath = zfs.getFullpath(getProject());
|
---|
611 | dirMode = zfs.getDirMode(getProject());
|
---|
612 | fileMode = zfs.getFileMode(getProject());
|
---|
613 | }
|
---|
614 |
|
---|
615 | if (prefix.length() > 0 && fullpath.length() > 0) {
|
---|
616 | throw new BuildException("Both prefix and fullpath attributes must"
|
---|
617 | + " not be set on the same fileset.");
|
---|
618 | }
|
---|
619 |
|
---|
620 | if (resources.length != 1 && fullpath.length() > 0) {
|
---|
621 | throw new BuildException("fullpath attribute may only be specified"
|
---|
622 | + " for filesets that specify a single"
|
---|
623 | + " file.");
|
---|
624 | }
|
---|
625 |
|
---|
626 | if (prefix.length() > 0) {
|
---|
627 | if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
|
---|
628 | prefix += "/";
|
---|
629 | }
|
---|
630 | addParentDirs(null, prefix, zOut, "", dirMode);
|
---|
631 | }
|
---|
632 |
|
---|
633 | ZipFile zf = null;
|
---|
634 | try {
|
---|
635 | boolean dealingWithFiles = false;
|
---|
636 | File base = null;
|
---|
637 |
|
---|
638 | if (zfs == null || zfs.getSrc(getProject()) == null) {
|
---|
639 | dealingWithFiles = true;
|
---|
640 | base = fileset.getDir(getProject());
|
---|
641 | } else {
|
---|
642 | zf = new ZipFile(zfs.getSrc(getProject()), encoding);
|
---|
643 | }
|
---|
644 |
|
---|
645 | for (int i = 0; i < resources.length; i++) {
|
---|
646 | String name = null;
|
---|
647 | if (fullpath.length() > 0) {
|
---|
648 | name = fullpath;
|
---|
649 | } else {
|
---|
650 | name = resources[i].getName();
|
---|
651 | }
|
---|
652 | name = name.replace(File.separatorChar, '/');
|
---|
653 |
|
---|
654 | if ("".equals(name)) {
|
---|
655 | continue;
|
---|
656 | }
|
---|
657 | if (resources[i].isDirectory() && !name.endsWith("/")) {
|
---|
658 | name = name + "/";
|
---|
659 | }
|
---|
660 |
|
---|
661 | if (!doFilesonly && !dealingWithFiles
|
---|
662 | && resources[i].isDirectory()
|
---|
663 | && !zfs.hasDirModeBeenSet()) {
|
---|
664 | int nextToLastSlash = name.lastIndexOf("/",
|
---|
665 | name.length() - 2);
|
---|
666 | if (nextToLastSlash != -1) {
|
---|
667 | addParentDirs(base, name.substring(0,
|
---|
668 | nextToLastSlash + 1),
|
---|
669 | zOut, prefix, dirMode);
|
---|
670 | }
|
---|
671 | ZipEntry ze = zf.getEntry(resources[i].getName());
|
---|
672 | addParentDirs(base, name, zOut, prefix, ze.getUnixMode());
|
---|
673 |
|
---|
674 | } else {
|
---|
675 | addParentDirs(base, name, zOut, prefix, dirMode);
|
---|
676 | }
|
---|
677 |
|
---|
678 | if (!resources[i].isDirectory() && dealingWithFiles) {
|
---|
679 | File f = fileUtils.resolveFile(base,
|
---|
680 | resources[i].getName());
|
---|
681 | zipFile(f, zOut, prefix + name, fileMode);
|
---|
682 | } else if (!resources[i].isDirectory()) {
|
---|
683 | ZipEntry ze = zf.getEntry(resources[i].getName());
|
---|
684 |
|
---|
685 | if (ze != null) {
|
---|
686 | boolean oldCompress = doCompress;
|
---|
687 | if (keepCompression) {
|
---|
688 | doCompress = (ze.getMethod() == ZipEntry.DEFLATED);
|
---|
689 | }
|
---|
690 | try {
|
---|
691 | zipFile(zf.getInputStream(ze), zOut, prefix + name,
|
---|
692 | ze.getTime(), zfs.getSrc(getProject()),
|
---|
693 | zfs.hasFileModeBeenSet() ? fileMode
|
---|
694 | : ze.getUnixMode());
|
---|
695 | } finally {
|
---|
696 | doCompress = oldCompress;
|
---|
697 | }
|
---|
698 | }
|
---|
699 | }
|
---|
700 | }
|
---|
701 | } finally {
|
---|
702 | if (zf != null) {
|
---|
703 | zf.close();
|
---|
704 | }
|
---|
705 | }
|
---|
706 | }
|
---|
707 |
|
---|
708 | /**
|
---|
709 | * method for subclasses to override
|
---|
710 | */
|
---|
711 | protected void initZipOutputStream(ZipOutputStream zOut)
|
---|
712 | throws IOException, BuildException {
|
---|
713 | }
|
---|
714 |
|
---|
715 | /**
|
---|
716 | * method for subclasses to override
|
---|
717 | */
|
---|
718 | protected void finalizeZipOutputStream(ZipOutputStream zOut)
|
---|
719 | throws IOException, BuildException {
|
---|
720 | }
|
---|
721 |
|
---|
722 | /**
|
---|
723 | * Create an empty zip file
|
---|
724 | *
|
---|
725 | * @return true for historic reasons
|
---|
726 | */
|
---|
727 | protected boolean createEmptyZip(File zipFile) throws BuildException {
|
---|
728 | // In this case using java.util.zip will not work
|
---|
729 | // because it does not permit a zero-entry archive.
|
---|
730 | // Must create it manually.
|
---|
731 | log("Note: creating empty " + archiveType + " archive " + zipFile,
|
---|
732 | Project.MSG_INFO);
|
---|
733 | OutputStream os = null;
|
---|
734 | try {
|
---|
735 | os = new FileOutputStream(zipFile);
|
---|
736 | // Cf. PKZIP specification.
|
---|
737 | byte[] empty = new byte[22];
|
---|
738 | empty[0] = 80; // P
|
---|
739 | empty[1] = 75; // K
|
---|
740 | empty[2] = 5;
|
---|
741 | empty[3] = 6;
|
---|
742 | // remainder zeros
|
---|
743 | os.write(empty);
|
---|
744 | } catch (IOException ioe) {
|
---|
745 | throw new BuildException("Could not create empty ZIP archive "
|
---|
746 | + "(" + ioe.getMessage() + ")", ioe,
|
---|
747 | getLocation());
|
---|
748 | } finally {
|
---|
749 | if (os != null) {
|
---|
750 | try {
|
---|
751 | os.close();
|
---|
752 | } catch (IOException e) {
|
---|
753 | //ignore
|
---|
754 | }
|
---|
755 | }
|
---|
756 | }
|
---|
757 | return true;
|
---|
758 | }
|
---|
759 |
|
---|
760 | /**
|
---|
761 | * @since Ant 1.5.2
|
---|
762 | */
|
---|
763 | private synchronized ZipScanner getZipScanner() {
|
---|
764 | if (zs == null) {
|
---|
765 | zs = new ZipScanner();
|
---|
766 | zs.setEncoding(encoding);
|
---|
767 | zs.setSrc(zipFile);
|
---|
768 | }
|
---|
769 | return zs;
|
---|
770 | }
|
---|
771 |
|
---|
772 | /**
|
---|
773 | * Collect the resources that are newer than the corresponding
|
---|
774 | * entries (or missing) in the original archive.
|
---|
775 | *
|
---|
776 | * <p>If we are going to recreate the archive instead of updating
|
---|
777 | * it, all resources should be considered as new, if a single one
|
---|
778 | * is. Because of this, subclasses overriding this method must
|
---|
779 | * call <code>super.getResourcesToAdd</code> and indicate with the
|
---|
780 | * third arg if they already know that the archive is
|
---|
781 | * out-of-date.</p>
|
---|
782 | *
|
---|
783 | * @param filesets The filesets to grab resources from
|
---|
784 | * @param zipFile intended archive file (may or may not exist)
|
---|
785 | * @param needsUpdate whether we already know that the archive is
|
---|
786 | * out-of-date. Subclasses overriding this method are supposed to
|
---|
787 | * set this value correctly in their call to
|
---|
788 | * super.getResourcesToAdd.
|
---|
789 | * @return an array of resources to add for each fileset passed in as well
|
---|
790 | * as a flag that indicates whether the archive is uptodate.
|
---|
791 | *
|
---|
792 | * @exception BuildException if it likes
|
---|
793 | */
|
---|
794 | protected ArchiveState getResourcesToAdd(FileSet[] filesets,
|
---|
795 | File zipFile,
|
---|
796 | boolean needsUpdate)
|
---|
797 | throws BuildException {
|
---|
798 |
|
---|
799 | Resource[][] initialResources = grabResources(filesets);
|
---|
800 | if (isEmpty(initialResources)) {
|
---|
801 | if (needsUpdate && doUpdate) {
|
---|
802 | /*
|
---|
803 | * This is a rather hairy case.
|
---|
804 | *
|
---|
805 | * One of our subclasses knows that we need to update the
|
---|
806 | * archive, but at the same time, there are no resources
|
---|
807 | * known to us that would need to be added. Only the
|
---|
808 | * subclass seems to know what's going on.
|
---|
809 | *
|
---|
810 | * This happens if <jar> detects that the manifest has changed,
|
---|
811 | * for example. The manifest is not part of any resources
|
---|
812 | * because of our support for inline <manifest>s.
|
---|
813 | *
|
---|
814 | * If we invoke createEmptyZip like Ant 1.5.2 did,
|
---|
815 | * we'll loose all stuff that has been in the original
|
---|
816 | * archive (bugzilla report 17780).
|
---|
817 | */
|
---|
818 | return new ArchiveState(true, initialResources);
|
---|
819 | }
|
---|
820 |
|
---|
821 | if (emptyBehavior.equals("skip")) {
|
---|
822 | if (doUpdate) {
|
---|
823 | log(archiveType + " archive " + zipFile
|
---|
824 | + " not updated because no new files were included.",
|
---|
825 | Project.MSG_VERBOSE);
|
---|
826 | } else {
|
---|
827 | log("Warning: skipping " + archiveType + " archive "
|
---|
828 | + zipFile + " because no files were included.",
|
---|
829 | Project.MSG_WARN);
|
---|
830 | }
|
---|
831 | } else if (emptyBehavior.equals("fail")) {
|
---|
832 | throw new BuildException("Cannot create " + archiveType
|
---|
833 | + " archive " + zipFile
|
---|
834 | + ": no files were included.",
|
---|
835 | getLocation());
|
---|
836 | } else {
|
---|
837 | // Create.
|
---|
838 | if (!zipFile.exists()) {
|
---|
839 | needsUpdate = true;
|
---|
840 | }
|
---|
841 | }
|
---|
842 | return new ArchiveState(needsUpdate, initialResources);
|
---|
843 | }
|
---|
844 |
|
---|
845 | // initialResources is not empty
|
---|
846 |
|
---|
847 | if (!zipFile.exists()) {
|
---|
848 | return new ArchiveState(true, initialResources);
|
---|
849 | }
|
---|
850 |
|
---|
851 | if (needsUpdate && !doUpdate) {
|
---|
852 | // we are recreating the archive, need all resources
|
---|
853 | return new ArchiveState(true, initialResources);
|
---|
854 | }
|
---|
855 |
|
---|
856 | Resource[][] newerResources = new Resource[filesets.length][];
|
---|
857 |
|
---|
858 | for (int i = 0; i < filesets.length; i++) {
|
---|
859 | if (!(fileset instanceof ZipFileSet)
|
---|
860 | || ((ZipFileSet) fileset).getSrc(getProject()) == null) {
|
---|
861 | File base = filesets[i].getDir(getProject());
|
---|
862 |
|
---|
863 | for (int j = 0; j < initialResources[i].length; j++) {
|
---|
864 | File resourceAsFile =
|
---|
865 | fileUtils.resolveFile(base,
|
---|
866 | initialResources[i][j].getName());
|
---|
867 | if (resourceAsFile.equals(zipFile)) {
|
---|
868 | throw new BuildException("A zip file cannot include "
|
---|
869 | + "itself", getLocation());
|
---|
870 | }
|
---|
871 | }
|
---|
872 | }
|
---|
873 | }
|
---|
874 |
|
---|
875 | for (int i = 0; i < filesets.length; i++) {
|
---|
876 | if (initialResources[i].length == 0) {
|
---|
877 | newerResources[i] = new Resource[] {};
|
---|
878 | continue;
|
---|
879 | }
|
---|
880 |
|
---|
881 | FileNameMapper myMapper = new IdentityMapper();
|
---|
882 | if (filesets[i] instanceof ZipFileSet) {
|
---|
883 | ZipFileSet zfs = (ZipFileSet) filesets[i];
|
---|
884 | if (zfs.getFullpath(getProject()) != null
|
---|
885 | && !zfs.getFullpath(getProject()).equals("")) {
|
---|
886 | // in this case all files from origin map to
|
---|
887 | // the fullPath attribute of the zipfileset at
|
---|
888 | // destination
|
---|
889 | MergingMapper fm = new MergingMapper();
|
---|
890 | fm.setTo(zfs.getFullpath(getProject()));
|
---|
891 | myMapper = fm;
|
---|
892 |
|
---|
893 | } else if (zfs.getPrefix(getProject()) != null
|
---|
894 | && !zfs.getPrefix(getProject()).equals("")) {
|
---|
895 | GlobPatternMapper gm = new GlobPatternMapper();
|
---|
896 | gm.setFrom("*");
|
---|
897 | String prefix = zfs.getPrefix(getProject());
|
---|
898 | if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
|
---|
899 | prefix += "/";
|
---|
900 | }
|
---|
901 | gm.setTo(prefix + "*");
|
---|
902 | myMapper = gm;
|
---|
903 | }
|
---|
904 | }
|
---|
905 |
|
---|
906 | Resource[] resources = initialResources[i];
|
---|
907 | if (doFilesonly) {
|
---|
908 | resources = selectFileResources(resources);
|
---|
909 | }
|
---|
910 |
|
---|
911 | newerResources[i] =
|
---|
912 | ResourceUtils.selectOutOfDateSources(this,
|
---|
913 | resources,
|
---|
914 | myMapper,
|
---|
915 | getZipScanner());
|
---|
916 | needsUpdate = needsUpdate || (newerResources[i].length > 0);
|
---|
917 |
|
---|
918 | if (needsUpdate && !doUpdate) {
|
---|
919 | // we will return initialResources anyway, no reason
|
---|
920 | // to scan further.
|
---|
921 | break;
|
---|
922 | }
|
---|
923 | }
|
---|
924 |
|
---|
925 | if (needsUpdate && !doUpdate) {
|
---|
926 | // we are recreating the archive, need all resources
|
---|
927 | return new ArchiveState(true, initialResources);
|
---|
928 | }
|
---|
929 |
|
---|
930 | return new ArchiveState(needsUpdate, newerResources);
|
---|
931 | }
|
---|
932 |
|
---|
933 | /**
|
---|
934 | * Fetch all included and not excluded resources from the sets.
|
---|
935 | *
|
---|
936 | * <p>Included directories will precede included files.</p>
|
---|
937 | *
|
---|
938 | * @since Ant 1.5.2
|
---|
939 | */
|
---|
940 | protected Resource[][] grabResources(FileSet[] filesets) {
|
---|
941 | Resource[][] result = new Resource[filesets.length][];
|
---|
942 | for (int i = 0; i < filesets.length; i++) {
|
---|
943 | boolean skipEmptyNames = true;
|
---|
944 | if (filesets[i] instanceof ZipFileSet) {
|
---|
945 | ZipFileSet zfs = (ZipFileSet) filesets[i];
|
---|
946 | skipEmptyNames = zfs.getPrefix(getProject()).equals("")
|
---|
947 | && zfs.getFullpath(getProject()).equals("");
|
---|
948 | }
|
---|
949 | DirectoryScanner rs =
|
---|
950 | filesets[i].getDirectoryScanner(getProject());
|
---|
951 | if (rs instanceof ZipScanner) {
|
---|
952 | ((ZipScanner) rs).setEncoding(encoding);
|
---|
953 | }
|
---|
954 | Vector resources = new Vector();
|
---|
955 | String[] directories = rs.getIncludedDirectories();
|
---|
956 | for (int j = 0; j < directories.length; j++) {
|
---|
957 | if (!"".equals(directories[j]) || !skipEmptyNames) {
|
---|
958 | resources.addElement(rs.getResource(directories[j]));
|
---|
959 | }
|
---|
960 | }
|
---|
961 | String[] files = rs.getIncludedFiles();
|
---|
962 | for (int j = 0; j < files.length; j++) {
|
---|
963 | if (!"".equals(files[j]) || !skipEmptyNames) {
|
---|
964 | resources.addElement(rs.getResource(files[j]));
|
---|
965 | }
|
---|
966 | }
|
---|
967 |
|
---|
968 | result[i] = new Resource[resources.size()];
|
---|
969 | resources.copyInto(result[i]);
|
---|
970 | }
|
---|
971 | return result;
|
---|
972 | }
|
---|
973 |
|
---|
974 | /**
|
---|
975 | * @since Ant 1.5.2
|
---|
976 | */
|
---|
977 | protected void zipDir(File dir, ZipOutputStream zOut, String vPath,
|
---|
978 | int mode)
|
---|
979 | throws IOException {
|
---|
980 | zipDir(dir, zOut, vPath, mode, null);
|
---|
981 | }
|
---|
982 |
|
---|
983 | /**
|
---|
984 | * Add a directory to the zip stream.
|
---|
985 | * @param dir the directort to add to the archive
|
---|
986 | * @param zOut the stream to write to
|
---|
987 | * @param vPath the name this entry shall have in the archive
|
---|
988 | * @param mode the Unix permissions to set.
|
---|
989 | * @param extra ZipExtraFields to add
|
---|
990 | * @throws IOException on error
|
---|
991 | * @since Ant 1.6.3
|
---|
992 | */
|
---|
993 | protected void zipDir(File dir, ZipOutputStream zOut, String vPath,
|
---|
994 | int mode, ZipExtraField[] extra)
|
---|
995 | throws IOException {
|
---|
996 | if (addedDirs.get(vPath) != null) {
|
---|
997 | // don't add directories we've already added.
|
---|
998 | // no warning if we try, it is harmless in and of itself
|
---|
999 | return;
|
---|
1000 | }
|
---|
1001 |
|
---|
1002 | log("adding directory " + vPath, Project.MSG_VERBOSE);
|
---|
1003 | addedDirs.put(vPath, vPath);
|
---|
1004 |
|
---|
1005 | if (!skipWriting) {
|
---|
1006 | ZipEntry ze = new ZipEntry (vPath);
|
---|
1007 | if (dir != null && dir.exists()) {
|
---|
1008 | // ZIPs store time with a granularity of 2 seconds, round up
|
---|
1009 | ze.setTime(dir.lastModified() + (roundUp ? 1999 : 0));
|
---|
1010 | } else {
|
---|
1011 | // ZIPs store time with a granularity of 2 seconds, round up
|
---|
1012 | ze.setTime(System.currentTimeMillis() + (roundUp ? 1999 : 0));
|
---|
1013 | }
|
---|
1014 | ze.setSize (0);
|
---|
1015 | ze.setMethod (ZipEntry.STORED);
|
---|
1016 | // This is faintly ridiculous:
|
---|
1017 | ze.setCrc (EMPTY_CRC);
|
---|
1018 | ze.setUnixMode(mode);
|
---|
1019 |
|
---|
1020 | if (extra != null) {
|
---|
1021 | ze.setExtraFields(extra);
|
---|
1022 | }
|
---|
1023 |
|
---|
1024 | zOut.putNextEntry(ze);
|
---|
1025 | }
|
---|
1026 | }
|
---|
1027 |
|
---|
1028 | /**
|
---|
1029 | * Adds a new entry to the archive, takes care of duplicates as well.
|
---|
1030 | *
|
---|
1031 | * @param in the stream to read data for the entry from.
|
---|
1032 | * @param zOut the stream to write to.
|
---|
1033 | * @param vPath the name this entry shall have in the archive.
|
---|
1034 | * @param lastModified last modification time for the entry.
|
---|
1035 | * @param fromArchive the original archive we are copying this
|
---|
1036 | * entry from, will be null if we are not copying from an archive.
|
---|
1037 | * @param mode the Unix permissions to set.
|
---|
1038 | *
|
---|
1039 | * @since Ant 1.5.2
|
---|
1040 | */
|
---|
1041 | protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath,
|
---|
1042 | long lastModified, File fromArchive, int mode)
|
---|
1043 | throws IOException {
|
---|
1044 | if (entries.contains(vPath)) {
|
---|
1045 |
|
---|
1046 | if (duplicate.equals("preserve")) {
|
---|
1047 | log(vPath + " already added, skipping", Project.MSG_INFO);
|
---|
1048 | return;
|
---|
1049 | } else if (duplicate.equals("fail")) {
|
---|
1050 | throw new BuildException("Duplicate file " + vPath
|
---|
1051 | + " was found and the duplicate "
|
---|
1052 | + "attribute is 'fail'.");
|
---|
1053 | } else {
|
---|
1054 | // duplicate equal to add, so we continue
|
---|
1055 | log("duplicate file " + vPath
|
---|
1056 | + " found, adding.", Project.MSG_VERBOSE);
|
---|
1057 | }
|
---|
1058 | } else {
|
---|
1059 | log("adding entry " + vPath, Project.MSG_VERBOSE);
|
---|
1060 | }
|
---|
1061 |
|
---|
1062 | entries.put(vPath, vPath);
|
---|
1063 |
|
---|
1064 | if (!skipWriting) {
|
---|
1065 | ZipEntry ze = new ZipEntry(vPath);
|
---|
1066 | ze.setTime(lastModified);
|
---|
1067 | ze.setMethod(doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED);
|
---|
1068 |
|
---|
1069 | /*
|
---|
1070 | * ZipOutputStream.putNextEntry expects the ZipEntry to
|
---|
1071 | * know its size and the CRC sum before you start writing
|
---|
1072 | * the data when using STORED mode - unless it is seekable.
|
---|
1073 | *
|
---|
1074 | * This forces us to process the data twice.
|
---|
1075 | */
|
---|
1076 | if (!zOut.isSeekable() && !doCompress) {
|
---|
1077 | long size = 0;
|
---|
1078 | CRC32 cal = new CRC32();
|
---|
1079 | if (!in.markSupported()) {
|
---|
1080 | // Store data into a byte[]
|
---|
1081 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
---|
1082 |
|
---|
1083 | byte[] buffer = new byte[8 * 1024];
|
---|
1084 | int count = 0;
|
---|
1085 | do {
|
---|
1086 | size += count;
|
---|
1087 | cal.update(buffer, 0, count);
|
---|
1088 | bos.write(buffer, 0, count);
|
---|
1089 | count = in.read(buffer, 0, buffer.length);
|
---|
1090 | } while (count != -1);
|
---|
1091 | in = new ByteArrayInputStream(bos.toByteArray());
|
---|
1092 |
|
---|
1093 | } else {
|
---|
1094 | in.mark(Integer.MAX_VALUE);
|
---|
1095 | byte[] buffer = new byte[8 * 1024];
|
---|
1096 | int count = 0;
|
---|
1097 | do {
|
---|
1098 | size += count;
|
---|
1099 | cal.update(buffer, 0, count);
|
---|
1100 | count = in.read(buffer, 0, buffer.length);
|
---|
1101 | } while (count != -1);
|
---|
1102 | in.reset();
|
---|
1103 | }
|
---|
1104 | ze.setSize(size);
|
---|
1105 | ze.setCrc(cal.getValue());
|
---|
1106 | }
|
---|
1107 |
|
---|
1108 | ze.setUnixMode(mode);
|
---|
1109 | zOut.putNextEntry(ze);
|
---|
1110 |
|
---|
1111 | byte[] buffer = new byte[8 * 1024];
|
---|
1112 | int count = 0;
|
---|
1113 | do {
|
---|
1114 | if (count != 0) {
|
---|
1115 | zOut.write(buffer, 0, count);
|
---|
1116 | }
|
---|
1117 | count = in.read(buffer, 0, buffer.length);
|
---|
1118 | } while (count != -1);
|
---|
1119 | }
|
---|
1120 | addedFiles.addElement(vPath);
|
---|
1121 | }
|
---|
1122 |
|
---|
1123 | /**
|
---|
1124 | * Method that gets called when adding from java.io.File instances.
|
---|
1125 | *
|
---|
1126 | * <p>This implementation delegates to the six-arg version.</p>
|
---|
1127 | *
|
---|
1128 | * @param file the file to add to the archive
|
---|
1129 | * @param zOut the stream to write to
|
---|
1130 | * @param vPath the name this entry shall have in the archive
|
---|
1131 | * @param mode the Unix permissions to set.
|
---|
1132 | *
|
---|
1133 | * @since Ant 1.5.2
|
---|
1134 | */
|
---|
1135 | protected void zipFile(File file, ZipOutputStream zOut, String vPath,
|
---|
1136 | int mode)
|
---|
1137 | throws IOException {
|
---|
1138 | if (file.equals(zipFile)) {
|
---|
1139 | throw new BuildException("A zip file cannot include itself",
|
---|
1140 | getLocation());
|
---|
1141 | }
|
---|
1142 |
|
---|
1143 | FileInputStream fIn = new FileInputStream(file);
|
---|
1144 | try {
|
---|
1145 | // ZIPs store time with a granularity of 2 seconds, round up
|
---|
1146 | zipFile(fIn, zOut, vPath,
|
---|
1147 | file.lastModified() + (roundUp ? 1999 : 0),
|
---|
1148 | null, mode);
|
---|
1149 | } finally {
|
---|
1150 | fIn.close();
|
---|
1151 | }
|
---|
1152 | }
|
---|
1153 |
|
---|
1154 | /**
|
---|
1155 | * Ensure all parent dirs of a given entry have been added.
|
---|
1156 | *
|
---|
1157 | * @since Ant 1.5.2
|
---|
1158 | */
|
---|
1159 | protected final void addParentDirs(File baseDir, String entry,
|
---|
1160 | ZipOutputStream zOut, String prefix,
|
---|
1161 | int dirMode)
|
---|
1162 | throws IOException {
|
---|
1163 | if (!doFilesonly) {
|
---|
1164 | Stack directories = new Stack();
|
---|
1165 | int slashPos = entry.length();
|
---|
1166 |
|
---|
1167 | while ((slashPos = entry.lastIndexOf('/', slashPos - 1)) != -1) {
|
---|
1168 | String dir = entry.substring(0, slashPos + 1);
|
---|
1169 | if (addedDirs.get(prefix + dir) != null) {
|
---|
1170 | break;
|
---|
1171 | }
|
---|
1172 | directories.push(dir);
|
---|
1173 | }
|
---|
1174 |
|
---|
1175 | while (!directories.isEmpty()) {
|
---|
1176 | String dir = (String) directories.pop();
|
---|
1177 | File f = null;
|
---|
1178 | if (baseDir != null) {
|
---|
1179 | f = new File(baseDir, dir);
|
---|
1180 | } else {
|
---|
1181 | f = new File(dir);
|
---|
1182 | }
|
---|
1183 | zipDir(f, zOut, prefix + dir, dirMode);
|
---|
1184 | }
|
---|
1185 | }
|
---|
1186 | }
|
---|
1187 |
|
---|
1188 | /**
|
---|
1189 | * Do any clean up necessary to allow this instance to be used again.
|
---|
1190 | *
|
---|
1191 | * <p>When we get here, the Zip file has been closed and all we
|
---|
1192 | * need to do is to reset some globals.</p>
|
---|
1193 | *
|
---|
1194 | * <p>This method will only reset globals that have been changed
|
---|
1195 | * during execute(), it will not alter the attributes or nested
|
---|
1196 | * child elements. If you want to reset the instance so that you
|
---|
1197 | * can later zip a completely different set of files, you must use
|
---|
1198 | * the reset method.</p>
|
---|
1199 | *
|
---|
1200 | * @see #reset
|
---|
1201 | */
|
---|
1202 | protected void cleanUp() {
|
---|
1203 | addedDirs.clear();
|
---|
1204 | addedFiles.removeAllElements();
|
---|
1205 | entries.clear();
|
---|
1206 | addingNewFiles = false;
|
---|
1207 | doUpdate = savedDoUpdate;
|
---|
1208 | Enumeration e = filesetsFromGroupfilesets.elements();
|
---|
1209 | while (e.hasMoreElements()) {
|
---|
1210 | ZipFileSet zf = (ZipFileSet) e.nextElement();
|
---|
1211 | filesets.removeElement(zf);
|
---|
1212 | }
|
---|
1213 | filesetsFromGroupfilesets.removeAllElements();
|
---|
1214 | }
|
---|
1215 |
|
---|
1216 | /**
|
---|
1217 | * Makes this instance reset all attributes to their default
|
---|
1218 | * values and forget all children.
|
---|
1219 | *
|
---|
1220 | * @since Ant 1.5
|
---|
1221 | *
|
---|
1222 | * @see #cleanUp
|
---|
1223 | */
|
---|
1224 | public void reset() {
|
---|
1225 | filesets.removeAllElements();
|
---|
1226 | zipFile = null;
|
---|
1227 | baseDir = null;
|
---|
1228 | groupfilesets.removeAllElements();
|
---|
1229 | duplicate = "add";
|
---|
1230 | archiveType = "zip";
|
---|
1231 | doCompress = true;
|
---|
1232 | emptyBehavior = "skip";
|
---|
1233 | doUpdate = false;
|
---|
1234 | doFilesonly = false;
|
---|
1235 | encoding = null;
|
---|
1236 | }
|
---|
1237 |
|
---|
1238 | /**
|
---|
1239 | * @return true if all individual arrays are empty
|
---|
1240 | *
|
---|
1241 | * @since Ant 1.5.2
|
---|
1242 | */
|
---|
1243 | protected static final boolean isEmpty(Resource[][] r) {
|
---|
1244 | for (int i = 0; i < r.length; i++) {
|
---|
1245 | if (r[i].length > 0) {
|
---|
1246 | return false;
|
---|
1247 | }
|
---|
1248 | }
|
---|
1249 | return true;
|
---|
1250 | }
|
---|
1251 |
|
---|
1252 | /**
|
---|
1253 | * Drops all non-file resources from the given array.
|
---|
1254 | *
|
---|
1255 | * @since Ant 1.6
|
---|
1256 | */
|
---|
1257 | protected Resource[] selectFileResources(Resource[] orig) {
|
---|
1258 | if (orig.length == 0) {
|
---|
1259 | return orig;
|
---|
1260 | }
|
---|
1261 |
|
---|
1262 | Vector v = new Vector(orig.length);
|
---|
1263 | for (int i = 0; i < orig.length; i++) {
|
---|
1264 | if (!orig[i].isDirectory()) {
|
---|
1265 | v.addElement(orig[i]);
|
---|
1266 | } else {
|
---|
1267 | log("Ignoring directory " + orig[i].getName()
|
---|
1268 | + " as only files will be added.", Project.MSG_VERBOSE);
|
---|
1269 | }
|
---|
1270 | }
|
---|
1271 |
|
---|
1272 | if (v.size() != orig.length) {
|
---|
1273 | Resource[] r = new Resource[v.size()];
|
---|
1274 | v.copyInto(r);
|
---|
1275 | return r;
|
---|
1276 | }
|
---|
1277 | return orig;
|
---|
1278 | }
|
---|
1279 |
|
---|
1280 | /**
|
---|
1281 | * Possible behaviors when a duplicate file is added:
|
---|
1282 | * "add", "preserve" or "fail"
|
---|
1283 | */
|
---|
1284 | public static class Duplicate extends EnumeratedAttribute {
|
---|
1285 | public String[] getValues() {
|
---|
1286 | return new String[] {"add", "preserve", "fail"};
|
---|
1287 | }
|
---|
1288 | }
|
---|
1289 |
|
---|
1290 | /**
|
---|
1291 | * Holds the up-to-date status and the out-of-date resources of
|
---|
1292 | * the original archive.
|
---|
1293 | *
|
---|
1294 | * @since Ant 1.5.3
|
---|
1295 | */
|
---|
1296 | public static class ArchiveState {
|
---|
1297 | private boolean outOfDate;
|
---|
1298 | private Resource[][] resourcesToAdd;
|
---|
1299 |
|
---|
1300 | ArchiveState(boolean state, Resource[][] r) {
|
---|
1301 | outOfDate = state;
|
---|
1302 | resourcesToAdd = r;
|
---|
1303 | }
|
---|
1304 |
|
---|
1305 | public boolean isOutOfDate() {
|
---|
1306 | return outOfDate;
|
---|
1307 | }
|
---|
1308 |
|
---|
1309 | public Resource[][] getResourcesToAdd() {
|
---|
1310 | return resourcesToAdd;
|
---|
1311 | }
|
---|
1312 | /**
|
---|
1313 | * find out if there are absolutely no resources to add
|
---|
1314 | * @since Ant 1.6.3
|
---|
1315 | * @return true if there are no resources to add
|
---|
1316 | */
|
---|
1317 | public boolean isWithoutAnyResources() {
|
---|
1318 | if (resourcesToAdd == null) {
|
---|
1319 | return true;
|
---|
1320 | }
|
---|
1321 | for (int counter = 0; counter < resourcesToAdd.length; counter++) {
|
---|
1322 | if (resourcesToAdd[counter] != null) {
|
---|
1323 | if (resourcesToAdd[counter].length > 0) {
|
---|
1324 | return false;
|
---|
1325 | }
|
---|
1326 | }
|
---|
1327 | }
|
---|
1328 | return true;
|
---|
1329 | }
|
---|
1330 | }
|
---|
1331 | }
|
---|