KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > jlink


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 /**
19  * jlink.java links together multiple .jar files Original code by Patrick
20  * Beard. Modifications to work with ANT by Matthew Kuperus Heun.
21  *
22  */

23 package org.apache.tools.ant.taskdefs.optional.jlink;
24
25 import java.io.BufferedInputStream JavaDoc;
26 import java.io.File JavaDoc;
27 import java.io.FileInputStream JavaDoc;
28 import java.io.FileOutputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStream JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.Vector JavaDoc;
33 import java.util.zip.CRC32 JavaDoc;
34 import java.util.zip.Deflater JavaDoc;
35 import java.util.zip.ZipEntry JavaDoc;
36 import java.util.zip.ZipException JavaDoc;
37 import java.util.zip.ZipFile JavaDoc;
38 import java.util.zip.ZipOutputStream JavaDoc;
39
40 // CheckStyle:TypeNameCheck OFF - bc
41
/**
42  * jlink links together multiple .jar files.
43  */

44 public class jlink {
45
46
47     private String JavaDoc outfile = null;
48
49     private Vector JavaDoc mergefiles = new Vector JavaDoc(10);
50
51     private Vector JavaDoc addfiles = new Vector JavaDoc(10);
52
53     private boolean compression = false;
54
55     // CheckStyle:VisibilityModifier OFF - bc
56

57     byte[] buffer = new byte[8192];
58
59     // CheckStyle:VisibilityModifier OFF - bc
60

61     /** The file that will be created by this instance of jlink.
62      * @param outfile the file to create.
63      */

64     public void setOutfile(String JavaDoc outfile) {
65         if (outfile == null) {
66             return;
67         }
68         this.outfile = outfile;
69     }
70
71
72     /**
73      * Adds a file to be merged into the output.
74      * @param fileToMerge the file to merge into the output.
75      */

76     public void addMergeFile(String JavaDoc fileToMerge) {
77         if (fileToMerge == null) {
78             return;
79         }
80         mergefiles.addElement(fileToMerge);
81     }
82
83
84     /** Adds a file to be added into the output.
85      * @param fileToAdd the file to add to the output.
86      */

87     public void addAddFile(String JavaDoc fileToAdd) {
88         if (fileToAdd == null) {
89             return;
90         }
91         addfiles.addElement(fileToAdd);
92     }
93
94
95     /**
96      * Adds several files to be merged into the output.
97      * @param filesToMerge an array of files to merge into the output.
98      */

99     public void addMergeFiles(String JavaDoc[] filesToMerge) {
100         if (filesToMerge == null) {
101             return;
102         }
103         for (int i = 0; i < filesToMerge.length; i++) {
104             addMergeFile(filesToMerge[i]);
105         }
106     }
107
108
109     /**
110      * Adds several file to be added into the output.
111      * @param filesToAdd an array of files to add to the output.
112      */

113     public void addAddFiles(String JavaDoc[] filesToAdd) {
114         if (filesToAdd == null) {
115             return;
116         }
117         for (int i = 0; i < filesToAdd.length; i++) {
118             addAddFile(filesToAdd[i]);
119         }
120     }
121
122
123     /**
124      * Determines whether output will be compressed.
125      * @param compress if true use compression.
126      */

127     public void setCompression(boolean compress) {
128         this.compression = compress;
129     }
130
131
132     /**
133      * Performs the linking of files. Addfiles are added to the output as-is.
134      * For example, a jar file is added to the output as a jar file. However,
135      * mergefiles are first examined for their type. If it is a jar or zip
136      * file, the contents will be extracted from the mergefile and entered
137      * into the output. If a zip or jar file is encountered in a subdirectory
138      * it will be added, not merged. If a directory is encountered, it becomes
139      * the root entry of all the files below it. Thus, you can provide
140      * multiple, disjoint directories, as addfiles: they will all be added in
141      * a rational manner to outfile.
142      * @throws Exception on error.
143      */

144     public void link() throws Exception JavaDoc {
145         ZipOutputStream JavaDoc output = new ZipOutputStream JavaDoc(new FileOutputStream JavaDoc(outfile));
146
147         if (compression) {
148             output.setMethod(ZipOutputStream.DEFLATED);
149             output.setLevel(Deflater.DEFAULT_COMPRESSION);
150         } else {
151             output.setMethod(ZipOutputStream.STORED);
152         }
153
154         Enumeration JavaDoc merges = mergefiles.elements();
155
156         while (merges.hasMoreElements()) {
157             String JavaDoc path = (String JavaDoc) merges.nextElement();
158             File JavaDoc f = new File JavaDoc(path);
159
160             if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) {
161                 //Do the merge
162
mergeZipJarContents(output, f);
163             } else {
164                 //Add this file to the addfiles Vector and add it
165
//later at the top level of the output file.
166
addAddFile(path);
167             }
168         }
169
170         Enumeration JavaDoc adds = addfiles.elements();
171
172         while (adds.hasMoreElements()) {
173             String JavaDoc name = (String JavaDoc) adds.nextElement();
174             File JavaDoc f = new File JavaDoc(name);
175
176             if (f.isDirectory()) {
177                 //System.out.println("in jlink: adding directory contents of " + f.getPath());
178
addDirContents(output, f, f.getName() + '/', compression);
179             } else {
180                 addFile(output, f, "", compression);
181             }
182         }
183         if (output != null) {
184             try {
185                 output.close();
186             } catch (IOException JavaDoc ioe) {
187                 //do nothing
188
}
189         }
190     }
191
192
193     /**
194      * The command line entry point for jlink.
195      * @param args an array of arguments
196      */

