0N/A/*
5686N/A * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.java.util.jar.pack;
0N/A
0N/Aimport java.beans.PropertyChangeListener;
3063N/Aimport java.io.BufferedInputStream;
3063N/Aimport java.io.ByteArrayOutputStream;
3063N/Aimport java.io.File;
3063N/Aimport java.io.FileInputStream;
3063N/Aimport java.io.IOException;
3063N/Aimport java.io.InputStream;
3063N/Aimport java.io.OutputStream;
3063N/Aimport java.util.HashSet;
3315N/Aimport java.util.Set;
3063N/Aimport java.util.SortedMap;
3063N/Aimport java.util.TimeZone;
3063N/Aimport java.util.jar.JarEntry;
3063N/Aimport java.util.jar.JarInputStream;
3063N/Aimport java.util.jar.JarOutputStream;
3063N/Aimport java.util.jar.Pack200;
3063N/Aimport java.util.zip.CRC32;
3063N/Aimport java.util.zip.CheckedOutputStream;
3063N/Aimport java.util.zip.ZipEntry;
0N/A
0N/A/*
0N/A * Implementation of the Pack provider.
0N/A * </pre></blockquote>
0N/A * @author John Rose
0N/A * @author Kumar Srinivasan
0N/A */
0N/A
0N/A
2667N/Apublic class UnpackerImpl extends TLGlobals implements Pack200.Unpacker {
0N/A
0N/A
0N/A /**
0N/A * Register a listener for changes to options.
0N/A * @param listener An object to be invoked when a property is changed.
0N/A */
0N/A public void addPropertyChangeListener(PropertyChangeListener listener) {
2667N/A props.addListener(listener);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Remove a listener for the PropertyChange event.
0N/A * @param listener The PropertyChange listener to be removed.
0N/A */
0N/A public void removePropertyChangeListener(PropertyChangeListener listener) {
2667N/A props.removeListener(listener);
0N/A }
0N/A
2667N/A public UnpackerImpl() {}
0N/A
0N/A
0N/A
0N/A /**
0N/A * Get the set of options for the pack and unpack engines.
0N/A * @return A sorted association of option key strings to option values.
0N/A */
3315N/A @SuppressWarnings("unchecked")
3315N/A public SortedMap properties() {
2667N/A return props;
0N/A }
0N/A
0N/A // Back-pointer to NativeUnpacker, when active.
0N/A Object _nunp;
0N/A
0N/A
0N/A public String toString() {
0N/A return Utils.getVersionString();
0N/A }
0N/A
0N/A //Driver routines
0N/A
0N/A // The unpack worker...
0N/A /**
0N/A * Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally
0N/A * the entire buffer must be read, it may be more efficient to read the packed-stream
0N/A * to a file and pass the File object, in the alternate method described below.
0N/A * <p>
0N/A * Closes its input but not its output. (The output can accumulate more elements.)
0N/A * @param in an InputStream.
0N/A * @param out a JarOutputStream.
0N/A * @exception IOException if an error is encountered.
0N/A */
5686N/A public synchronized void unpack(InputStream in, JarOutputStream out) throws IOException {
3042N/A if (in == null) {
3042N/A throw new NullPointerException("null input");
3042N/A }
3042N/A if (out == null) {
3042N/A throw new NullPointerException("null output");
3042N/A }
0N/A assert(Utils.currentInstance.get() == null);
2667N/A TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE))
2667N/A ? null
2667N/A : TimeZone.getDefault();
0N/A
0N/A try {
0N/A Utils.currentInstance.set(this);
0N/A if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
2667N/A final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);
3042N/A BufferedInputStream in0 = new BufferedInputStream(in);
3042N/A if (Utils.isJarMagic(Utils.readMagic(in0))) {
0N/A if (verbose > 0)
0N/A Utils.log.info("Copying unpacked JAR file...");
3042N/A Utils.copyJarFile(new JarInputStream(in0), out);
2667N/A } else if (props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) {
3042N/A (new DoUnpack()).run(in0, out);
3042N/A in0.close();
0N/A Utils.markJarFile(out);
0N/A } else {
3042N/A (new NativeUnpack(this)).run(in0, out);
3042N/A in0.close();
0N/A Utils.markJarFile(out);
0N/A }
0N/A } finally {
0N/A _nunp = null;
0N/A Utils.currentInstance.set(null);
0N/A if (tz != null) TimeZone.setDefault(tz);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Takes an input File containing the pack file, and generates a JarOutputStream.
0N/A * <p>
0N/A * Does not close its output. (The output can accumulate more elements.)
0N/A * @param in a File.
0N/A * @param out a JarOutputStream.
0N/A * @exception IOException if an error is encountered.
0N/A */
5686N/A public synchronized void unpack(File in, JarOutputStream out) throws IOException {
3042N/A if (in == null) {
3042N/A throw new NullPointerException("null input");
3042N/A }
3042N/A if (out == null) {
3042N/A throw new NullPointerException("null output");
3042N/A }
0N/A // Use the stream-based implementation.
0N/A // %%% Reconsider if native unpacker learns to memory-map the file.
3774N/A try (FileInputStream instr = new FileInputStream(in)) {
3774N/A unpack(instr, out);
3774N/A }
2667N/A if (props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) {
0N/A in.delete();
0N/A }
0N/A }
0N/A
0N/A private class DoUnpack {
2667N/A final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);
0N/A
0N/A {
2667N/A props.setInteger(Pack200.Unpacker.PROGRESS, 0);
0N/A }
0N/A
0N/A // Here's where the bits are read from disk:
0N/A final Package pkg = new Package();
0N/A
0N/A final boolean keepModtime
2667N/A = Pack200.Packer.KEEP.equals(
2667N/A props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP));
0N/A final boolean keepDeflateHint
2667N/A = Pack200.Packer.KEEP.equals(
2667N/A props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP));
0N/A final int modtime;
0N/A final boolean deflateHint;
0N/A {
0N/A if (!keepModtime) {
2667N/A modtime = props.getTime(Utils.UNPACK_MODIFICATION_TIME);
0N/A } else {
0N/A modtime = pkg.default_modtime;
0N/A }
0N/A
0N/A deflateHint = (keepDeflateHint) ? false :
2667N/A props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT);
0N/A }
0N/A
0N/A // Checksum apparatus.
0N/A final CRC32 crc = new CRC32();
0N/A final ByteArrayOutputStream bufOut = new ByteArrayOutputStream();
0N/A final OutputStream crcOut = new CheckedOutputStream(bufOut, crc);
0N/A
0N/A public void run(BufferedInputStream in, JarOutputStream out) throws IOException {
0N/A if (verbose > 0) {
2667N/A props.list(System.out);
0N/A }
0N/A for (int seg = 1; ; seg++) {
0N/A unpackSegment(in, out);
0N/A
0N/A // Try to get another segment.
0N/A if (!Utils.isPackMagic(Utils.readMagic(in))) break;
0N/A if (verbose > 0)
0N/A Utils.log.info("Finished segment #"+seg);
0N/A }
0N/A }
0N/A
0N/A private void unpackSegment(InputStream in, JarOutputStream out) throws IOException {
2667N/A props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0");
0N/A // Process the output directory or jar output.
0N/A new PackageReader(pkg, in).read();
0N/A
2667N/A if (props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug");
2667N/A if (props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile");
2667N/A props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50");
0N/A pkg.ensureAllClassFiles();
0N/A // Now write out the files.
3315N/A Set<Package.Class> classesToWrite = new HashSet<>(pkg.getClasses());
3315N/A for (Package.File file : pkg.getFiles()) {
0N/A String name = file.nameString;
0N/A JarEntry je = new JarEntry(Utils.getJarEntryName(name));
0N/A boolean deflate;
0N/A
2667N/A deflate = (keepDeflateHint)
2667N/A ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) ||
2667N/A ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0))
2667N/A : deflateHint;
0N/A
0N/A boolean needCRC = !deflate; // STORE mode requires CRC
0N/A
0N/A if (needCRC) crc.reset();
0N/A bufOut.reset();
0N/A if (file.isClassStub()) {
0N/A Package.Class cls = file.getStubClass();
0N/A assert(cls != null);
0N/A new ClassWriter(cls, needCRC ? crcOut : bufOut).write();
0N/A classesToWrite.remove(cls); // for an error check
0N/A } else {
0N/A // collect data & maybe CRC
0N/A file.writeTo(needCRC ? crcOut : bufOut);
0N/A }
0N/A je.setMethod(deflate ? JarEntry.DEFLATED : JarEntry.STORED);
0N/A if (needCRC) {
0N/A if (verbose > 0)
0N/A Utils.log.info("stored size="+bufOut.size()+" and crc="+crc.getValue());
0N/A
0N/A je.setMethod(JarEntry.STORED);
0N/A je.setSize(bufOut.size());
0N/A je.setCrc(crc.getValue());
0N/A }
0N/A if (keepModtime) {
0N/A je.setTime(file.modtime);
0N/A // Convert back to milliseconds
0N/A je.setTime((long)file.modtime * 1000);
0N/A } else {
0N/A je.setTime((long)modtime * 1000);
0N/A }
0N/A out.putNextEntry(je);
0N/A bufOut.writeTo(out);
0N/A out.closeEntry();
0N/A if (verbose > 0)
0N/A Utils.log.info("Writing "+Utils.zeString((ZipEntry)je));
0N/A }
0N/A assert(classesToWrite.isEmpty());
2667N/A props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100");
0N/A pkg.reset(); // reset for the next segment, if any
0N/A }
0N/A }
0N/A}