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}