197     public static void main(String JavaDoc[] args) {
198         // jlink output input1 ... inputN
199
if (args.length < 2) {
200             System.out.println("usage: jlink output input1 ... inputN");
201             System.exit(1);
202         }
203         jlink linker = new jlink();
204
205         linker.setOutfile(args[0]);
206         // To maintain compatibility with the command-line version,
207
// we will only add files to be merged.
208
for (int i = 1; i < args.length; i++) {
209             linker.addMergeFile(args[i]);
210         }
211         try {
212             linker.link();
213         } catch (Exception JavaDoc ex) {
214             System.err.print(ex.getMessage());
215         }
216     }
217
218
219     /*
220      * Actually performs the merging of f into the output.
221      * f should be a zip or jar file.
222      */

223     private void mergeZipJarContents(ZipOutputStream JavaDoc output, File JavaDoc f) throws IOException JavaDoc {
224         //Check to see that the file with name "name" exists.
225
if (!f.exists()) {
226             return;
227         }
228         ZipFile JavaDoc zipf = new ZipFile JavaDoc(f);
229         Enumeration JavaDoc entries = zipf.entries();
230
231         while (entries.hasMoreElements()) {
232             ZipEntry JavaDoc inputEntry = (ZipEntry JavaDoc) entries.nextElement();
233             //Ignore manifest entries. They're bound to cause conflicts between
234
//files that are being merged. User should supply their own
235
//manifest file when doing the merge.
236
String JavaDoc inputEntryName = inputEntry.getName();
237             int index = inputEntryName.indexOf("META-INF");
238
239             if (index < 0) {
240                 //META-INF not found in the name of the entry. Go ahead and process it.
241
try {
242                     output.putNextEntry(processEntry(zipf, inputEntry));
243                 } catch (ZipException JavaDoc ex) {
244                     //If we get here, it could be because we are trying to put a
245
//directory entry that already exists.
246
//For example, we're trying to write "com", but a previous
247
//entry from another mergefile was called "com".
248
//In that case, just ignore the error and go on to the
249
//next entry.
250
String JavaDoc mess = ex.getMessage();
251
252                     if (mess.indexOf("duplicate") >= 0) {
253                         //It was the duplicate entry.
254
continue;
255                     } else {
256                         // I hate to admit it, but we don't know what happened
257
// here. Throw the Exception.
258
throw ex;
259                     }
260                 }
261
262                 InputStream JavaDoc in = zipf.getInputStream(inputEntry);
263                 int len = buffer.length;
264                 int count = -1;
265
266                 while ((count = in.read(buffer, 0, len)) > 0) {
267                     output.write(buffer, 0, count);
268                 }
269                 in.close();
270                 output.closeEntry();
271             }
272         }
273         zipf.close();
274     }
275
276
277     /*
278      * Adds contents of a directory to the output.
279      */

280     private void addDirContents(ZipOutputStream JavaDoc output, File JavaDoc dir, String JavaDoc prefix,
281                                 boolean compress) throws IOException JavaDoc {
282         String JavaDoc[] contents = dir.list();
283
284         for (int i = 0; i < contents.length; ++i) {
285             String JavaDoc name = contents[i];
286             File JavaDoc file = new File JavaDoc(dir, name);
287
288             if (file.isDirectory()) {
289                 addDirContents(output, file, prefix + name + '/', compress);
290             } else {
291                 addFile(output, file, prefix, compress);
292             }
293         }
294     }
295
296
297     /*
298      * Gets the name of an entry in the file. This is the real name
299      * which for a class is the name of the package with the class
300      * name appended.
301      */

