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 }