001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.sandbox.utils;
038:
039: import java.io.ByteArrayInputStream;
040: import java.io.ByteArrayOutputStream;
041: import java.io.FileInputStream;
042: import java.io.FileNotFoundException;
043: import java.io.OutputStream;
044: import java.util.ArrayList;
045: import java.util.zip.ZipFile;
046: import java.util.zip.ZipInputStream;
047: import java.util.zip.ZipEntry;
048: import java.util.zip.ZipException;
049: import java.io.File;
050: import java.io.FileFilter;
051: import java.io.FileOutputStream;
052: import java.io.IOException;
053: import java.util.Map;
054: import java.util.List;
055: import java.util.jar.JarFile;
056: import java.util.jar.JarOutputStream;
057: import java.util.jar.Pack200;
058: import java.util.jar.Pack200.Packer;
059: import java.util.jar.Pack200.Unpacker;
060: import org.netbeans.installer.utils.helper.ErrorLevel;
061: import org.netbeans.installer.utils.progress.Progress;
062:
063: /**
064: *
065: * @author Dmitry Lipin
066: */
067: public class ZipPackUtils {
068: /////////////////////////////////////////////////////////////////////////////////
069: // Constants
070: public static final String PACK_SUFFIX = ".pack.gz"; //NOI18N
071: public static final String ZIP_SUFFIX = ".zip"; //NOI18N
072:
073: private static final PackedFilter PACKED_FILTER = new PackedFilter(
074: PACK_SUFFIX);
075: private static final JarsFilter JAR_FILTER = new JarsFilter();
076:
077: private static final int BUFFER_SIZE = 102400;
078:
079: /////////////////////////////////////////////////////////////////////////////////
080: // Static
081: private static Packer packer = null;
082: private static Unpacker unpacker = Pack200.newUnpacker();
083:
084: /**
085: * Unzip file <b>filename</b> to the directory <b>outpuDir</b>.
086: *
087: * @return List of unzipped files (<i>null</i> if filename or outputDir is <i>null</i>)
088: * @param zipfile ZIP file
089: * @param outputDir Directory for ZIP file to be extracted
090: * @param unpack Unpack or not packed files (.pack.gz)
091: * @param useZipModificationTime Set modification time of unzipped file to zip entry time
092: * @param progress
093: * Progress instance to display detail and set percentage.
094: * <br>If <b>progress</b> is <i>null</i> then nothing would be displayed
095: * @param conditionList List of conditions to be added to every item of
096: * returning InstallationFileObject
097: */
098: public static List<File> unzip(File zipfile, String outputDir,
099: boolean unpack, boolean useZipModificationTime,
100: Progress progress) {
101: return unzip(zipfile.getPath(), outputDir, unpack,
102: useZipModificationTime, progress);
103: }
104:
105: /**
106: * Unzip file <b>filename</b> to the directory <b>outpuDir</b>.
107: *
108: * @return List of unzipped files.
109: * If filename or outputDir is <i>null</i> then return <i>null</i>.
110: * @param zipfile ZIP file name
111: * @param outputDir Directory for ZIP file to be extracted
112: * @param unpack Unpack or not packed files (.pack.gz)
113: * @param useZipModificationTime
114: * Set modification time of unzipped file to zip entry time
115: * @param progress
116: * Progress instance to display detail and set percentage.
117: * <br>If <b>progress</b> is <i>null</i> then nothing would be
118: * displayed.
119: */
120: public static List<File> unzip(File zipfile, File outputDir,
121: boolean unpack, boolean useZipModificationTime,
122: Progress progress) {
123: return unzip(zipfile.getPath(), outputDir.getAbsolutePath(),
124: unpack, useZipModificationTime, progress);
125: }
126:
127: /**
128: * Unzip file <b>filename</b> to the directory <b>outpuDir</b>.
129: *
130: * @return List of unzipped files (<i>null</i> if filename or outputDir is <i>null</i>)
131: * @param filename ZIP file name
132: * @param outputDir Directory for ZIP file to be extracted
133: * @param unpack Unpack or not packed files (.pack.gz)
134: * @param useZipModificationTime Set modification time of unzipped file to zip entry time
135: * @param progress Progress instance to display detail and set percentage.
136: * <br>If <b>progress</b> is <i>null</i> then nothing would be displayed
137: */
138: public static List<File> unzip(String filename, File outputDir,
139: boolean unpack, boolean useZipModificationTime,
140: Progress progress) {
141:
142: return unzip(filename, outputDir.getAbsolutePath(), unpack,
143: useZipModificationTime, progress);
144: }
145:
146: /**
147: * Unzip file <b>filename</b> to the directory <b>outpuDir</b>.
148: *
149: * @return List of unzipped files.
150: * If filename or outputDir is <i>null</i> then return <i>null</i>
151: * @param filename ZIP file name
152: * @param outputDir Directory for ZIP file to be extracted
153: * @param unpack Unpack or not packed files (.pack.gz)
154: * @param useZipModificationTime
155: * If set modification time of unzipped file to zip entry time
156: * @param progress Progress instance to display detail and set percentage.
157: * @param conditionList List of conditions
158: * <br>If <b>progress</b> is <i>null</i> then nothing would be displayed
159: */
160: public static List<File> unzip(String filename, String outputDir,
161: boolean unpack, boolean useZipModificationTime,
162: Progress progress) {
163: List<File> entriesList = new ArrayList<File>();
164:
165: if (!isInputParametrsOK(filename, outputDir)) {
166: return entriesList;
167: }
168: ZipInputStream zis = null;
169:
170: try {
171: LogManager.log(ErrorLevel.MESSAGE, "Extract files from " + //NOI18N
172: (new File(filename)).getName() + " ..."); //NOI18N
173:
174: zis = new ZipInputStream(new FileInputStream(filename));
175: ZipFile zf = new ZipFile(filename);
176: double entriesTotalNumber = zf.size();
177: double entriesUnzipped = 0.0;
178:
179: // create output dir if it does not exist
180: File outputDirFile = new File(outputDir);
181: createOutputDir(outputDirFile, entriesList);
182:
183: //extract each entry
184: ZipEntry zipentry;
185: while ((zipentry = zis.getNextEntry()) != null) {
186: if (progress.isCanceled())
187: return entriesList;
188:
189: String name = zipentry.getName();
190: String entryName = new File(outputDir + File.separator
191: + name).getCanonicalPath();
192:
193: LogManager.log(ErrorLevel.MESSAGE, " unzipping "
194: + name + " to " + //NOI18N
195: outputDir);
196:
197: setProgressDetail(progress, entryName, entriesUnzipped,
198: entriesTotalNumber);
199: entriesUnzipped++;
200:
201: File createdFile;
202:
203: if (zipentry.isDirectory()) {
204: createdFile = new File(entryName);
205: createOutputDir(createdFile, entriesList);
206: } else {
207: createOutputDir(
208: new File(entryName).getParentFile(),
209: entriesList);
210: createdFile = new File(unpack(zis, entryName,
211: unpack));
212: entriesList.add(createdFile);
213: zis.closeEntry();
214: }
215:
216: // set last modification time from the source
217: if (useZipModificationTime) {
218: createdFile.setLastModified(zipentry.getTime());
219: }
220: }
221: zis.close();
222: } catch (ZipException e) {
223: LogManager.log(ErrorLevel.MESSAGE, "Wrong zip file: "
224: + filename); //NOI18N
225: } catch (FileNotFoundException e) {
226: LogManager.log(ErrorLevel.MESSAGE, "File " + filename
227: + "was not found"); //NOI18N
228: } catch (IOException e) {
229: LogManager.log(ErrorLevel.MESSAGE, "I/O Error on file: "
230: + filename); //NOI18N
231: } finally {
232: try {
233: LogManager.log(ErrorLevel.MESSAGE, "Extract finished."); //NOI18N
234: zis.closeEntry();
235: zis.close();
236: } catch (IOException e) {
237: e = null;// do nothing
238: } catch (NullPointerException e) {
239: e = null;// do nothing
240: }
241: }
242: return entriesList;
243: }
244:
245: /**
246: * Packs the file. If the file is a directory is will be navigated recursively.
247: *
248: * @param file File to pack.
249: * @param includes Includes list for packer.
250: */
251: public static void packFile(File file, List<String> includes) {
252: if (packer == null) {
253: packer = Pack200.newPacker();
254:
255: Map<String, String> properties = packer.properties();
256:
257: // properties.put(Packer.SEGMENT_LIMIT, "-1");
258: // properties.put(Packer.EFFORT, "9");
259: properties.put(Packer.KEEP_FILE_ORDER, Packer.TRUE);
260: properties.put(Packer.DEFLATE_HINT, Packer.KEEP);
261: properties.put(Packer.MODIFICATION_TIME, Packer.KEEP);
262: properties.put(Packer.UNKNOWN_ATTRIBUTE, Packer.PASS);
263: }
264:
265: if (file.isDirectory()) {
266: File[] children = file.listFiles(JAR_FILTER);
267:
268: for (File child : children) {
269: packFile(child, includes);
270: }
271: } else {
272: if (includes.contains(file.getName())) {
273: try {
274: // correct path
275: File canonicalFile = file.getCanonicalFile();
276:
277: LogManager.log(ErrorLevel.MESSAGE,
278: " compressing file: " + //NOI18N
279: canonicalFile.getAbsolutePath());
280:
281: JarFile jarFile = new JarFile(canonicalFile);
282: FileOutputStream outputStream = new FileOutputStream(
283: new File(canonicalFile.getAbsolutePath()
284: + PACK_SUFFIX));
285:
286: packer.pack(jarFile, outputStream);
287:
288: jarFile.close();
289: outputStream.close();
290:
291: if (!canonicalFile.delete()) {
292: LogManager
293: .log(ErrorLevel.MESSAGE,
294: " ... cannot delete, deleting on exit"); //NOI18N
295: canonicalFile.deleteOnExit();
296: }
297: } catch (IOException e) {
298: e.printStackTrace();
299: }
300: }
301: }
302: }
303:
304: /**
305: * Unpacks the file. If the file is a directory it is browsed recursively.
306: *
307: * @param file Fiel to unpack
308: * @throws java.io.IOException if an I/O error occurs
309: * @return List of unpacked files
310: */
311: public static List<String> unpackFile(File file) throws IOException {
312: List<String> unpackedFilesList = new ArrayList<String>();
313:
314: if (file.isDirectory()) {
315: for (File child : file.listFiles(PACKED_FILTER)) {
316: unpackedFilesList.addAll(unpackFile(child));
317: }
318: } else {
319: String path = file.getCanonicalPath();
320:
321: if (!path.endsWith(PACK_SUFFIX)) {
322: unpackedFilesList.add(path);
323: return unpackedFilesList;
324: }
325:
326: LogManager.log(" decompressing file: " + path);
327:
328: String target = path.substring(0, path.length()
329: - PACK_SUFFIX.length());
330: JarOutputStream output = null;
331: try {
332: output = new JarOutputStream(new FileOutputStream(
333: new File(target)));
334: unpacker.unpack(file, output);
335: unpackedFilesList.add(target);
336: } finally {
337: if (output != null) {
338: try {
339: output.close();
340: } catch (IOException e) {
341: ErrorManager.notify(ErrorLevel.DEBUG, e);
342: }
343: }
344: }
345:
346: if (!file.delete()) {
347: file.deleteOnExit();
348: }
349: }
350:
351: return unpackedFilesList;
352: }
353:
354: // private //////////////////////////////////////////////////////////////////////
355: private static void createOutputDir(File outputDirFile,
356: List<File> list) {
357: if (!outputDirFile.exists()) {
358: createOutputDir(outputDirFile.getParentFile(), list);
359: if (outputDirFile.mkdir()) {
360: list.add(outputDirFile);
361: }
362: }
363: return;
364:
365: }
366:
367: private static boolean isInputParametrsOK(String filename,
368: String outputDir) {
369: if (filename == null) {
370: LogManager.log(ErrorLevel.WARNING,
371: "Null filename for unzipping"); //NOI18N
372: return false;
373: }
374: if (outputDir == null) {
375: LogManager.log(ErrorLevel.WARNING,
376: "Null output dir for unzipping"); //NOI18N
377: return false;
378: }
379: return true;
380: }
381:
382: /**
383: * Set progress values.
384: * @param progress The <code>Progress</code> instance
385: */
386: private static void setProgressDetail(Progress progress,
387: String entryName, double unzipped, double total) {
388: if (progress != null) {
389: int percentage = (int) ((unzipped * Progress.COMPLETE) / total);
390:
391: progress.setDetail(StringUtils.format("Extracting {0}",
392: entryName));
393: progress.setPercentage(percentage);
394: }
395: }
396:
397: /**
398: * Unpack data from <b>zis</b> tp file with path <b>name</b>.
399: *
400: * @param zis ZipInputStream for unpacking
401: * @param path Output file path
402: * @param unpack If unpack .pack.gz files automatically
403: *
404: * @return Written file name
405: */
406: private static String unpack(ZipInputStream zis, String path,
407: boolean unpack) throws IOException {
408: int n;
409: String writedFile;
410: boolean needUnpack = path.endsWith(PACK_SUFFIX) && unpack;
411: OutputStream os = needUnpack ? new ByteArrayOutputStream(
412: BUFFER_SIZE) : new FileOutputStream(path);
413:
414: byte[] buf = new byte[BUFFER_SIZE];
415:
416: while ((n = zis.read(buf, 0, BUFFER_SIZE)) > -1) {
417: os.write(buf, 0, n);
418: }
419: writedFile = needUnpack ? unpackPackedJar(
420: (ByteArrayOutputStream) os, path) : path;
421:
422: os.close();
423:
424: return writedFile;
425: }
426:
427: /** Unpack packed jar from <b>baos</b> to file with path <b>filename</b>.
428: * @param baos ByteArrayOutputStream with packed data
429: * @param filename Packed Jar file name
430: * @return Unpacked jar filename
431: */
432: private static String unpackPackedJar(ByteArrayOutputStream baos,
433: String filename) throws IOException {
434: String unpackedFile = filename.substring(0, filename.length()
435: - PACK_SUFFIX.length());
436:
437: JarOutputStream outputStream = new JarOutputStream(
438: new FileOutputStream(new File(unpackedFile)));
439: ByteArrayInputStream bais = new ByteArrayInputStream(baos
440: .toByteArray());
441: unpacker.unpack(bais, outputStream);
442: outputStream.close();
443: return unpackedFile;
444:
445: }
446:
447: /////////////////////////////////////////////////////////////////////////////////
448: // Instance
449: private ZipPackUtils() {
450: // does nothing
451: }
452:
453: /////////////////////////////////////////////////////////////////////////////////
454: // Inner Classes
455: /**
456: * A file filter that accepts only jar files and directories.
457: */
458: private static class JarsFilter implements FileFilter {
459: /**
460: * Checks whether the file is a jar file.
461: *
462: * @param file File to examine
463: * @return true if the file is a jar file or a directory
464: */
465: public boolean accept(File file) {
466: if (file.isDirectory()) {
467: return true;
468: }
469:
470: return file.getName().endsWith(DOT_JAR)
471: || file.getName().endsWith(DOT_WAR)
472: || file.getName().endsWith(DOT_EAR);
473:
474: }
475:
476: public static final String DOT_JAR = ".jar"; //NOI18N
477: public static final String DOT_WAR = ".war"; //NOI18N
478: public static final String DOT_EAR = ".ear"; //NOI18N
479: }
480:
481: /**
482: * A file filter that accepts only packed jar files and directories.
483: */
484: private static class PackedFilter implements FileFilter {
485: private String suffix;
486:
487: /**
488: * Creates a new instance of PackedJarsFilter.
489: *
490: * @param aSuffix
491: * The suffix for the packed jar files.
492: */
493: public PackedFilter(String aSuffix) {
494: suffix = aSuffix;
495: }
496:
497: /**
498: * Checks whether the file is a packed jar file.
499: *
500: * @param file File to examine
501: * @return true if the file is a jar file or a directory
502: */
503: public boolean accept(File file) {
504: if (file.isDirectory()) {
505: return true;
506: }
507:
508: return file.getName().endsWith(suffix);
509: }
510: }
511: }
|