    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.java.util.jar;
    020
    021import java.beans.PropertyChangeListener;
    022import java.io.File;
    023import java.io.IOException;
    024import java.io.InputStream;
    025import java.io.OutputStream;
    026import java.security.AccessController;
    027import java.security.PrivilegedAction;
    028import java.util.Objects;
    029import java.util.SortedMap;
    030import java.util.jar.JarFile;
    031import java.util.jar.JarInputStream;
    032import java.util.jar.JarOutputStream;
    033
    034import org.apache.commons.compress.harmony.archive.internal.nls.Messages;
    035import org.apache.commons.io.input.BoundedInputStream;
    036
    037/**
    038 * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}.
    039 */
    040public abstract class Pack200 {
    041
    042    /**
    043     * The interface defining the API for converting a JAR file to an output stream in the Pack200 format.
    044     */
    045    public interface Packer {
    046
    047        /**
    048         * The format of a class attribute name.
    049         */
    050        String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$
    051
    052        /**
    053         * The format of a code attribute name.
    054         */
    055        String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$
    056
    057        /**
    058         * The deflation hint to set in the output archive.
    059         */
    060        String DEFLATE_HINT = "pack.deflate.hint"; //$NON-NLS-1$
    061
    062        /**
    063         * The indicated amount of effort to use in compressing the archive.
    064         */
    065        String EFFORT = "pack.effort"; //$NON-NLS-1$
    066
    067        /**
    068         * a String representation for {@code error}.
    069         */
    070        String ERROR = "error"; //$NON-NLS-1$
    071
    072        /**
    073         * a String representation of {@code false}.
    074         */
    075        String FALSE = "false"; //$NON-NLS-1$
    076
    077        /**
    078         * The format of a field attribute name.
    079         */
    080        String FIELD_ATTRIBUTE_PFX = "pack.field.attribute."; //$NON-NLS-1$
    081
    082        /**
    083         * The String representation for {@code keep}.
    084         */
    085        String KEEP = "keep"; //$NON-NLS-1$
    086
    087        /**
    088         * Decide if all elements shall transmit in their original order.
    089         */
    090        String KEEP_FILE_ORDER = "pack.keep.file.order"; //$NON-NLS-1$
    091
    092        /**
    093         * The String representation for {@code latest}.
    094         */
    095        String LATEST = "latest"; //$NON-NLS-1$
    096
    097        /**
    098         * The format of a method attribute name.
    099         */
    100        String METHOD_ATTRIBUTE_PFX = "pack.method.attribute."; //$NON-NLS-1$
    101
    102        /**
    103         * If it shall attempt to determine the latest modification time if this is set to {@code LATEST}.
    104         */
    105        String MODIFICATION_TIME = "pack.modification.time"; //$NON-NLS-1$
    106
    107        /**
    108         * The String representation of {@code pass}.
    109         */
    110        String PASS = "pass"; //$NON-NLS-1$
    111
    112        /**
    113         * The file that will not be compressed.
    114         */
    115        String PASS_FILE_PFX = "pack.pass.file."; //$NON-NLS-1$
    116
    117        /**
    118         * Packer progress as a percentage.
    119         */
    120        String PROGRESS = "pack.progress"; //$NON-NLS-1$
    121
    122        /**
    123         * The number of bytes of each archive segment.
    124         */
    125        String SEGMENT_LIMIT = "pack.segment.limit"; //$NON-NLS-1$
    126
    127        /**
    128         * The String representation of {@code strip}.
    129         */
    130        String STRIP = "strip"; //$NON-NLS-1$
    131
    132        /**
    133         * The String representation of {@code true}.
    134         */
    135        String TRUE = "true"; //$NON-NLS-1$
    136
    137        /**
    138         * The action to take if an unknown attribute is encountered.
    139         */
    140        String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute"; //$NON-NLS-1$
    141
    142        /**
    143         * Adds a listener for PropertyChange events
    144         *
    145         * @param listener the listener to listen if PropertyChange events occurs
    146         */
    147        void addPropertyChangeListener(PropertyChangeListener listener);
    148
    149        /**
    150         * Packs the specified JAR file to the specified output stream.
    151         *
    152         * @param in  JAR file to be compressed.
    153         * @param out target output stream for the compressed data.
    154         * @throws IOException if I/O exception occurs.
    155         */
    156        void pack(JarFile in, OutputStream out) throws IOException;
    157
    158        /**
    159         * Packs the data from the specified jar input stream to the specified output stream.
    160         *
    161         * @param in  input stream of uncompressed JAR data.
    162         * @param out target output stream for the compressed data.
    163         * @throws IOException if I/O exception occurs.
    164         */
    165        void pack(JarInputStream in, OutputStream out) throws IOException;
    166
    167        /**
    168         * Gets a sorted map of the properties of this packer.
    169         *
    170         * @return the properties of the packer.
    171         */
    172        SortedMap<String, String> properties();
    173
    174        /**
    175         * Removes a listener
    176         *
    177         * @param listener listener to remove
    178         */
    179        void removePropertyChangeListener(PropertyChangeListener listener);
    180    }
    181
    182    /**
    183     * The interface defining the API for converting a packed stream in the Pack200 format to a JAR file.
    184     */
    185    public interface Unpacker {
    186
    187        /**
    188         * The String indicating if the unpacker should ignore all transmitted values, can be replaced by either {@code true} or {@code false}.
    189         */
    190        String DEFLATE_HINT = "unpack.deflate.hint"; //$NON-NLS-1$
    191
    192        /**
    193         * a String representation of {@code false}.
    194         */
    195        String FALSE = "false"; //$NON-NLS-1$
    196
    197        /**
    198         * a String representation of {@code keep}.
    199         */
    200        String KEEP = "keep"; //$NON-NLS-1$
    201
    202        /**
    203         * The progress as a {@code percentage}.
    204         */
    205        String PROGRESS = "unpack.progress"; //$NON-NLS-1$
    206
    207        /**
    208         * a String representation of {@code true}.
    209         */
    210        String TRUE = "true"; //$NON-NLS-1$
    211
    212        /**
    213         * Adds a listener for {@code PropertyChange} events.
    214         *
    215         * @param listener the listener to listen if {@code PropertyChange} events occurs.
    216         */
    217        void addPropertyChangeListener(PropertyChangeListener listener);
    218
    219        /**
    220         * Gets a sorted map of the properties of this unpacker.
    221         *
    222         * @return the properties of unpacker.
    223         */
    224        SortedMap<String, String> properties();
    225
    226        /**
    227         * Removes a listener.
    228         *
    229         * @param listener listener to remove.
    230         */
    231        void removePropertyChangeListener(PropertyChangeListener listener);
    232
    233        /**
    234         * Unpacks the contents of the specified {@code File} to the specified JAR output stream.
    235         *
    236         * @param in  file to uncompress.
    237         * @param out JAR output stream of uncompressed data.
    238         * @throws IOException if I/O exception occurs.
    239         */
    240        void unpack(File in, JarOutputStream out) throws IOException;
    241
    242        /**
    243         * Unpacks the specified stream to the specified JAR output stream.
    244         *
    245         * @param in  stream to uncompress, preferably a {@link BoundedInputStream}.
    246         * @param out JAR output stream of uncompressed data.
    247         * @throws IOException if I/O exception occurs.
    248         */
    249        void unpack(InputStream in, JarOutputStream out) throws IOException;
    250
    251    }
    252
    253    /**
    254     * System property key.
    255     */
    256    private static final String SYSTEM_PROPERTY_PACKER = "java.util.jar.Pack200.Packer"; //$NON-NLS-1$
    257
    258    /**
    259     * System property key.
    260     */
    261    private static final String SYSTEM_PROPERTY_UNPACKER = "java.util.jar.Pack200.Unpacker"; //$NON-NLS-1$
    262
    263    static Object newInstance(final String systemProperty, final String defaultClassName) {
    264        return AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    265            final String className = System.getProperty(systemProperty, defaultClassName);
    266            try {
    267                // TODO Not sure if this will cause problems loading the class
    268                ClassLoader classLoader = Pack200.class.getClassLoader();
    269                if (classLoader == null) {
    270                    classLoader = Objects.requireNonNull(ClassLoader.getSystemClassLoader(), "ClassLoader.getSystemClassLoader()");
    271                }
    272                return classLoader.loadClass(className).getConstructor().newInstance();
    273            } catch (final Exception e) {
    274                throw new Error(Messages.getString("archive.3E", className), e); //$NON-NLS-1$
    275            }
    276        });
    277    }
    278
    279    /**
    280     * Returns a new instance of a packer engine.
    281     * <p>
    282     * The implementation of the packer engine is defined by the system property {@code 'java.util.jar.Pack200.Packer'}. If this system property is defined an
    283     * instance of the specified class is returned, otherwise the system's default implementation is returned.
    284     * </p>
    285     *
    286     * @return an instance of {@code Packer}
    287     */
    288    public static Pack200.Packer newPacker() {
    289        return (Packer) newInstance(SYSTEM_PROPERTY_PACKER, "org.apache.commons.compress.harmony.pack200.Pack200PackerAdapter"); //$NON-NLS-1$
    290    }
    291
    292    /**
    293     * Returns a new instance of an unpacker engine.
    294     * <p>
    295     * The implementation of the unpacker engine is defined by the system property {@link Pack200.Unpacker}. If this system property is defined an instance of
    296     * the specified class is returned, otherwise the system's default implementation is returned.
    297     * </p>
    298     *
    299     * @return an instance of {@link Pack200.Unpacker}.
    300     */
    301    public static Pack200.Unpacker newUnpacker() {
    302        return (Unpacker) newInstance(SYSTEM_PROPERTY_UNPACKER, "org.apache.commons.compress.harmony.unpack200.Pack200UnpackerAdapter"); //$NON-NLS-1$
    303    }
    304
    305    /**
    306     * Prevents this class from being instantiated.
    307     */
    308    private Pack200() {
    309        // do nothing
    310    }
    311
    312}