View Javadoc

1   /*
2    * $Id: UnzipJar.java 2992 2011-11-24 19:25:54Z andrewinkler $
3    * ============================================================================
4    * Project gluehloch-homepage-resource
5    * Copyright (c) 2004-2010 by Andre Winkler. All rights reserved.
6    * ============================================================================
7    *          GNU LESSER GENERAL PUBLIC LICENSE
8    *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
9    *
10   *  This library is free software; you can redistribute it and/or
11   *  modify it under the terms of the GNU Lesser General Public
12   *  License as published by the Free Software Foundation; either
13   *  version 2.1 of the License, or (at your option) any later version.
14   *
15   *  This library is distributed in the hope that it will be useful,
16   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   *  Lesser General Public License for more details.
19   *
20   *  You should have received a copy of the GNU Lesser General Public
21   *  License along with this library; if not, write to the Free Software
22   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   *
24   */
25  
26  package de.awtools.homegen;
27  
28  import java.io.File;
29  import java.io.FileOutputStream;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.net.URI;
33  import java.util.Enumeration;
34  import java.util.TreeSet;
35  import java.util.zip.ZipEntry;
36  import java.util.zip.ZipFile;
37  
38  import org.apache.commons.io.FileUtils;
39  import org.apache.commons.io.IOUtils;
40  import org.apache.commons.lang.StringUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import de.awtools.basic.file.AWToolsFileUtils;
45  
46  /**
47   * Extrahiert die Web-Resource (CSS, JavaScript,...) aus dem eigenen Java-
48   * Archiv.
49   * 
50   * @version $LastChangedRevision: 2992 $ $LastChangedDate: 2011-11-24 20:25:54 +0100 (Thu, 24 Nov 2011) $
51   * @author by Andre Winkler, $LastChangedBy: andrewinkler $
52   *
53   * @since 1.4
54   */
55  public final class UnzipJar {
56  
57      /** Der Logger der Klasse. */
58      private final Logger log = LoggerFactory.getLogger(UnzipJar.class);
59  
60      // -- zipFile -------------------------------------------------------------
61  
62      /** Der Name der ZIP/JAR Datei. */
63      private URI zipFileName;
64  
65      /**
66       * @param value Setzt den Namen der ZIP/JAR Datei.
67       */
68      public void setZipFileName(final URI value) {
69          zipFileName = value;
70      }
71  
72      // -- targetDirectory -----------------------------------------------------
73  
74      /** Das Zielverzeichnis für alle Ausgaben. */
75      private String targetDirectory;
76  
77      /**
78       * @param value Setzt den Namen für Ausgabeverzeichnis. Dieser Parameter
79       *     ist optional. Ist er nicht gesetzt, wird in das aktuelle
80       *     Arbeitsverzeichnis extrahiert.
81       */
82      public void setTargetDirectory(final String value) {
83          targetDirectory = value;
84      }
85  
86      // -- filterInclude -------------------------------------------------------
87  
88      /**
89       * Definiert einen Filter der zu extrahierenden Dateien. Der Filter bezieht
90       * sich auf den Prefix eines Zip-Eintrags. Der Filter ist ein postiv
91       * Filter, d.h. alle Zip-Einträge mit dem Prefix 'filterInclude' werden
92       * extrahiert. Zusätzlich wird dieser Prefix aus dem Pfad der zu
93       * extrahierenden Datei entfernt.<br>
94       * Beispiel: Aus dem Zip-Eintrag
95       * <code>plugin-resources/web/css/main.css</code> wird mit dem Filter
96       * <code>plugin-resources/web<code> die Datei <code>css/main.css</code>.
97       */
98      private String filterInclude;
99  
100     /**
101      * Setzt einen Filter. Siehe auch {@link #filterInclude}.
102      *
103      * @param value
104      */
105     public void setFilterInclude(final String value) {
106         filterInclude = value;
107     }
108 
109     // ------------------------------------------------------------------------
110 
111     /** Verwaltet die erstellten Directories. */
112     private final TreeSet<String> dirsMade = new TreeSet<String>();
113 
114     /** Einmalig eine Warnung schreiben, daß absolute Pfade ignoriert werden. */
115     private boolean warnedMkDir = false;
116 
117     /** Die ZIP/JAR File-Handle. */
118     private ZipFile zipFile;
119 
120     /** Ein Buffer. */
121     private byte[] bytes;
122 
123     /**
124      * Extrahiert ein Zip-Archiv. Zurückgegeben wird das extrahierte
125      * {@link ZipFile}.
126      *
127      * @return Das extrahierte Zip-Archiv.
128      * @throws IOException IO-Probleme.
129      */
130     public ZipFile call() throws IOException {
131         init();
132 
133         Enumeration<? extends ZipEntry> all = zipFile.entries();
134         while (all.hasMoreElements()) {
135             unzip(all.nextElement());
136         }
137 
138         return zipFile;
139     }
140 
141     /**
142      * Stellt die internen Zustände in ihren Initialzustand zurück.
143      *
144      * @throws IOException Fehler in der Initialisierung.
145      */
146     private void init() throws IOException {
147         File file = new File(zipFileName);
148         if (log.isDebugEnabled()) {
149             if (file.exists()) {
150                 log.debug("File '" + zipFileName + "' exists!");
151             } else {
152                 log.debug("File '" + zipFileName + "' does not exist!");
153             }
154         }
155 
156         zipFile = new ZipFile(file);
157         bytes = new byte[8092];
158         warnedMkDir = false;
159         dirsMade.clear();
160 
161         // Eine Vorbedingung für die spätere Ausführung: Verzeichnisse müssen
162         // mit einem '/' beendet werden.
163         if ((targetDirectory != null) && (!(targetDirectory.endsWith("/") || targetDirectory
164                 .endsWith("\\")))) {
165             targetDirectory = targetDirectory + "/";
166         }
167 
168         if (targetDirectory == null) {
169             targetDirectory = "./";
170         }
171     }
172 
173     private void unzip(final ZipEntry zipEntry) throws IOException {
174         String zipName = zipEntry.getName();
175 
176         // Vorangestellte '/' Trenner werden entfernt.
177         if (zipName.startsWith("/")) {
178             if (!warnedMkDir) {
179                 log.warn("Ignoring absolute paths");
180             }
181             warnedMkDir = true;
182             zipName = zipName.substring(1);
183         }
184 
185         // Filter prüft...
186         if (StringUtils.isNotBlank(filterInclude)) {
187             if (!zipName.startsWith(filterInclude)) {
188                 return;
189             } else {
190                 zipName = StringUtils.replace(zipName, filterInclude, "", 1);
191             }
192         }
193 
194         // Verzeichniseinträge werden ignoriert. Verzeichnisse werden
195         // automatisch angelegt (weswegen Verzeichniseinträge ignoriert werden
196         // können).
197         if (zipName.endsWith("/")) {
198             return;
199         }
200 
201         // Else must be a file; open the file for output
202         // Get the directory part.
203         int ix = zipName.lastIndexOf('/');
204         if (ix > 0) {
205             StringBuilder dirNameBuilder = new StringBuilder(targetDirectory);
206             dirNameBuilder.append(zipName.substring(0, ix));
207             String dirPath = AWToolsFileUtils.normalizePath(dirNameBuilder
208                     .toString());
209 
210             if (!dirsMade.contains(dirNameBuilder.toString())) {
211                 File createDirecotory = new File(dirPath);
212                 if (!(createDirecotory.exists() && createDirecotory
213                         .isDirectory())) {
214                     log.debug("Creating Directory: " + dirPath);
215 
216                     FileUtils.forceMkdir(createDirecotory);
217                     dirsMade.add(dirNameBuilder.toString());
218                 }
219             }
220         }
221 
222         extractZipEntry(zipEntry, zipName);
223     }
224 
225     /**
226      * Packt einen Zip-Eintrag aus.
227      *
228      * @param zipEntry Der zu entpackende Zip-Eintrag. 
229      * @param zipName Datei-Bezeichner für den entpackten Zip-Eintrag.
230      * @throws IOException Fehler beim Export.
231      */
232     private void extractZipEntry(final ZipEntry zipEntry, final String zipName)
233             throws IOException {
234 
235         log.debug("Extract: " + zipName);
236 
237         File targetFile = new File(targetDirectory + zipName);
238         // @todo Auspacken nur wenn: 
239         //		FileUtils.isFileNewer(targetFile, zipEntry.getTime());
240 
241         FileOutputStream os = null;
242         InputStream is = null;
243         try {
244             os = new FileOutputStream(targetFile);
245             is = zipFile.getInputStream(zipEntry);
246             int n = 0;
247             while ((n = is.read(bytes)) > 0) {
248                 os.write(bytes, 0, n);
249             }
250         } finally {
251             IOUtils.closeQuietly(is);
252             IOUtils.closeQuietly(os);
253         }
254 
255         targetFile.setLastModified(zipEntry.getTime());
256     }
257 
258     // ------------------------------------------------------------------------
259 
260     /**
261      * @param args Kommandozeilenparameter. args[0], die zu entpackende Datei;
262      *     args[1], das Zielverzeichnis.
263      * @throws Exception Im Fehlerfall eine Exception.
264      */
265     public static void main(final String[] args) throws Exception {
266         UnzipJar unzip = new UnzipJar();
267         if (args.length > 0) {
268             System.out.println("Unzip file: " + args[0]);
269             unzip.setZipFileName(new URI(args[0]));
270         }
271         if (args.length > 1) {
272             unzip.setTargetDirectory(args[1]);
273         }
274         if (args.length > 2) {
275             unzip.setFilterInclude(args[2]);
276         }
277 
278         if (args.length == 0) {
279             System.out.println("Geben sie die zu extrahierende Datei an!");
280         } else {
281             unzip.call();
282         }
283     }
284 
285 }