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}