1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.harmony.unpack200;
20
21 import java.io.BufferedInputStream;
22 import java.io.BufferedOutputStream;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.util.jar.JarEntry;
32 import java.util.jar.JarInputStream;
33 import java.util.jar.JarOutputStream;
34 import java.util.zip.GZIPInputStream;
35
36 import org.apache.commons.compress.harmony.pack200.Pack200Exception;
37 import org.apache.commons.io.IOUtils;
38 import org.apache.commons.io.input.BoundedInputStream;
39
40
41
42
43
44 public class Archive {
45
46 private static final int[] MAGIC = { 0xCA, 0xFE, 0xD0, 0x0D };
47
48 private BoundedInputStream inputStream;
49
50 private final JarOutputStream outputStream;
51
52 private boolean removePackFile;
53
54 private int logLevel = Segment.LOG_LEVEL_STANDARD;
55
56 private FileOutputStream logFile;
57
58 private boolean overrideDeflateHint;
59
60 private boolean deflateHint;
61
62 private final Path inputPath;
63
64 private final long inputSize;
65
66 private final String outputFileName;
67
68 private final boolean closeStreams;
69
70
71
72
73
74
75
76
77
78 public Archive(final InputStream inputStream, final JarOutputStream outputStream) throws IOException {
79 this.inputStream = Pack200UnpackerAdapter.newBoundedInputStream(inputStream);
80 this.outputStream = outputStream;
81 if (inputStream instanceof FileInputStream) {
82 inputPath = Paths.get(Pack200UnpackerAdapter.readPathString((FileInputStream) inputStream));
83 } else {
84 inputPath = null;
85 }
86 this.outputFileName = null;
87 this.inputSize = -1;
88 this.closeStreams = false;
89 }
90
91
92
93
94
95
96
97
98
99 @SuppressWarnings("resource")
100 public Archive(final String inputFileName, final String outputFileName) throws FileNotFoundException, IOException {
101 this.inputPath = Paths.get(inputFileName);
102 this.inputSize = Files.size(this.inputPath);
103 this.inputStream = BoundedInputStream.builder().setPath(inputPath).setMaxCount(inputSize).get();
104 this.outputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(outputFileName)));
105 this.outputFileName = outputFileName;
106 this.closeStreams = true;
107 }
108
109 private boolean available(final InputStream inputStream) throws IOException {
110 inputStream.mark(1);
111 final int check = inputStream.read();
112 inputStream.reset();
113 return check != -1;
114 }
115
116
117
118
119
120
121 public void setDeflateHint(final boolean deflateHint) {
122 overrideDeflateHint = true;
123 this.deflateHint = deflateHint;
124 }
125
126
127
128
129
130
131
132
133 public void setLogFile(final String logFileName) throws FileNotFoundException {
134 logFile = new FileOutputStream(logFileName);
135 }
136
137
138
139
140
141
142
143
144
145 public void setLogFile(final String logFileName, final boolean append) throws FileNotFoundException {
146 logFile = new FileOutputStream(logFileName, append);
147 }
148
149
150
151
152
153
154 public void setQuiet(final boolean quiet) {
155 if (quiet || logLevel == Segment.LOG_LEVEL_QUIET) {
156 logLevel = Segment.LOG_LEVEL_QUIET;
157 }
158 }
159
160
161
162
163
164
165 public void setRemovePackFile(final boolean removePackFile) {
166 this.removePackFile = removePackFile;
167 }
168
169
170
171
172
173
174 public void setVerbose(final boolean verbose) {
175 if (verbose) {
176 logLevel = Segment.LOG_LEVEL_VERBOSE;
177 } else if (logLevel == Segment.LOG_LEVEL_VERBOSE) {
178 logLevel = Segment.LOG_LEVEL_STANDARD;
179 }
180 }
181
182
183
184
185
186
187
188 public void unpack() throws Pack200Exception, IOException {
189 outputStream.setComment("PACK200");
190 try {
191 if (!inputStream.markSupported()) {
192 inputStream = new BoundedInputStream(new BufferedInputStream(inputStream));
193 if (!inputStream.markSupported()) {
194 throw new IllegalStateException();
195 }
196 }
197 inputStream.mark(2);
198 if ((inputStream.read() & 0xFF | (inputStream.read() & 0xFF) << 8) == GZIPInputStream.GZIP_MAGIC) {
199 inputStream.reset();
200 inputStream = new BoundedInputStream(new BufferedInputStream(new GZIPInputStream(inputStream)));
201 } else {
202 inputStream.reset();
203 }
204 inputStream.mark(MAGIC.length);
205
206 final int[] word = new int[MAGIC.length];
207 for (int i = 0; i < word.length; i++) {
208 word[i] = inputStream.read();
209 }
210 boolean compressedWithE0 = false;
211 for (int m = 0; m < MAGIC.length; m++) {
212 if (word[m] != MAGIC[m]) {
213 compressedWithE0 = true;
214 break;
215 }
216 }
217 inputStream.reset();
218 if (compressedWithE0) {
219 final JarInputStream jarInputStream = new JarInputStream(inputStream);
220 JarEntry jarEntry;
221 while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
222 outputStream.putNextEntry(jarEntry);
223 IOUtils.copy(jarInputStream, outputStream, 16_384);
224 outputStream.closeEntry();
225 }
226 } else {
227 int i = 0;
228 while (available(inputStream)) {
229 i++;
230 final Segment segment = new Segment();
231 segment.setLogLevel(logLevel);
232 segment.setLogStream(logFile);
233 segment.setPreRead(false);
234 if (i == 1) {
235 segment.log(Segment.LOG_LEVEL_VERBOSE, "Unpacking from " + inputPath + " to " + outputFileName);
236 }
237 segment.log(Segment.LOG_LEVEL_VERBOSE, "Reading segment " + i);
238 if (overrideDeflateHint) {
239 segment.overrideDeflateHint(deflateHint);
240 }
241 segment.unpack(inputStream, outputStream);
242 outputStream.flush();
243 }
244 }
245 } finally {
246 if (closeStreams) {
247 IOUtils.closeQuietly(inputStream);
248 IOUtils.closeQuietly(outputStream);
249 }
250 IOUtils.closeQuietly(logFile);
251 }
252 if (removePackFile && inputPath != null) {
253 Files.delete(inputPath);
254 }
255 }
256
257 }