/* * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.java.util.jar.pack; import java.beans.PropertyChangeListener; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; import java.util.Set; import java.util.SortedMap; import java.util.TimeZone; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import java.util.jar.Pack200; import java.util.zip.CRC32; import java.util.zip.CheckedOutputStream; import java.util.zip.ZipEntry; /* * Implementation of the Pack provider. * * @author John Rose * @author Kumar Srinivasan */ public class UnpackerImpl extends TLGlobals implements Pack200.Unpacker { /** * Register a listener for changes to options. * @param listener An object to be invoked when a property is changed. */ public void addPropertyChangeListener(PropertyChangeListener listener) { props.addListener(listener); } /** * Remove a listener for the PropertyChange event. * @param listener The PropertyChange listener to be removed. */ public void removePropertyChangeListener(PropertyChangeListener listener) { props.removeListener(listener); } public UnpackerImpl() {} /** * Get the set of options for the pack and unpack engines. * @return A sorted association of option key strings to option values. */ public SortedMap properties() { return props; } // Back-pointer to NativeUnpacker, when active. Object _nunp; public String toString() { return Utils.getVersionString(); } //Driver routines // The unpack worker... /** * Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally * the entire buffer must be read, it may be more efficient to read the packed-stream * to a file and pass the File object, in the alternate method described below. *

* Closes its input but not its output. (The output can accumulate more elements.) * @param in an InputStream. * @param out a JarOutputStream. * @exception IOException if an error is encountered. */ public synchronized void unpack(InputStream in, JarOutputStream out) throws IOException { if (in == null) { throw new NullPointerException("null input"); } if (out == null) { throw new NullPointerException("null output"); } assert(Utils.currentInstance.get() == null); TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null : TimeZone.getDefault(); try { Utils.currentInstance.set(this); if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC")); final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); BufferedInputStream in0 = new BufferedInputStream(in); if (Utils.isJarMagic(Utils.readMagic(in0))) { if (verbose > 0) Utils.log.info("Copying unpacked JAR file..."); Utils.copyJarFile(new JarInputStream(in0), out); } else if (props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) { (new DoUnpack()).run(in0, out); in0.close(); Utils.markJarFile(out); } else { try { (new NativeUnpack(this)).run(in0, out); } catch (UnsatisfiedLinkError ule) { // failover to java implementation (new DoUnpack()).run(in0, out); } in0.close(); Utils.markJarFile(out); } } finally { _nunp = null; Utils.currentInstance.set(null); if (tz != null) TimeZone.setDefault(tz); } } /** * Takes an input File containing the pack file, and generates a JarOutputStream. *

* Does not close its output. (The output can accumulate more elements.) * @param in a File. * @param out a JarOutputStream. * @exception IOException if an error is encountered. */ public synchronized void unpack(File in, JarOutputStream out) throws IOException { if (in == null) { throw new NullPointerException("null input"); } if (out == null) { throw new NullPointerException("null output"); } // Use the stream-based implementation. // %%% Reconsider if native unpacker learns to memory-map the file. try (FileInputStream instr = new FileInputStream(in)) { unpack(instr, out); } if (props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) { in.delete(); } } private class DoUnpack { final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); { props.setInteger(Pack200.Unpacker.PROGRESS, 0); } // Here's where the bits are read from disk: final Package pkg = new Package(); final boolean keepModtime = Pack200.Packer.KEEP.equals( props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP)); final boolean keepDeflateHint = Pack200.Packer.KEEP.equals( props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP)); final int modtime; final boolean deflateHint; { if (!keepModtime) { modtime = props.getTime(Utils.UNPACK_MODIFICATION_TIME); } else { modtime = pkg.default_modtime; } deflateHint = (keepDeflateHint) ? false : props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT); } // Checksum apparatus. final CRC32 crc = new CRC32(); final ByteArrayOutputStream bufOut = new ByteArrayOutputStream(); final OutputStream crcOut = new CheckedOutputStream(bufOut, crc); public void run(BufferedInputStream in, JarOutputStream out) throws IOException { if (verbose > 0) { props.list(System.out); } for (int seg = 1; ; seg++) { unpackSegment(in, out); // Try to get another segment. if (!Utils.isPackMagic(Utils.readMagic(in))) break; if (verbose > 0) Utils.log.info("Finished segment #"+seg); } } private void unpackSegment(InputStream in, JarOutputStream out) throws IOException { props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0"); // Process the output directory or jar output. new PackageReader(pkg, in).read(); if (props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug"); if (props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile"); props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50"); pkg.ensureAllClassFiles(); // Now write out the files. Set classesToWrite = new HashSet<>(pkg.getClasses()); for (Package.File file : pkg.getFiles()) { String name = file.nameString; JarEntry je = new JarEntry(Utils.getJarEntryName(name)); boolean deflate; deflate = (keepDeflateHint) ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) || ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0)) : deflateHint; boolean needCRC = !deflate; // STORE mode requires CRC if (needCRC) crc.reset(); bufOut.reset(); if (file.isClassStub()) { Package.Class cls = file.getStubClass(); assert(cls != null); new ClassWriter(cls, needCRC ? crcOut : bufOut).write(); classesToWrite.remove(cls); // for an error check } else { // collect data & maybe CRC file.writeTo(needCRC ? crcOut : bufOut); } je.setMethod(deflate ? JarEntry.DEFLATED : JarEntry.STORED); if (needCRC) { if (verbose > 0) Utils.log.info("stored size="+bufOut.size()+" and crc="+crc.getValue()); je.setMethod(JarEntry.STORED); je.setSize(bufOut.size()); je.setCrc(crc.getValue()); } if (keepModtime) { je.setTime(file.modtime); // Convert back to milliseconds je.setTime((long)file.modtime * 1000); } else { je.setTime((long)modtime * 1000); } out.putNextEntry(je); bufOut.writeTo(out); out.closeEntry(); if (verbose > 0) Utils.log.info("Writing "+Utils.zeString((ZipEntry)je)); } assert(classesToWrite.isEmpty()); props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100"); pkg.reset(); // reset for the next segment, if any } } }