blob: d3149c13cc08a7bf93333dceef11a75358f93e6d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.java.util.jar.pack;
27
28import java.util.*;
29import java.util.jar.*;
30import java.util.zip.*;
31import java.io.*;
32import java.beans.PropertyChangeListener;
33import java.beans.PropertyChangeEvent;
34
35/*
36 * Implementation of the Pack provider.
37 * </pre></blockquote>
38 * @author John Rose
39 * @author Kumar Srinivasan
40 */
41
42
43public class UnpackerImpl implements Pack200.Unpacker {
44
45
46 /**
47 * Register a listener for changes to options.
48 * @param listener An object to be invoked when a property is changed.
49 */
50 public void addPropertyChangeListener(PropertyChangeListener listener) {
51 _props.addListener(listener);
52 }
53
54
55 /**
56 * Remove a listener for the PropertyChange event.
57 * @param listener The PropertyChange listener to be removed.
58 */
59 public void removePropertyChangeListener(PropertyChangeListener listener) {
60 _props.removeListener(listener);
61 }
62
63 public UnpackerImpl() {
64 _props = new PropMap();
65 //_props.getProperty() consults defaultProps invisibly.
66 //_props.putAll(defaultProps);
67 }
68
69 // Private stuff.
70 final PropMap _props;
71
72
73 /**
74 * Get the set of options for the pack and unpack engines.
75 * @return A sorted association of option key strings to option values.
76 */
77 public SortedMap properties() {
78 return _props;
79 }
80
81 // Back-pointer to NativeUnpacker, when active.
82 Object _nunp;
83
84
85 public String toString() {
86 return Utils.getVersionString();
87 }
88
89 //Driver routines
90
91 // The unpack worker...
92 /**
93 * Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally
94 * the entire buffer must be read, it may be more efficient to read the packed-stream
95 * to a file and pass the File object, in the alternate method described below.
96 * <p>
97 * Closes its input but not its output. (The output can accumulate more elements.)
98 * @param in an InputStream.
99 * @param out a JarOutputStream.
100 * @exception IOException if an error is encountered.
101 */
102 public void unpack(InputStream in0, JarOutputStream out) throws IOException {
103 assert(Utils.currentInstance.get() == null);
104 TimeZone tz = (_props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null :
105 TimeZone.getDefault();
106
107 try {
108 Utils.currentInstance.set(this);
109 if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
110 final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
111 BufferedInputStream in = new BufferedInputStream(in0);
112 if (Utils.isJarMagic(Utils.readMagic(in))) {
113 if (verbose > 0)
114 Utils.log.info("Copying unpacked JAR file...");
115 Utils.copyJarFile(new JarInputStream(in), out);
116 } else if (_props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) {
117 (new DoUnpack()).run(in, out);
118 in.close();
119 Utils.markJarFile(out);
120 } else {
121 (new NativeUnpack(this)).run(in, out);
122 in.close();
123 Utils.markJarFile(out);
124 }
125 } finally {
126 _nunp = null;
127 Utils.currentInstance.set(null);
128 if (tz != null) TimeZone.setDefault(tz);
129 }
130 }
131
132 /**
133 * Takes an input File containing the pack file, and generates a JarOutputStream.
134 * <p>
135 * Does not close its output. (The output can accumulate more elements.)
136 * @param in a File.
137 * @param out a JarOutputStream.
138 * @exception IOException if an error is encountered.
139 */
140 public void unpack(File in, JarOutputStream out) throws IOException {
141 // Use the stream-based implementation.
142 // %%% Reconsider if native unpacker learns to memory-map the file.
143 FileInputStream instr = new FileInputStream(in);
144 unpack(instr, out);
145 if (_props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) {
146 in.delete();
147 }
148 }
149
150 private class DoUnpack {
151 final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
152
153 {
154 _props.setInteger(Pack200.Unpacker.PROGRESS, 0);
155 }
156
157 // Here's where the bits are read from disk:
158 final Package pkg = new Package();
159
160 final boolean keepModtime
161 = Pack200.Packer.KEEP.equals(_props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP));
162 final boolean keepDeflateHint
163 = Pack200.Packer.KEEP.equals(_props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP));
164 final int modtime;
165 final boolean deflateHint;
166 {
167 if (!keepModtime) {
168 modtime = _props.getTime(Utils.UNPACK_MODIFICATION_TIME);
169 } else {
170 modtime = pkg.default_modtime;
171 }
172
173 deflateHint = (keepDeflateHint) ? false :
174 _props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT);
175 }
176
177 // Checksum apparatus.
178 final CRC32 crc = new CRC32();
179 final ByteArrayOutputStream bufOut = new ByteArrayOutputStream();
180 final OutputStream crcOut = new CheckedOutputStream(bufOut, crc);
181
182 public void run(BufferedInputStream in, JarOutputStream out) throws IOException {
183 if (verbose > 0) {
184 _props.list(System.out);
185 }
186 for (int seg = 1; ; seg++) {
187 unpackSegment(in, out);
188
189 // Try to get another segment.
190 if (!Utils.isPackMagic(Utils.readMagic(in))) break;
191 if (verbose > 0)
192 Utils.log.info("Finished segment #"+seg);
193 }
194 }
195
196 private void unpackSegment(InputStream in, JarOutputStream out) throws IOException {
197 _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0");
198 // Process the output directory or jar output.
199 new PackageReader(pkg, in).read();
200
201 if (_props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug");
202 if (_props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile");
203 _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50");
204 pkg.ensureAllClassFiles();
205 // Now write out the files.
206 HashSet classesToWrite = new HashSet(pkg.getClasses());
207 for (Iterator i = pkg.getFiles().iterator(); i.hasNext(); ) {
208 Package.File file = (Package.File) i.next();
209 String name = file.nameString;
210 JarEntry je = new JarEntry(Utils.getJarEntryName(name));
211 boolean deflate;
212
213 deflate = (keepDeflateHint) ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) ||
214 ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0)) :
215 deflateHint;
216
217 boolean needCRC = !deflate; // STORE mode requires CRC
218
219 if (needCRC) crc.reset();
220 bufOut.reset();
221 if (file.isClassStub()) {
222 Package.Class cls = file.getStubClass();
223 assert(cls != null);
224 new ClassWriter(cls, needCRC ? crcOut : bufOut).write();
225 classesToWrite.remove(cls); // for an error check
226 } else {
227 // collect data & maybe CRC
228 file.writeTo(needCRC ? crcOut : bufOut);
229 }
230 je.setMethod(deflate ? JarEntry.DEFLATED : JarEntry.STORED);
231 if (needCRC) {
232 if (verbose > 0)
233 Utils.log.info("stored size="+bufOut.size()+" and crc="+crc.getValue());
234
235 je.setMethod(JarEntry.STORED);
236 je.setSize(bufOut.size());
237 je.setCrc(crc.getValue());
238 }
239 if (keepModtime) {
240 je.setTime(file.modtime);
241 // Convert back to milliseconds
242 je.setTime((long)file.modtime * 1000);
243 } else {
244 je.setTime((long)modtime * 1000);
245 }
246 out.putNextEntry(je);
247 bufOut.writeTo(out);
248 out.closeEntry();
249 if (verbose > 0)
250 Utils.log.info("Writing "+Utils.zeString((ZipEntry)je));
251 }
252 assert(classesToWrite.isEmpty());
253 _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100");
254 pkg.reset(); // reset for the next segment, if any
255 }
256 }
257}