source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 14.0 KB
Line 
1/*
2 * Copyright 2000,2002-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17/**
18 * jlink.java links together multiple .jar files Original code by Patrick
19 * Beard. Modifications to work with ANT by Matthew Kuperus Heun.
20 *
21 */
22package org.apache.tools.ant.taskdefs.optional.jlink;
23
24import java.io.BufferedInputStream;
25import java.io.File;
26import java.io.FileInputStream;
27import java.io.FileOutputStream;
28import java.io.IOException;
29import java.io.InputStream;
30import java.util.Enumeration;
31import java.util.Vector;
32import java.util.zip.CRC32;
33import java.util.zip.Deflater;
34import java.util.zip.ZipEntry;
35import java.util.zip.ZipException;
36import java.util.zip.ZipFile;
37import java.util.zip.ZipOutputStream;
38
39public class jlink extends Object {
40
41 /** The file that will be created by this instance of jlink. */
42 public void setOutfile(String outfile) {
43 if (outfile == null) {
44 return;
45 }
46 this.outfile = outfile;
47 }
48
49
50 /** Adds a file to be merged into the output. */
51 public void addMergeFile(String mergefile) {
52 if (mergefile == null) {
53 return;
54 }
55 mergefiles.addElement(mergefile);
56 }
57
58
59 /** Adds a file to be added into the output. */
60 public void addAddFile(String addfile) {
61 if (addfile == null) {
62 return;
63 }
64 addfiles.addElement(addfile);
65 }
66
67
68 /** Adds several files to be merged into the output. */
69 public void addMergeFiles(String[] mergefiles) {
70 if (mergefiles == null) {
71 return;
72 }
73 for (int i = 0; i < mergefiles.length; i++) {
74 addMergeFile(mergefiles[i]);
75 }
76 }
77
78
79 /** Adds several file to be added into the output. */
80 public void addAddFiles(String[] addfiles) {
81 if (addfiles == null) {
82 return;
83 }
84 for (int i = 0; i < addfiles.length; i++) {
85 addAddFile(addfiles[i]);
86 }
87 }
88
89
90 /** Determines whether output will be compressed. */
91 public void setCompression(boolean compress) {
92 this.compression = compress;
93 }
94
95
96 /**
97 * Performs the linking of files. Addfiles are added to the output as-is.
98 * For example, a jar file is added to the output as a jar file. However,
99 * mergefiles are first examined for their type. If it is a jar or zip
100 * file, the contents will be extracted from the mergefile and entered
101 * into the output. If a zip or jar file is encountered in a subdirectory
102 * it will be added, not merged. If a directory is encountered, it becomes
103 * the root entry of all the files below it. Thus, you can provide
104 * multiple, disjoint directories, as addfiles: they will all be added in
105 * a rational manner to outfile.
106 */
107 public void link() throws Exception {
108 ZipOutputStream output = new ZipOutputStream(new FileOutputStream(outfile));
109
110 if (compression) {
111 output.setMethod(ZipOutputStream.DEFLATED);
112 output.setLevel(Deflater.DEFAULT_COMPRESSION);
113 } else {
114 output.setMethod(ZipOutputStream.STORED);
115 }
116
117 Enumeration merges = mergefiles.elements();
118
119 while (merges.hasMoreElements()) {
120 String path = (String) merges.nextElement();
121 File f = new File(path);
122
123 if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) {
124 //Do the merge
125 mergeZipJarContents(output, f);
126 } else {
127 //Add this file to the addfiles Vector and add it
128 //later at the top level of the output file.
129 addAddFile(path);
130 }
131 }
132
133 Enumeration adds = addfiles.elements();
134
135 while (adds.hasMoreElements()) {
136 String name = (String) adds.nextElement();
137 File f = new File(name);
138
139 if (f.isDirectory()) {
140 //System.out.println("in jlink: adding directory contents of " + f.getPath());
141 addDirContents(output, f, f.getName() + '/', compression);
142 } else {
143 addFile(output, f, "", compression);
144 }
145 }
146 if (output != null) {
147 try {
148 output.close();
149 } catch (IOException ioe) {
150 }
151 }
152 }
153
154
155 public static void main(String[] args) {
156 // jlink output input1 ... inputN
157 if (args.length < 2) {
158 System.out.println("usage: jlink output input1 ... inputN");
159 System.exit(1);
160 }
161 jlink linker = new jlink();
162
163 linker.setOutfile(args[0]);
164 // To maintain compatibility with the command-line version,
165 // we will only add files to be merged.
166 for (int i = 1; i < args.length; i++) {
167 linker.addMergeFile(args[i]);
168 }
169 try {
170 linker.link();
171 } catch (Exception ex) {
172 System.err.print(ex.getMessage());
173 }
174 }
175
176
177 /*
178 * Actually performs the merging of f into the output.
179 * f should be a zip or jar file.
180 */
181 private void mergeZipJarContents(ZipOutputStream output, File f) throws IOException {
182 //Check to see that the file with name "name" exists.
183 if (!f.exists()) {
184 return;
185 }
186 ZipFile zipf = new ZipFile(f);
187 Enumeration entries = zipf.entries();
188
189 while (entries.hasMoreElements()) {
190 ZipEntry inputEntry = (ZipEntry) entries.nextElement();
191 //Ignore manifest entries. They're bound to cause conflicts between
192 //files that are being merged. User should supply their own
193 //manifest file when doing the merge.
194 String inputEntryName = inputEntry.getName();
195 int index = inputEntryName.indexOf("META-INF");
196
197 if (index < 0) {
198 //META-INF not found in the name of the entry. Go ahead and process it.
199 try {
200 output.putNextEntry(processEntry(zipf, inputEntry));
201 } catch (ZipException ex) {
202 //If we get here, it could be because we are trying to put a
203 //directory entry that already exists.
204 //For example, we're trying to write "com", but a previous
205 //entry from another mergefile was called "com".
206 //In that case, just ignore the error and go on to the
207 //next entry.
208 String mess = ex.getMessage();
209
210 if (mess.indexOf("duplicate") >= 0) {
211 //It was the duplicate entry.
212 continue;
213 } else {
214 // I hate to admit it, but we don't know what happened
215 // here. Throw the Exception.
216 throw ex;
217 }
218 }
219
220 InputStream in = zipf.getInputStream(inputEntry);
221 int len = buffer.length;
222 int count = -1;
223
224 while ((count = in.read(buffer, 0, len)) > 0) {
225 output.write(buffer, 0, count);
226 }
227 in.close();
228 output.closeEntry();
229 }
230 }
231 zipf.close();
232 }
233
234
235 /*
236 * Adds contents of a directory to the output.
237 */
238 private void addDirContents(ZipOutputStream output, File dir, String prefix,
239 boolean compress) throws IOException {
240 String[] contents = dir.list();
241
242 for (int i = 0; i < contents.length; ++i) {
243 String name = contents[i];
244 File file = new File(dir, name);
245
246 if (file.isDirectory()) {
247 addDirContents(output, file, prefix + name + '/', compress);
248 } else {
249 addFile(output, file, prefix, compress);
250 }
251 }
252 }
253
254
255 /*
256 * Gets the name of an entry in the file. This is the real name
257 * which for a class is the name of the package with the class
258 * name appended.
259 */
260 private String getEntryName(File file, String prefix) {
261 String name = file.getName();
262
263 if (!name.endsWith(".class")) {
264 // see if the file is in fact a .class file, and determine its actual name.
265 InputStream input = null;
266 try {
267 input = new FileInputStream(file);
268 String className = ClassNameReader.getClassName(input);
269
270 if (className != null) {
271 return className.replace('.', '/') + ".class";
272 }
273 } catch (IOException ioe) {
274 } finally {
275 if (input != null) {
276 try {
277 input.close();
278 } catch (IOException e) {
279 }
280 }
281 }
282 }
283 System.out.println("From " + file.getPath() + " and prefix " + prefix
284 + ", creating entry " + prefix + name);
285 return (prefix + name);
286 }
287
288
289 /*
290 * Adds a file to the output stream.
291 */
292 private void addFile(ZipOutputStream output, File file, String prefix,
293 boolean compress) throws IOException {
294 //Make sure file exists
295 if (!file.exists()) {
296 return;
297 }
298 ZipEntry entry = new ZipEntry(getEntryName(file, prefix));
299
300 entry.setTime(file.lastModified());
301 entry.setSize(file.length());
302 if (!compress) {
303 entry.setCrc(calcChecksum(file));
304 }
305 FileInputStream input = new FileInputStream(file);
306
307 addToOutputStream(output, input, entry);
308 }
309
310
311 /*
312 * A convenience method that several other methods might call.
313 */
314 private void addToOutputStream(ZipOutputStream output, InputStream input,
315 ZipEntry ze) throws IOException {
316 try {
317 output.putNextEntry(ze);
318 } catch (ZipException zipEx) {
319 //This entry already exists. So, go with the first one.
320 input.close();
321 return;
322 }
323
324 int numBytes = -1;
325
326 while ((numBytes = input.read(buffer)) > 0) {
327 output.write(buffer, 0, numBytes);
328 }
329 output.closeEntry();
330 input.close();
331 }
332
333
334 /*
335 * A method that does the work on a given entry in a mergefile.
336 * The big deal is to set the right parameters in the ZipEntry
337 * on the output stream.
338 */
339 private ZipEntry processEntry(ZipFile zip, ZipEntry inputEntry) throws IOException {
340 /*
341 First, some notes.
342 On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the
343 ZipInputStream does not work for compressed (deflated) files. Those calls return -1.
344 For uncompressed (stored) files, those calls do work.
345 However, using ZipFile.getEntries() works for both compressed and
346 uncompressed files.
347
348 Now, from some simple testing I did, it seems that the value of CRC-32 is
349 independent of the compression setting. So, it should be easy to pass this
350 information on to the output entry.
351 */
352 String name = inputEntry.getName();
353
354 if (!(inputEntry.isDirectory() || name.endsWith(".class"))) {
355 try {
356 InputStream input = zip.getInputStream(zip.getEntry(name));
357 String className = ClassNameReader.getClassName(input);
358
359 input.close();
360 if (className != null) {
361 name = className.replace('.', '/') + ".class";
362 }
363 } catch (IOException ioe) {
364 }
365 }
366 ZipEntry outputEntry = new ZipEntry(name);
367
368 outputEntry.setTime(inputEntry.getTime());
369 outputEntry.setExtra(inputEntry.getExtra());
370 outputEntry.setComment(inputEntry.getComment());
371 outputEntry.setTime(inputEntry.getTime());
372 if (compression) {
373 outputEntry.setMethod(ZipEntry.DEFLATED);
374 //Note, don't need to specify size or crc for compressed files.
375 } else {
376 outputEntry.setMethod(ZipEntry.STORED);
377 outputEntry.setCrc(inputEntry.getCrc());
378 outputEntry.setSize(inputEntry.getSize());
379 }
380 return outputEntry;
381 }
382
383
384 /*
385 * Necessary in the case where you add a entry that
386 * is not compressed.
387 */
388 private long calcChecksum(File f) throws IOException {
389 BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
390
391 return calcChecksum(in);
392 }
393
394
395 /*
396 * Necessary in the case where you add a entry that
397 * is not compressed.
398 */
399 private long calcChecksum(InputStream in) throws IOException {
400 CRC32 crc = new CRC32();
401 int len = buffer.length;
402 int count = -1;
403 int haveRead = 0;
404
405 while ((count = in.read(buffer, 0, len)) > 0) {
406 haveRead += count;
407 crc.update(buffer, 0, count);
408 }
409 in.close();
410 return crc.getValue();
411 }
412
413
414 private String outfile = null;
415
416 private Vector mergefiles = new Vector(10);
417
418 private Vector addfiles = new Vector(10);
419
420 private boolean compression = false;
421
422 byte[] buffer = new byte[8192];
423
424}
425
426
Note: See TracBrowser for help on using the repository browser.