302     private String JavaDoc getEntryName(File JavaDoc file, String JavaDoc prefix) {
303         String JavaDoc name = file.getName();
304
305         if (!name.endsWith(".class")) {
306             // see if the file is in fact a .class file, and determine its actual name.
307
InputStream JavaDoc input = null;
308             try {
309                 input = new FileInputStream JavaDoc(file);
310                 String JavaDoc className = ClassNameReader.getClassName(input);
311
312                 if (className != null) {
313                     return className.replace('.', '/') + ".class";
314                 }
315             } catch (IOException JavaDoc ioe) {
316                 //do nothing
317
} finally {
318                 if (input != null) {
319                     try {
320                         input.close();
321                     } catch (IOException JavaDoc e) {
322                         //do nothing
323
}
324                 }
325             }
326         }
327         System.out.println("From " + file.getPath() + " and prefix " + prefix
328                            + ", creating entry " + prefix + name);
329         return (prefix + name);
330     }
331
332
333     /*
334      * Adds a file to the output stream.
335      */

336     private void addFile(ZipOutputStream JavaDoc output, File JavaDoc file, String JavaDoc prefix,
337                          boolean compress) throws IOException JavaDoc {
338         //Make sure file exists
339
if (!file.exists()) {
340             return;
341         }
342         ZipEntry JavaDoc entry = new ZipEntry JavaDoc(getEntryName(file, prefix));
343
344         entry.setTime(file.lastModified());
345         entry.setSize(file.length());
346         if (!compress) {
347             entry.setCrc(calcChecksum(file));
348         }
349         FileInputStream JavaDoc input = new FileInputStream JavaDoc(file);
350
351         addToOutputStream(output, input, entry);
352     }
353
354
355     /*
356      * A convenience method that several other methods might call.
357      */

358     private void addToOutputStream(ZipOutputStream JavaDoc output, InputStream JavaDoc input,
359                                    ZipEntry JavaDoc ze) throws IOException JavaDoc {
360         try {
361             output.putNextEntry(ze);
362         } catch (ZipException JavaDoc zipEx) {
363             //This entry already exists. So, go with the first one.
364
input.close();
365             return;
366         }
367
368         int numBytes = -1;
369
370         while ((numBytes = input.read(buffer)) > 0) {
371             output.write(buffer, 0, numBytes);
372         }
373         output.closeEntry();
374         input.close();
375     }
376
377
378     /*
379      * A method that does the work on a given entry in a mergefile.
380      * The big deal is to set the right parameters in the ZipEntry
381      * on the output stream.
382      */

383     private ZipEntry JavaDoc processEntry(ZipFile JavaDoc zip, ZipEntry JavaDoc inputEntry) {
384         /*
385           First, some notes.
386           On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the
387           ZipInputStream does not work for compressed (deflated) files. Those calls return -1.
388           For uncompressed (stored) files, those calls do work.
389           However, using ZipFile.getEntries() works for both compressed and
390           uncompressed files.
391
392           Now, from some simple testing I did, it seems that the value of CRC-32 is
393           independent of the compression setting. So, it should be easy to pass this
394           information on to the output entry.
395         */

396         String JavaDoc name = inputEntry.getName();
397
398         if (!(inputEntry.isDirectory() || name.endsWith(".class"))) {
399             try {
400                 InputStream JavaDoc input = zip.getInputStream(zip.getEntry(name));
401                 String JavaDoc className = ClassNameReader.getClassName(input);
402
403                 input.close();
404                 if (className != null) {
405                     name = className.replace('.', '/') + ".class";
406                 }
407             } catch (IOException JavaDoc ioe) {
408                 //do nothing
409
}
410         }
411         ZipEntry JavaDoc outputEntry = new ZipEntry JavaDoc(name);
412
413         outputEntry.setTime(inputEntry.getTime());
414         outputEntry.setExtra(inputEntry.getExtra());
415         outputEntry.setComment(inputEntry.getComment());
416         outputEntry.setTime(inputEntry.getTime());
417         if (compression) {
418             outputEntry.setMethod(ZipEntry.DEFLATED);
419             //Note, don't need to specify size or crc for compressed files.
420
} else {
421             outputEntry.setMethod(ZipEntry.STORED);
422             outputEntry.setCrc(inputEntry.getCrc());
423             outputEntry.setSize(inputEntry.getSize());
424         }
425         return outputEntry;
426     }
427
428
429     /*
430      * Necessary in the case where you add a entry that
431      * is not compressed.
432      */

433     private long calcChecksum(File JavaDoc f) throws IOException JavaDoc {
434         BufferedInputStream JavaDoc in = new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(f));
435
436         return calcChecksum(in);
437     }
438
439
440     /*
441      * Necessary in the case where you add a entry that
442      * is not compressed.
443      */

444     private long calcChecksum(InputStream JavaDoc in) throws IOException JavaDoc {
445         CRC32 JavaDoc crc = new CRC32 JavaDoc();
446         int len = buffer.length;
447         int count = -1;
448         int haveRead = 0;
449
450         while ((count = in.read(buffer, 0, len)) > 0) {
451             haveRead += count;
452             crc.update(buffer, 0, count);
453         }
454         in.close();
455         return crc.getValue();
456     }
457
458
459 }
460
461
462
Popular Tags