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