001: /*
002: * @(#)GroboReZipTask.java
003: *
004: * Copyright (C) 2004 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.ant;
028:
029: import java.io.ByteArrayInputStream;
030: import java.io.ByteArrayOutputStream;
031: import java.io.File;
032: import java.io.FileInputStream;
033: import java.io.FileOutputStream;
034: import java.io.IOException;
035: import java.io.InputStream;
036: import java.util.Enumeration;
037: import java.util.Stack;
038: import java.util.Vector;
039: import java.util.zip.CRC32;
040:
041: import org.apache.tools.ant.BuildException;
042: import org.apache.tools.ant.DirectoryScanner;
043: import org.apache.tools.ant.Project;
044: import org.apache.tools.ant.Task;
045: import org.apache.tools.ant.types.FileSet;
046: import org.apache.tools.ant.types.PatternSet; //import org.apache.tools.ant.types.Resource;
047: import org.apache.tools.ant.util.FileUtils;
048:
049: // We have to use the Ant ZipScanner due to the Reference type.
050: // But it's easy to avoid this...
051: //import org.apache.tools.ant.types.ZipScanner;
052:
053: // use our own re-packaging of the Apache zip utility
054: import net.sourceforge.groboutils.codecoverage.v2.ant.zip.ZipEntry;
055: import net.sourceforge.groboutils.codecoverage.v2.ant.zip.ZipOutputStream;
056: import net.sourceforge.groboutils.codecoverage.v2.ant.zip.ZipFile;
057: import net.sourceforge.groboutils.codecoverage.v2.ant.zip.ZipFileSet;
058:
059: /**
060: * Similar to the Zip task, but its purpose is to modify not only
061: * one zip file, but possibly many containing zip files as well.
062: * Also, should have meta-inf support for modifying the classpath to
063: * add in the runtime jar file.
064: *
065: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
066: * @version $Date: 2004/07/08 05:29:19 $
067: * @since March 23, 2004
068: */
069: public class GroboReZipTask extends Task {
070: private boolean failOnError = true;
071: private Vector zips = new Vector();
072:
073: private static final int BUFFER_SIZE = 8 * 1024;
074: private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
075:
076: private static final FileUtils FILEUTILS = FileUtils.newFileUtils();
077: private static final long EMPTY_CRC = new CRC32().getValue();
078:
079: public static class ReZipFileSet extends ZipFileSet {
080: private boolean replaceOnly = false;
081:
082: public ReZipFileSet() {
083: super ();
084: }
085:
086: public ReZipFileSet(FileSet fs) {
087: super (fs);
088: }
089:
090: public ReZipFileSet(ZipFileSet zfs) {
091: super (zfs);
092: }
093:
094: public ReZipFileSet(ReZipFileSet rzfs) {
095: super ((ZipFileSet) rzfs);
096: this .replaceOnly = rzfs.replaceOnly;
097: }
098:
099: /**
100: * If this is set to true, then only the files in this fileset
101: * that already exist in the owning zip file will be added, all
102: * others in this fileset will be ignored.
103: */
104: public void setReplaceOnly(boolean replace) {
105: this .replaceOnly = replace;
106: }
107:
108: public boolean getReplaceOnly(Project p) {
109: if (isReference()) {
110: return ((ReZipFileSet) getRef(p)).getReplaceOnly(p);
111: }
112: return this .replaceOnly;
113: }
114: }
115:
116: /** the base type for altering a zip file. */
117: public static class AlterZip {
118: private Vector subZip = new Vector();
119: private Vector fileSets = new Vector();
120: private Vector alter = new Vector();
121: private String srcFile;
122: private String destFile;
123: String encoding;
124: boolean doCompress = true;
125:
126: public void setSrc(String f) {
127: this .srcFile = f;
128: }
129:
130: public void setDest(String f) {
131: this .destFile = f;
132: }
133:
134: public void addFileSet(ReZipFileSet zfs) {
135: if (zfs != null) {
136: this .fileSets.addElement(zfs);
137: }
138: }
139:
140: public void addZipFileSet(ReZipFileSet zfs) {
141: if (zfs != null) {
142: this .fileSets.addElement(zfs);
143: }
144: }
145:
146: public void addAlterZip(AlterZip z) {
147: if (z != null) {
148: this .subZip.addElement(z);
149: }
150: }
151:
152: public void addAlterWar(AlterWar z) {
153: if (z != null) {
154: this .subZip.addElement(z);
155: }
156: }
157:
158: public void addAlterEar(AlterEar z) {
159: if (z != null) {
160: this .subZip.addElement(z);
161: }
162: }
163:
164: public void addAlterJar(AlterJar z) {
165: if (z != null) {
166: this .subZip.addElement(z);
167: }
168: }
169:
170: protected void _addModifyEntry(ModifyEntry me) {
171: if (me != null) {
172: this .alter.addElement(me);
173: }
174: }
175:
176: public File getAbsoluteSrc(Project p) {
177: return p.resolveFile(this .srcFile);
178: }
179:
180: public File getAbsoluteDest(Project p) {
181: return p.resolveFile(this .destFile);
182: }
183:
184: public String getSrc() {
185: return this .srcFile;
186: }
187:
188: public String getDest() {
189: return this .destFile;
190: }
191:
192: public boolean hasDest() {
193: return (this .destFile != null);
194: }
195:
196: public AlterZip[] getSubZips() {
197: AlterZip az[] = new AlterZip[this .subZip.size()];
198: this .subZip.copyInto(az);
199: return az;
200: }
201:
202: public FileSet[] getFileSets() {
203: FileSet fs[] = new FileSet[this .fileSets.size()];
204: this .fileSets.copyInto(fs);
205: return fs;
206: }
207:
208: public ModifyEntry[] getModifyEntries() {
209: Vector entries = new Vector();
210: Enumeration e = this .alter.elements();
211: while (e.hasMoreElements()) {
212: entries.addElement(e.nextElement());
213: }
214: e = this .subZip.elements();
215: while (e.hasMoreElements()) {
216: AlterZip az = (AlterZip) e.nextElement();
217: entries.addElement(new AlterZipModifyEntry(az));
218: }
219: ModifyEntry me[] = new ModifyEntry[entries.size()];
220: entries.copyInto(me);
221: return me;
222: }
223:
224: public ModifyEntry shouldModify(String r) {
225: ModifyEntry[] me = getModifyEntries();
226: for (int i = 0; i < me.length; ++i) {
227: if (me[i].shouldModify(r)) {
228: return me[i];
229: }
230: }
231: return null;
232: }
233: }
234:
235: /**
236: * Tells the processing system that a particular file needs to
237: * be modified.
238: */
239: public static abstract class ModifyEntry {
240: private String filename;
241:
242: public ModifyEntry(String src) {
243: this .filename = src;
244: }
245:
246: public boolean shouldModify(String r) {
247: return (this .filename.equals(r));
248: }
249:
250: public abstract void modify(GroboReZipTask grze,
251: InputStream in, File outfile) throws IOException;
252: }
253:
254: /**
255: * Recursively alter the inner zip file, by writing its contents
256: * to temporary files.
257: */
258: public static class AlterZipModifyEntry extends ModifyEntry {
259: private AlterZip az;
260:
261: public AlterZipModifyEntry(AlterZip az) {
262: super (az.srcFile);
263: this .az = az;
264: }
265:
266: public void modify(GroboReZipTask grze, InputStream in,
267: File outfile) throws IOException {
268: File src = FILEUTILS.createTempFile("z", ".zip", null);
269: src.deleteOnExit();
270: FileOutputStream fos = new FileOutputStream(src);
271: byte buff[] = new byte[BUFFER_SIZE];
272: try {
273: int size = in.read(buff, 0, BUFFER_SIZE);
274: while (size > 0) {
275: fos.write(buff, 0, size);
276: size = in.read(buff, 0, BUFFER_SIZE);
277: }
278: } finally {
279: fos.close();
280: }
281:
282: try {
283: grze.processZip(this .az, src, outfile);
284: } finally {
285: delete(src);
286: }
287: }
288: }
289:
290: public static class AlterEar extends AlterZip {
291: // currently, this is exactly the same.
292: // we may want to alter the application.xml file
293: // in the future.
294: }
295:
296: public static class AlterJar extends AlterZip {
297: // currently, this is exactly the same.
298: // we may want to alter the MANIFEST.MF file
299: // in the future, to possibly append the classpath entry.
300: // This should be based on a 'addLib' like syntax.
301: }
302:
303: public static class AlterWar extends AlterZip {
304: public void addClasses(ReZipFileSet zfs) {
305: zfs.setPrefix("WEB-INF/classes/");
306: zfs.setReplaceOnly(true);
307: addZipFileSet(zfs);
308: }
309:
310: public void addLib(ReZipFileSet zfs) {
311: zfs.setPrefix("WEB-INF/lib/");
312: addZipFileSet(zfs);
313: }
314: }
315:
316: //------------------------------------------------------------------
317:
318: public void addAlterZip(AlterZip z) {
319: if (z != null) {
320: this .zips.addElement(z);
321: }
322: }
323:
324: public void addAlterWar(AlterWar z) {
325: if (z != null) {
326: this .zips.addElement(z);
327: }
328: }
329:
330: public void addAlterEar(AlterEar z) {
331: if (z != null) {
332: this .zips.addElement(z);
333: }
334: }
335:
336: public void addAlterJar(AlterJar z) {
337: if (z != null) {
338: this .zips.addElement(z);
339: }
340: }
341:
342: //------------------------------------------------------------------
343:
344: public void execute() throws BuildException {
345: // the top level zips act a bit differently than the rest -
346: // their files are absolute.
347:
348: Enumeration e = this .zips.elements();
349: while (e.hasMoreElements()) {
350: AlterZip az = (AlterZip) e.nextElement();
351: File src = az.getAbsoluteSrc(getProject());
352: if (src == null) {
353: String msg = "No source specified for zip.";
354: if (this .failOnError) {
355: throw new BuildException(msg);
356: } else {
357: log(msg, Project.MSG_WARN);
358: }
359: }
360: File dest = null;
361: boolean isTemp = false;
362: if (az.hasDest()) {
363: dest = az.getAbsoluteDest(getProject());
364: if (dest.equals(src)) {
365: // replacing the original file...
366: dest = null;
367: }
368: }
369: if (dest == null) {
370: isTemp = true;
371: dest = FILEUTILS.createTempFile("z", ".zip", null);
372: dest.deleteOnExit();
373: }
374:
375: log("Altering [" + src.getAbsolutePath() + "] into ["
376: + dest.getAbsolutePath() + "]", Project.MSG_INFO);
377:
378: try {
379: processZip(az, src, dest);
380:
381: if (isTemp) {
382: // replace the original file.
383: FILEUTILS.copyFile(dest, src);
384: System.gc();
385: System.runFinalization();
386: delete(dest);
387: }
388: } catch (SecurityException se) {
389: throw new BuildException(
390: "Not allowed to rename temporary file to old file ("
391: + src.getAbsolutePath() + ")");
392: } catch (IOException ioe) {
393: String msg = "Problem processing zip file "
394: + src.getAbsolutePath() + ": "
395: + ioe.getMessage();
396: if (this .failOnError) {
397: throw new BuildException(msg, ioe);
398: } else {
399: log(msg, Project.MSG_WARN);
400: }
401: } catch (BuildException be) {
402: if (this .failOnError) {
403: throw be;
404: } else {
405: log(be.getMessage(), Project.MSG_WARN);
406: }
407: }
408: }
409: }
410:
411: //------------------------------------------------------------------
412:
413: public void processZip(AlterZip az, File src, File dest)
414: throws BuildException, IOException {
415: ZipFile inzip = new ZipFile(src);
416: az.encoding = inzip.getEncoding();
417: Vector originalFiles = new Vector();
418: Enumeration entries = inzip.getEntries();
419: while (entries.hasMoreElements()) {
420: ZipEntry ze = (ZipEntry) entries.nextElement();
421: originalFiles.addElement(ze.getName());
422: }
423: entries = null;
424: inzip.close();
425: inzip = null;
426:
427: ZipOutputStream zOut = new ZipOutputStream(
428: new FileOutputStream(dest));
429: zOut.setEncoding(az.encoding);
430:
431: Vector addedFiles = new Vector();
432: addFiles(zOut, az.getFileSets(), az, addedFiles, originalFiles);
433:
434: log("Adding contents of original zip [" + src.getAbsolutePath()
435: + "]", Project.MSG_VERBOSE);
436: ReZipFileSet oldFiles = new ReZipFileSet();
437: oldFiles.setProject(getProject());
438: oldFiles.setSrc(src);
439: oldFiles.setReplaceOnly(false);
440: /*
441: Enumeration af = addedFiles.elements();
442: while (af.hasMoreElements())
443: {
444: PatternSet.NameEntry ne = oldFiles.createExclude();
445: ne.setName( (String)af.nextElement() );
446: }
447: */
448: addFiles(zOut, oldFiles, az, addedFiles, originalFiles);
449:
450: finalizeZipOutputStream(zOut);
451: }
452:
453: private void addFiles(ZipOutputStream zOut, FileSet[] filesets,
454: AlterZip az, Vector addedFiles, Vector originalFiles)
455: throws IOException, BuildException {
456: for (int i = 0; i < filesets.length; ++i) {
457: addFiles(zOut, filesets[i], az, addedFiles, originalFiles);
458: }
459: }
460:
461: private void addFiles(ZipOutputStream zOut, FileSet fileset,
462: AlterZip az, Vector addedFiles, Vector originalFiles)
463: throws IOException, BuildException {
464: DirectoryScanner ds = fileset.getDirectoryScanner(getProject());
465: //if (ds instanceof ZipScanner)
466: //{
467: // ((ZipScanner) ds).setEncoding( az.encoding );
468: //}
469: ds.scan();
470: String[] f = ds.getIncludedFiles();
471: if (f.length > 0) {
472: addResources(az, fileset, f, false, zOut, addedFiles,
473: originalFiles);
474: }
475:
476: String[] d = ds.getIncludedDirectories();
477: if (d.length > 0) {
478: addResources(az, fileset, d, true, zOut, addedFiles,
479: originalFiles);
480: }
481: }
482:
483: /**
484: * Add the given resources. Ripped from Ant's Zip task.
485: *
486: * @param fileset may give additional information like fullpath or
487: * permissions.
488: * @param resources the resources to add
489: * @param zOut the stream to write to
490: *
491: * @since Ant 1.5.2
492: */
493: protected final void addResources(AlterZip az, FileSet fileset,
494: String[] resources, boolean areDirs, ZipOutputStream zOut,
495: Vector addedFiles, Vector originalFiles) throws IOException {
496: String prefix = "";
497: String fullpath = "";
498: int dirMode = ZipFileSet.DEFAULT_DIR_MODE;
499: int fileMode = ZipFileSet.DEFAULT_FILE_MODE;
500: boolean replaceOnly = false;
501:
502: ZipFileSet zfs = null;
503: if (fileset instanceof ZipFileSet) {
504: zfs = (ZipFileSet) fileset;
505: prefix = zfs.getPrefix(getProject());
506: fullpath = zfs.getFullpath(getProject());
507: dirMode = zfs.getDirMode(getProject());
508: fileMode = zfs.getFileMode(getProject());
509: if (fileset instanceof ReZipFileSet) {
510: replaceOnly = ((ReZipFileSet) fileset)
511: .getReplaceOnly(getProject());
512: }
513: log(
514: "Processing resources from ZipFileSet: prefix=["
515: + prefix + "]; fullpath=[" + fullpath
516: + "]; dirMode=[" + dirMode
517: + "]; fileMode=[" + fileMode
518: + "]; replaceOnly=[" + replaceOnly + "]",
519: Project.MSG_DEBUG);
520: }
521:
522: if (prefix.length() > 0 && fullpath.length() > 0) {
523: throw new BuildException(
524: "Both prefix and fullpath attributes must"
525: + " not be set on the same fileset.");
526: }
527:
528: if (resources.length != 1 && fullpath.length() > 0) {
529: throw new BuildException(
530: "fullpath attribute may only be specified"
531: + " for filesets that specify a single"
532: + " file.");
533: }
534:
535: if (prefix.length() > 0) {
536: if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
537: prefix += "/";
538: }
539: addParentDirs(null, prefix, zOut, "", dirMode, addedFiles,
540: replaceOnly);
541: }
542:
543: ZipFile zf = null;
544: try {
545: boolean dealingWithFiles = false;
546: File base = null;
547: if (zfs == null || zfs.getSrc(getProject()) == null) {
548: dealingWithFiles = true;
549: base = fileset.getDir(getProject());
550: log("Dealing with files (base=[" + base + "])",
551: Project.MSG_DEBUG);
552: } else {
553: File src = zfs.getSrc(getProject());
554: zf = new ZipFile(src, az.encoding);
555: log("Dealing with zipFile (src=["
556: + src.getAbsolutePath() + "])",
557: Project.MSG_DEBUG);
558: }
559:
560: for (int i = 0; i < resources.length; i++) {
561: log("Processing resource [" + resources[i] + "]",
562: Project.MSG_DEBUG);
563: String name = null;
564: if (fullpath.length() > 0) {
565: name = fullpath;
566: } else {
567: name = resources[i];
568: }
569: name = name.replace(File.separatorChar, '/');
570:
571: if ("".equals(name)) {
572: log("Empty name - continuing.", Project.MSG_DEBUG);
573: continue;
574: }
575: if (areDirs && !name.endsWith("/")) {
576: name = name + "/";
577: }
578: String prefixName = prefix + name;
579: log("Output Zip entry name = [" + prefixName + "]",
580: Project.MSG_DEBUG);
581:
582: if (replaceOnly && !originalFiles.contains(prefixName)) {
583: // only add the file when the original Zip contains it.
584: log(prefixName
585: + " not in original zip, so skip it.",
586: Project.MSG_VERBOSE);
587: continue;
588: }
589:
590: if (!dealingWithFiles && areDirs
591: && !zfs.hasDirModeBeenSet()) {
592: log("Adding directory's path into zip",
593: Project.MSG_DEBUG);
594: int nextToLastSlash = name.lastIndexOf("/", name
595: .length() - 2);
596: if (nextToLastSlash != -1) {
597: addParentDirs(base, name.substring(0,
598: nextToLastSlash + 1), zOut, prefix,
599: dirMode, addedFiles, replaceOnly);
600: }
601: ZipEntry ze = zf.getEntry(resources[i]);
602: addParentDirs(base, name, zOut, prefix, ze
603: .getUnixMode(), addedFiles, replaceOnly);
604: } else {
605: log("Adding file's path into zip",
606: Project.MSG_DEBUG);
607: addParentDirs(base, name, zOut, prefix, dirMode,
608: addedFiles, replaceOnly);
609: }
610:
611: if (!areDirs) {
612: if (dealingWithFiles) {
613: File f = FILEUTILS.resolveFile(base,
614: resources[i]);
615: // only modify files that already exist in the
616: // zip file.
617: zipFile(az, f, zOut, prefixName, fileMode,
618: addedFiles);
619: } else {
620: ZipEntry ze = zf.getEntry(resources[i]);
621:
622: if (ze != null) {
623: log("Inserting zip entry [" + resources[i]
624: + "]", Project.MSG_DEBUG);
625: boolean oldCompress = az.doCompress;
626: az.doCompress = (ze.getMethod() == ZipEntry.DEFLATED);
627: ModifyEntry me = az
628: .shouldModify(resources[i]);
629: try {
630: zipFile(
631: az,
632: me,
633: zf.getInputStream(ze),
634: zOut,
635: prefixName,
636: ze.getTime(),
637: zfs.getSrc(getProject()),
638: zfs.hasFileModeBeenSet() ? fileMode
639: : ze.getUnixMode(),
640: addedFiles);
641: } finally {
642: az.doCompress = oldCompress;
643: }
644: }
645: }
646: }
647: }
648: } finally {
649: if (zf != null) {
650: zf.close();
651: }
652: }
653: }
654:
655: /**
656: * Ripped from Ant's Zip task.
657: *
658: * Ensure all parent dirs of a given entry have been added.
659: *
660: * @since Ant 1.5.2
661: */
662: protected final void addParentDirs(File baseDir, String entry,
663: ZipOutputStream zOut, String prefix, int dirMode,
664: Vector addedFiles, boolean replaceOnly) throws IOException {
665: // note: if replaceOnly == true, then any directories added would
666: // mean that for them to be added, they must already exist in
667: // the original zip file. If that's the case, then the directory
668: // will be added anyway. Therefore we don't need to add the
669: // directory if replaceOnly == true.
670: if (replaceOnly) {
671: return;
672: }
673:
674: Stack directories = new Stack();
675: int slashPos = entry.length();
676:
677: while ((slashPos = entry.lastIndexOf('/', slashPos - 1)) != -1) {
678: String dir = entry.substring(0, slashPos + 1);
679: if (addedFiles.contains(prefix + dir)) {
680: break;
681: }
682: directories.push(dir);
683: }
684:
685: while (!directories.isEmpty()) {
686: String dir = (String) directories.pop();
687: File f = null;
688: if (baseDir != null) {
689: f = new File(baseDir, dir);
690: } else {
691: f = new File(dir);
692: }
693:
694: zipDir(f, zOut, prefix + dir, dirMode, addedFiles);
695: }
696: }
697:
698: /**
699: * Ripped from Ant's Zip task.
700: *
701: * @since Ant 1.5.2
702: */
703: protected void zipDir(File dir, ZipOutputStream zOut, String vPath,
704: int mode, Vector addedFiles) throws IOException {
705: if (addedFiles.contains(vPath)) {
706: // don't add directories we've already added.
707: // no warning if we try, it is harmless in and of itself
708: return;
709: }
710:
711: log("adding directory " + vPath, Project.MSG_VERBOSE);
712: addedFiles.addElement(vPath);
713:
714: ZipEntry ze = new ZipEntry(vPath);
715: if (dir != null && dir.exists()) {
716: // ZIPs store time with a granularity of 2 seconds, round up
717: ze.setTime(dir.lastModified() + 1999);
718: } else {
719: // ZIPs store time with a granularity of 2 seconds, round up
720: ze.setTime(System.currentTimeMillis() + 1999);
721: }
722: ze.setSize(0);
723: ze.setMethod(ZipEntry.STORED);
724: // This is faintly ridiculous:
725: ze.setCrc(EMPTY_CRC);
726: ze.setUnixMode(mode);
727:
728: zOut.putNextEntry(ze);
729: }
730:
731: /**
732: * Ripped from Ant's Zip task.
733: * Method that gets called when adding from java.io.File instances.
734: *
735: * <p>This implementation delegates to the six-arg version.</p>
736: *
737: * @param file the file to add to the archive
738: * @param zOut the stream to write to
739: * @param vPath the name this entry shall have in the archive
740: * @param mode the Unix permissions to set.
741: *
742: * @since Ant 1.5.2
743: */
744: protected void zipFile(AlterZip az, File file,
745: ZipOutputStream zOut, String vPath, int mode,
746: Vector addedFiles) throws IOException {
747: /*
748: if (file.equals(zipFile))
749: {
750: throw new BuildException("A zip file cannot include itself",
751: getLocation());
752: }
753: */
754: log("zipFile( vPath = [" + vPath + "]; file = ["
755: + file.getAbsolutePath() + "] )", Project.MSG_DEBUG);
756: FileInputStream fIn = new FileInputStream(file);
757: try {
758: // ZIPs store time with a granularity of 2 seconds, round up
759: zipFile(az, null, fIn, zOut, vPath,
760: file.lastModified() + 1999, null, mode, addedFiles);
761: } finally {
762: fIn.close();
763: }
764: }
765:
766: /**
767: * Ripped from Ant's Zip task.
768: *
769: * Adds a new entry to the archive, takes care of duplicates as well.
770: *
771: * @param in the stream to read data for the entry from.
772: * @param zOut the stream to write to.
773: * @param vPath the name this entry shall have in the archive.
774: * @param lastModified last modification time for the entry.
775: * @param fromArchive the original archive we are copying this
776: * entry from, will be null if we are not copying from an archive.
777: * @param mode the Unix permissions to set.
778: *
779: * @since Ant 1.5.2
780: */
781: protected void zipFile(AlterZip az, ModifyEntry me, InputStream in,
782: ZipOutputStream zOut, String vPath, long lastModified,
783: File fromArchive, int mode, Vector addedFiles)
784: throws IOException {
785: if (addedFiles.contains(vPath)) {
786: log(vPath + " already added, skipping", Project.MSG_VERBOSE);
787: return;
788: /*
789: if (duplicate.equals("preserve")) {
790: log(vPath + " already added, skipping", Project.MSG_INFO);
791: return;
792: } else if (duplicate.equals("fail")) {
793: throw new BuildException("Duplicate file " + vPath
794: + " was found and the duplicate "
795: + "attribute is 'fail'.");
796: } else {
797: // duplicate equal to add, so we continue
798: log("duplicate file " + vPath
799: + " found, adding.", Project.MSG_VERBOSE);
800: }
801: */
802: } else {
803: log("adding entry " + vPath, Project.MSG_VERBOSE);
804: }
805:
806: ZipEntry ze = new ZipEntry(vPath);
807: ze.setTime(lastModified);
808: ze.setMethod(az.doCompress ? ZipEntry.DEFLATED
809: : ZipEntry.STORED);
810:
811: // should this zip file be modified?
812: File tmpFile = null;
813: try {
814: if (me != null) {
815: tmpFile = FILEUTILS.createTempFile("z", ".tmp", null);
816: tmpFile.deleteOnExit();
817:
818: me.modify(this , in, tmpFile);
819:
820: in = new FileInputStream(tmpFile);
821: }
822:
823: /*
824: * ZipOutputStream.putNextEntry expects the ZipEntry to
825: * know its size and the CRC sum before you start writing
826: * the data when using STORED mode - unless it is seekable.
827: *
828: * This forces us to process the data twice.
829: */
830: if (!zOut.isSeekable() && !az.doCompress) {
831: long size = 0;
832: CRC32 cal = new CRC32();
833: if (!in.markSupported()) {
834: // Store data into a byte[]
835: ByteArrayOutputStream bos = new ByteArrayOutputStream();
836:
837: byte[] buffer = new byte[8 * 1024];
838: int count = 0;
839: do {
840: size += count;
841: cal.update(buffer, 0, count);
842: bos.write(buffer, 0, count);
843: count = in.read(buffer, 0, buffer.length);
844: } while (count != -1);
845: in = new ByteArrayInputStream(bos.toByteArray());
846: } else {
847: in.mark(Integer.MAX_VALUE);
848: byte[] buffer = new byte[8 * 1024];
849: int count = 0;
850: do {
851: size += count;
852: cal.update(buffer, 0, count);
853: count = in.read(buffer, 0, buffer.length);
854: } while (count != -1);
855: in.reset();
856: }
857: ze.setSize(size);
858: ze.setCrc(cal.getValue());
859: }
860:
861: ze.setUnixMode(mode);
862: zOut.putNextEntry(ze);
863:
864: byte[] buffer = new byte[BUFFER_SIZE];
865: int count = 0;
866: do {
867: if (count != 0) {
868: zOut.write(buffer, 0, count);
869: }
870: count = in.read(buffer, 0, BUFFER_SIZE);
871: } while (count != -1);
872:
873: addedFiles.addElement(vPath);
874: } finally {
875: // close the input stream before deleting the temp file.
876: if (in != null) {
877: in.close();
878: }
879:
880: if (tmpFile != null) {
881: delete(tmpFile);
882: }
883: }
884: }
885:
886: /**
887: * method for subclasses to override
888: */
889: protected void initZipOutputStream(ZipOutputStream zOut)
890: throws IOException, BuildException {
891: }
892:
893: /**
894: * method for subclasses to override
895: */
896: protected void finalizeZipOutputStream(ZipOutputStream zOut)
897: throws IOException, BuildException {
898: zOut.finish();
899: }
900:
901: /**
902: * (pulled from the ant Delete task)
903: * Attempt to fix possible race condition when deleting
904: * files on WinXP. If the delete does not work,
905: * wait a little and try again.
906: */
907: private static boolean delete(File f) {
908: if (!f.delete()) {
909: try {
910: Thread.sleep(DELETE_RETRY_SLEEP_MILLIS);
911: } catch (InterruptedException ex) {
912: // ignore
913: }
914: return f.delete();
915: }
916: return true;
917: }
918: }
|