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