• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 package org.apache.commons.compress.compressors.pack200;
21 
22 import java.io.File;
23 import java.io.FilterInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.Map;
27 import java.util.jar.JarOutputStream;
28 import java.util.jar.Pack200;
29 
30 import org.apache.commons.compress.compressors.CompressorInputStream;
31 import org.apache.commons.compress.utils.IOUtils;
32 
33 /**
34  * An input stream that decompresses from the Pack200 format to be read
35  * as any other stream.
36  *
37  * <p>The {@link CompressorInputStream#getCount getCount} and {@link
38  * CompressorInputStream#getBytesRead getBytesRead} methods always
39  * return 0.</p>
40  *
41  * @NotThreadSafe
42  * @since 1.3
43  */
44 public class Pack200CompressorInputStream extends CompressorInputStream {
45     private final InputStream originalInput;
46     private final StreamBridge streamBridge;
47 
48     /**
49      * Decompresses the given stream, caching the decompressed data in
50      * memory.
51      *
52      * <p>When reading from a file the File-arg constructor may
53      * provide better performance.</p>
54      *
55      * @param in the InputStream from which this object should be created
56      * @throws IOException if reading fails
57      */
Pack200CompressorInputStream(final InputStream in)58     public Pack200CompressorInputStream(final InputStream in)
59         throws IOException {
60         this(in, Pack200Strategy.IN_MEMORY);
61     }
62 
63     /**
64      * Decompresses the given stream using the given strategy to cache
65      * the results.
66      *
67      * <p>When reading from a file the File-arg constructor may
68      * provide better performance.</p>
69      *
70      * @param in the InputStream from which this object should be created
71      * @param mode the strategy to use
72      * @throws IOException if reading fails
73      */
Pack200CompressorInputStream(final InputStream in, final Pack200Strategy mode)74     public Pack200CompressorInputStream(final InputStream in,
75                                         final Pack200Strategy mode)
76         throws IOException {
77         this(in, null, mode, null);
78     }
79 
80     /**
81      * Decompresses the given stream, caching the decompressed data in
82      * memory and using the given properties.
83      *
84      * <p>When reading from a file the File-arg constructor may
85      * provide better performance.</p>
86      *
87      * @param in the InputStream from which this object should be created
88      * @param props Pack200 properties to use
89      * @throws IOException if reading fails
90      */
Pack200CompressorInputStream(final InputStream in, final Map<String, String> props)91     public Pack200CompressorInputStream(final InputStream in,
92                                         final Map<String, String> props)
93         throws IOException {
94         this(in, Pack200Strategy.IN_MEMORY, props);
95     }
96 
97     /**
98      * Decompresses the given stream using the given strategy to cache
99      * the results and the given properties.
100      *
101      * <p>When reading from a file the File-arg constructor may
102      * provide better performance.</p>
103      *
104      * @param in the InputStream from which this object should be created
105      * @param mode the strategy to use
106      * @param props Pack200 properties to use
107      * @throws IOException if reading fails
108      */
Pack200CompressorInputStream(final InputStream in, final Pack200Strategy mode, final Map<String, String> props)109     public Pack200CompressorInputStream(final InputStream in,
110                                         final Pack200Strategy mode,
111                                         final Map<String, String> props)
112         throws IOException {
113         this(in, null, mode, props);
114     }
115 
116     /**
117      * Decompresses the given file, caching the decompressed data in
118      * memory.
119      *
120      * @param f the file to decompress
121      * @throws IOException if reading fails
122      */
Pack200CompressorInputStream(final File f)123     public Pack200CompressorInputStream(final File f) throws IOException {
124         this(f, Pack200Strategy.IN_MEMORY);
125     }
126 
127     /**
128      * Decompresses the given file using the given strategy to cache
129      * the results.
130      *
131      * @param f the file to decompress
132      * @param mode the strategy to use
133      * @throws IOException if reading fails
134      */
Pack200CompressorInputStream(final File f, final Pack200Strategy mode)135     public Pack200CompressorInputStream(final File f, final Pack200Strategy mode)
136         throws IOException {
137         this(null, f, mode, null);
138     }
139 
140     /**
141      * Decompresses the given file, caching the decompressed data in
142      * memory and using the given properties.
143      *
144      * @param f the file to decompress
145      * @param props Pack200 properties to use
146      * @throws IOException if reading fails
147      */
Pack200CompressorInputStream(final File f, final Map<String, String> props)148     public Pack200CompressorInputStream(final File f,
149                                         final Map<String, String> props)
150         throws IOException {
151         this(f, Pack200Strategy.IN_MEMORY, props);
152     }
153 
154     /**
155      * Decompresses the given file using the given strategy to cache
156      * the results and the given properties.
157      *
158      * @param f the file to decompress
159      * @param mode the strategy to use
160      * @param props Pack200 properties to use
161      * @throws IOException if reading fails
162      */
Pack200CompressorInputStream(final File f, final Pack200Strategy mode, final Map<String, String> props)163     public Pack200CompressorInputStream(final File f, final Pack200Strategy mode,
164                                         final Map<String, String> props)
165         throws IOException {
166         this(null, f, mode, props);
167     }
168 
Pack200CompressorInputStream(final InputStream in, final File f, final Pack200Strategy mode, final Map<String, String> props)169     private Pack200CompressorInputStream(final InputStream in, final File f,
170                                          final Pack200Strategy mode,
171                                          final Map<String, String> props)
172             throws IOException {
173         originalInput = in;
174         streamBridge = mode.newStreamBridge();
175         try (final JarOutputStream jarOut = new JarOutputStream(streamBridge)) {
176             final Pack200.Unpacker u = Pack200.newUnpacker();
177             if (props != null) {
178                 u.properties().putAll(props);
179             }
180             if (f == null) {
181                 u.unpack(new FilterInputStream(in) {
182                     @Override
183                     public void close() {
184                         // unpack would close this stream but we
185                         // want to give the user code more control
186                     }
187                 }, jarOut);
188             } else {
189                 u.unpack(f, jarOut);
190             }
191         }
192     }
193 
194     @Override
read()195     public int read() throws IOException {
196         return streamBridge.getInput().read();
197     }
198 
199     @Override
read(final byte[] b)200     public int read(final byte[] b) throws IOException {
201         return streamBridge.getInput().read(b);
202     }
203 
204     @Override
read(final byte[] b, final int off, final int count)205     public int read(final byte[] b, final int off, final int count) throws IOException {
206         return streamBridge.getInput().read(b, off, count);
207     }
208 
209     @Override
available()210     public int available() throws IOException {
211         return streamBridge.getInput().available();
212     }
213 
214     @Override
markSupported()215     public boolean markSupported() {
216         try {
217             return streamBridge.getInput().markSupported();
218         } catch (final IOException ex) {
219             return false;
220         }
221     }
222 
223     @Override
mark(final int limit)224     public void mark(final int limit) {
225         try {
226             streamBridge.getInput().mark(limit);
227         } catch (final IOException ex) {
228             throw new RuntimeException(ex); //NOSONAR
229         }
230     }
231 
232     @Override
reset()233     public void reset() throws IOException {
234         streamBridge.getInput().reset();
235     }
236 
237     @Override
skip(final long count)238     public long skip(final long count) throws IOException {
239         return IOUtils.skip(streamBridge.getInput(), count);
240     }
241 
242     @Override
close()243     public void close() throws IOException {
244         try {
245             streamBridge.stop();
246         } finally {
247             if (originalInput != null) {
248                 originalInput.close();
249             }
250         }
251     }
252 
253     private static final byte[] CAFE_DOOD = new byte[] {
254         (byte) 0xCA, (byte) 0xFE, (byte) 0xD0, (byte) 0x0D
255     };
256     private static final int SIG_LENGTH = CAFE_DOOD.length;
257 
258     /**
259      * Checks if the signature matches what is expected for a pack200
260      * file (0xCAFED00D).
261      *
262      * @param signature
263      *            the bytes to check
264      * @param length
265      *            the number of bytes to check
266      * @return true, if this stream is a pack200 compressed stream,
267      * false otherwise
268      */
matches(final byte[] signature, final int length)269     public static boolean matches(final byte[] signature, final int length) {
270         if (length < SIG_LENGTH) {
271             return false;
272         }
273 
274         for (int i = 0; i < SIG_LENGTH; i++) {
275             if (signature[i] != CAFE_DOOD[i]) {
276                 return false;
277             }
278         }
279 
280         return true;
281     }
282 }
283