alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (PackerImpl.java)

This example Java source code file (PackerImpl.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

attribute, bean, infile, inputstream, ioexception, jar, jarentry, javabean, list, outputstream, packagewriter, packerimpl, reading, runtimeexception, string, timezone, utc, util

The PackerImpl.java Java example source code

/*
 * 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 com.sun.java.util.jar.pack.Attribute.Layout;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Pack200;


/*
 * Implementation of the Pack provider.
 * </pre>
 * @author John Rose
 * @author Kumar Srinivasan
 */


public class PackerImpl  extends TLGlobals implements Pack200.Packer {

    /**
     * Constructs a Packer object and sets the initial state of
     * the packer engines.
     */
    public PackerImpl() {}

    /**
     * Get the set of options for the pack and unpack engines.
     * @return A sorted association of option key strings to option values.
     */
    public SortedMap<String, String> properties() {
        return props;
    }

    //Driver routines

    /**
     * Takes a JarFile and converts into a pack-stream.
     * <p>
     * Closes its input but not its output.  (Pack200 archives are appendable.)
     * @param in a JarFile
     * @param out an OutputStream
     * @exception IOException if an error is encountered.
     */
    public synchronized void pack(JarFile in, OutputStream out) throws IOException {
        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"));

            if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
                Utils.copyJarFile(in, out);
            } else {
                (new DoPack()).run(in, out);
            }
        } finally {
            Utils.currentInstance.set(null);
            if (tz != null) TimeZone.setDefault(tz);
            in.close();
        }
    }

    /**
     * Takes a JarInputStream and converts into a pack-stream.
     * <p>
     * Closes its input but not its output.  (Pack200 archives are appendable.)
     * <p>
     * The modification time and deflation hint attributes are not available,
     * for the jar-manifest file and the directory containing the file.
     *
     * @see #MODIFICATION_TIME
     * @see #DEFLATION_HINT
     * @param in a JarInputStream
     * @param out an OutputStream
     * @exception IOException if an error is encountered.
     */
    public synchronized void pack(JarInputStream in, OutputStream out) throws IOException {
        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"));
            if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
                Utils.copyJarFile(in, out);
            } else {
                (new DoPack()).run(in, out);
            }
        } finally {
            Utils.currentInstance.set(null);
            if (tz != null) TimeZone.setDefault(tz);
            in.close();
        }
    }
    /**
     * 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);
    }



    // All the worker bees.....
    // The packer worker.
    private class DoPack {
        final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);

        {
            props.setInteger(Pack200.Packer.PROGRESS, 0);
            if (verbose > 0) Utils.log.info(props.toString());
        }

        // Here's where the bits are collected before getting packed, we also
        // initialize the version numbers now.
        final Package pkg = new Package(Package.Version.makeVersion(props, "min.class"),
                                        Package.Version.makeVersion(props, "max.class"),
                                        Package.Version.makeVersion(props, "package"));

        final String unknownAttrCommand;
        {
            String uaMode = props.getProperty(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS);
            if (!(Pack200.Packer.STRIP.equals(uaMode) ||
                  Pack200.Packer.PASS.equals(uaMode) ||
                  Pack200.Packer.ERROR.equals(uaMode))) {
                throw new RuntimeException("Bad option: " + Pack200.Packer.UNKNOWN_ATTRIBUTE + " = " + uaMode);
            }
            unknownAttrCommand = uaMode.intern();
        }
        final String classFormatCommand;
        {
            String fmtMode = props.getProperty(Utils.CLASS_FORMAT_ERROR, Pack200.Packer.PASS);
            if (!(Pack200.Packer.PASS.equals(fmtMode) ||
                  Pack200.Packer.ERROR.equals(fmtMode))) {
                throw new RuntimeException("Bad option: " + Utils.CLASS_FORMAT_ERROR + " = " + fmtMode);
            }
            classFormatCommand = fmtMode.intern();
        }

        final Map<Attribute.Layout, Attribute> attrDefs;
        final Map<Attribute.Layout, String> attrCommands;
        {
            Map<Attribute.Layout, Attribute> lattrDefs   = new HashMap<>();
            Map<Attribute.Layout, String>  lattrCommands = new HashMap<>();
            String[] keys = {
                Pack200.Packer.CLASS_ATTRIBUTE_PFX,
                Pack200.Packer.FIELD_ATTRIBUTE_PFX,
                Pack200.Packer.METHOD_ATTRIBUTE_PFX,
                Pack200.Packer.CODE_ATTRIBUTE_PFX
            };
            int[] ctypes = {
                Constants.ATTR_CONTEXT_CLASS,
                Constants.ATTR_CONTEXT_FIELD,
                Constants.ATTR_CONTEXT_METHOD,
                Constants.ATTR_CONTEXT_CODE
            };
            for (int i = 0; i < ctypes.length; i++) {
                String pfx = keys[i];
                Map<String, String> map = props.prefixMap(pfx);
                for (String key : map.keySet()) {
                    assert(key.startsWith(pfx));
                    String name = key.substring(pfx.length());
                    String layout = props.getProperty(key);
                    Layout lkey = Attribute.keyForLookup(ctypes[i], name);
                    if (Pack200.Packer.STRIP.equals(layout) ||
                        Pack200.Packer.PASS.equals(layout) ||
                        Pack200.Packer.ERROR.equals(layout)) {
                        lattrCommands.put(lkey, layout.intern());
                    } else {
                        Attribute.define(lattrDefs, ctypes[i], name, layout);
                        if (verbose > 1) {
                            Utils.log.fine("Added layout for "+Constants.ATTR_CONTEXT_NAME[i]+" attribute "+name+" = "+layout);
                        }
                        assert(lattrDefs.containsKey(lkey));
                    }
                }
            }
            this.attrDefs = (lattrDefs.isEmpty()) ? null : lattrDefs;
            this.attrCommands = (lattrCommands.isEmpty()) ? null : lattrCommands;
        }

        final boolean keepFileOrder
            = props.getBoolean(Pack200.Packer.KEEP_FILE_ORDER);
        final boolean keepClassOrder
            = props.getBoolean(Utils.PACK_KEEP_CLASS_ORDER);

        final boolean keepModtime
            = Pack200.Packer.KEEP.equals(props.getProperty(Pack200.Packer.MODIFICATION_TIME));
        final boolean latestModtime
            = Pack200.Packer.LATEST.equals(props.getProperty(Pack200.Packer.MODIFICATION_TIME));
        final boolean keepDeflateHint
            = Pack200.Packer.KEEP.equals(props.getProperty(Pack200.Packer.DEFLATE_HINT));
        {
            if (!keepModtime && !latestModtime) {
                int modtime = props.getTime(Pack200.Packer.MODIFICATION_TIME);
                if (modtime != Constants.NO_MODTIME) {
                    pkg.default_modtime = modtime;
                }
            }
            if (!keepDeflateHint) {
                boolean deflate_hint = props.getBoolean(Pack200.Packer.DEFLATE_HINT);
                if (deflate_hint) {
                    pkg.default_options |= Constants.AO_DEFLATE_HINT;
                }
            }
        }

        long totalOutputSize = 0;
        int  segmentCount = 0;
        long segmentTotalSize = 0;
        long segmentSize = 0;  // running counter
        final long segmentLimit;
        {
            long limit;
            if (props.getProperty(Pack200.Packer.SEGMENT_LIMIT, "").equals(""))
                limit = -1;
            else
                limit = props.getLong(Pack200.Packer.SEGMENT_LIMIT);
            limit = Math.min(Integer.MAX_VALUE, limit);
            limit = Math.max(-1, limit);
            if (limit == -1)
                limit = Long.MAX_VALUE;
            segmentLimit = limit;
        }

        final List<String> passFiles;  // parsed pack.pass.file options
        {
            // Which class files will be passed through?
            passFiles = props.getProperties(Pack200.Packer.PASS_FILE_PFX);
            for (ListIterator<String> i = passFiles.listIterator(); i.hasNext(); ) {
                String file = i.next();
                if (file == null) { i.remove(); continue; }
                file = Utils.getJarEntryName(file);  // normalize '\\' to '/'
                if (file.endsWith("/"))
                    file = file.substring(0, file.length()-1);
                i.set(file);
            }
            if (verbose > 0) Utils.log.info("passFiles = " + passFiles);
        }

        {
            // Hook for testing:  Forces use of special archive modes.
            int opt = props.getInteger(Utils.COM_PREFIX+"archive.options");
            if (opt != 0)
                pkg.default_options |= opt;
        }

        // (Done collecting options from props.)

        boolean isClassFile(String name) {
            if (!name.endsWith(".class"))  return false;
            for (String prefix = name; ; ) {
                if (passFiles.contains(prefix))  return false;
                int chop = prefix.lastIndexOf('/');
                if (chop < 0)  break;
                prefix = prefix.substring(0, chop);
            }
            return true;
        }

        boolean isMetaInfFile(String name) {
            return name.startsWith("/" + Utils.METAINF) ||
                        name.startsWith(Utils.METAINF);
        }

        // Get a new package, based on the old one.
        private void makeNextPackage() {
            pkg.reset();
        }

        final class InFile {
            final String name;
            final JarFile jf;
            final JarEntry je;
            final File f;
            int modtime = Constants.NO_MODTIME;
            int options;
            InFile(String name) {
                this.name = Utils.getJarEntryName(name);
                this.f = new File(name);
                this.jf = null;
                this.je = null;
                int timeSecs = getModtime(f.lastModified());
                if (keepModtime && timeSecs != Constants.NO_MODTIME) {
                    this.modtime = timeSecs;
                } else if (latestModtime && timeSecs > pkg.default_modtime) {
                    pkg.default_modtime = timeSecs;
                }
            }
            InFile(JarFile jf, JarEntry je) {
                this.name = Utils.getJarEntryName(je.getName());
                this.f = null;
                this.jf = jf;
                this.je = je;
                int timeSecs = getModtime(je.getTime());
                if (keepModtime && timeSecs != Constants.NO_MODTIME) {
                     this.modtime = timeSecs;
                } else if (latestModtime && timeSecs > pkg.default_modtime) {
                    pkg.default_modtime = timeSecs;
                }
                if (keepDeflateHint && je.getMethod() == JarEntry.DEFLATED) {
                    options |= Constants.FO_DEFLATE_HINT;
                }
            }
            InFile(JarEntry je) {
                this(null, je);
            }
            long getInputLength() {
                long len = (je != null)? je.getSize(): f.length();
                assert(len >= 0) : this+".len="+len;
                // Bump size by pathname length and modtime/def-hint bytes.
                return Math.max(0, len) + name.length() + 5;
            }
            int getModtime(long timeMillis) {
                // Convert milliseconds to seconds.
                long seconds = (timeMillis+500) / 1000;
                if ((int)seconds == seconds) {
                    return (int)seconds;
                } else {
                    Utils.log.warning("overflow in modtime for "+f);
                    return Constants.NO_MODTIME;
                }
            }
            void copyTo(Package.File file) {
                if (modtime != Constants.NO_MODTIME)
                    file.modtime = modtime;
                file.options |= options;
            }
            InputStream getInputStream() throws IOException {
                if (jf != null)
                    return jf.getInputStream(je);
                else
                    return new FileInputStream(f);
            }

            public String toString() {
                return name;
            }
        }

        private int nread = 0;  // used only if (verbose > 0)
        private void noteRead(InFile f) {
            nread++;
            if (verbose > 2)
                Utils.log.fine("...read "+f.name);
            if (verbose > 0 && (nread % 1000) == 0)
                Utils.log.info("Have read "+nread+" files...");
        }

        void run(JarInputStream in, OutputStream out) throws IOException {
            // First thing we do is get the manifest, as JIS does
            // not provide the Manifest as an entry.
            if (in.getManifest() != null) {
                ByteArrayOutputStream tmp = new ByteArrayOutputStream();
                in.getManifest().write(tmp);
                InputStream tmpIn = new ByteArrayInputStream(tmp.toByteArray());
                pkg.addFile(readFile(JarFile.MANIFEST_NAME, tmpIn));
            }
            for (JarEntry je; (je = in.getNextJarEntry()) != null; ) {
                InFile inFile = new InFile(je);

                String name = inFile.name;
                Package.File bits = readFile(name, in);
                Package.File file = null;
                // (5078608) : discount the resource files in META-INF
                // from segment computation.
                long inflen = (isMetaInfFile(name))
                              ? 0L
                              : inFile.getInputLength();

                if ((segmentSize += inflen) > segmentLimit) {
                    segmentSize -= inflen;
                    int nextCount = -1;  // don't know; it's a stream
                    flushPartial(out, nextCount);
                }
                if (verbose > 1) {
                    Utils.log.fine("Reading " + name);
                }

                assert(je.isDirectory() == name.endsWith("/"));

                if (isClassFile(name)) {
                    file = readClass(name, bits.getInputStream());
                }
                if (file == null) {
                    file = bits;
                    pkg.addFile(file);
                }
                inFile.copyTo(file);
                noteRead(inFile);
            }
            flushAll(out);
        }

        void run(JarFile in, OutputStream out) throws IOException {
            List<InFile> inFiles = scanJar(in);

            if (verbose > 0)
                Utils.log.info("Reading " + inFiles.size() + " files...");

            int numDone = 0;
            for (InFile inFile : inFiles) {
                String name      = inFile.name;
                // (5078608) : discount the resource files completely from segmenting
                long inflen = (isMetaInfFile(name))
                               ? 0L
                               : inFile.getInputLength() ;
                if ((segmentSize += inflen) > segmentLimit) {
                    segmentSize -= inflen;
                    // Estimate number of remaining segments:
                    float filesDone = numDone+1;
                    float segsDone  = segmentCount+1;
                    float filesToDo = inFiles.size() - filesDone;
                    float segsToDo  = filesToDo * (segsDone/filesDone);
                    if (verbose > 1)
                        Utils.log.fine("Estimated segments to do: "+segsToDo);
                    flushPartial(out, (int) Math.ceil(segsToDo));
                }
                InputStream strm = inFile.getInputStream();
                if (verbose > 1)
                    Utils.log.fine("Reading " + name);
                Package.File file = null;
                if (isClassFile(name)) {
                    file = readClass(name, strm);
                    if (file == null) {
                        strm.close();
                        strm = inFile.getInputStream();
                    }
                }
                if (file == null) {
                    file = readFile(name, strm);
                    pkg.addFile(file);
                }
                inFile.copyTo(file);
                strm.close();  // tidy up
                noteRead(inFile);
                numDone += 1;
            }
            flushAll(out);
        }

        Package.File readClass(String fname, InputStream in) throws IOException {
            Package.Class cls = pkg.new Class(fname);
            in = new BufferedInputStream(in);
            ClassReader reader = new ClassReader(cls, in);
            reader.setAttrDefs(attrDefs);
            reader.setAttrCommands(attrCommands);
            reader.unknownAttrCommand = unknownAttrCommand;
            try {
                reader.read();
            } catch (IOException ioe) {
                String message = "Passing class file uncompressed due to";
                if (ioe instanceof Attribute.FormatException) {
                    Attribute.FormatException ee = (Attribute.FormatException) ioe;
                    // He passed up the category to us in layout.
                    if (ee.layout.equals(Pack200.Packer.PASS)) {
                        Utils.log.info(ee.toString());
                        Utils.log.warning(message + " unrecognized attribute: " +
                                fname);
                        return null;
                    }
                } else if (ioe instanceof ClassReader.ClassFormatException) {
                    ClassReader.ClassFormatException ce = (ClassReader.ClassFormatException) ioe;
                    if (classFormatCommand.equals(Pack200.Packer.PASS)) {
                        Utils.log.info(ce.toString());
                        Utils.log.warning(message + " unknown class format: " +
                                fname);
                        return null;
                    }
                }
                // Otherwise, it must be an error.
                throw ioe;
            }
            pkg.addClass(cls);
            return cls.file;
        }

        // Read raw data.
        Package.File readFile(String fname, InputStream in) throws IOException {

            Package.File file = pkg.new File(fname);
            file.readFrom(in);
            if (file.isDirectory() && file.getFileLength() != 0)
                throw new IllegalArgumentException("Non-empty directory: "+file.getFileName());
            return file;
        }

        void flushPartial(OutputStream out, int nextCount) throws IOException {
            if (pkg.files.isEmpty() && pkg.classes.isEmpty()) {
                return;  // do not flush an empty segment
            }
            flushPackage(out, Math.max(1, nextCount));
            props.setInteger(Pack200.Packer.PROGRESS, 25);
            // In case there will be another segment:
            makeNextPackage();
            segmentCount += 1;
            segmentTotalSize += segmentSize;
            segmentSize = 0;
        }

        void flushAll(OutputStream out) throws IOException {
            props.setInteger(Pack200.Packer.PROGRESS, 50);
            flushPackage(out, 0);
            out.flush();
            props.setInteger(Pack200.Packer.PROGRESS, 100);
            segmentCount += 1;
            segmentTotalSize += segmentSize;
            segmentSize = 0;
            if (verbose > 0 && segmentCount > 1) {
                Utils.log.info("Transmitted "
                                 +segmentTotalSize+" input bytes in "
                                 +segmentCount+" segments totaling "
                                 +totalOutputSize+" bytes");
            }
        }


        /** Write all information in the current package segment
         *  to the output stream.
         */
        void flushPackage(OutputStream out, int nextCount) throws IOException {
            int nfiles = pkg.files.size();
            if (!keepFileOrder) {
                // Keeping the order of classes costs about 1%
                // Keeping the order of all files costs something more.
                if (verbose > 1)  Utils.log.fine("Reordering files.");
                boolean stripDirectories = true;
                pkg.reorderFiles(keepClassOrder, stripDirectories);
            } else {
                // Package builder must have created a stub for each class.
                assert(pkg.files.containsAll(pkg.getClassStubs()));
                // Order of stubs in file list must agree with classes.
                List<Package.File> res = pkg.files;
                assert((res = new ArrayList<>(pkg.files))
                       .retainAll(pkg.getClassStubs()) || true);
                assert(res.equals(pkg.getClassStubs()));
            }
            pkg.trimStubs();

            // Do some stripping, maybe.
            if (props.getBoolean(Utils.COM_PREFIX+"strip.debug"))        pkg.stripAttributeKind("Debug");
            if (props.getBoolean(Utils.COM_PREFIX+"strip.compile"))      pkg.stripAttributeKind("Compile");
            if (props.getBoolean(Utils.COM_PREFIX+"strip.constants"))    pkg.stripAttributeKind("Constant");
            if (props.getBoolean(Utils.COM_PREFIX+"strip.exceptions"))   pkg.stripAttributeKind("Exceptions");
            if (props.getBoolean(Utils.COM_PREFIX+"strip.innerclasses")) pkg.stripAttributeKind("InnerClasses");

            PackageWriter pw = new PackageWriter(pkg, out);
            pw.archiveNextCount = nextCount;
            pw.write();
            out.flush();
            if (verbose > 0) {
                long outSize = pw.archiveSize0+pw.archiveSize1;
                totalOutputSize += outSize;
                long inSize = segmentSize;
                Utils.log.info("Transmitted "
                                 +nfiles+" files of "
                                 +inSize+" input bytes in a segment of "
                                 +outSize+" bytes");
            }
        }

        List<InFile> scanJar(JarFile jf) throws IOException {
            // Collect jar entries, preserving order.
            List<InFile> inFiles = new ArrayList<>();
            try {
                for (JarEntry je : Collections.list(jf.entries())) {
                    InFile inFile = new InFile(jf, je);
                    assert(je.isDirectory() == inFile.name.endsWith("/"));
                    inFiles.add(inFile);
                }
            } catch (IllegalStateException ise) {
                throw new IOException(ise.getLocalizedMessage(), ise);
            }
            return inFiles;
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java PackerImpl.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.