KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > jar > JarFile


1 /*
2  * @(#)JarFile.java 1.63 05/12/09
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util.jar;
9
10 import java.io.*;
11 import java.lang.ref.SoftReference JavaDoc;
12 import java.util.*;
13 import java.util.zip.*;
14 import java.security.CodeSigner JavaDoc;
15 import java.security.cert.Certificate JavaDoc;
16 import java.security.AccessController JavaDoc;
17 import sun.security.action.GetPropertyAction;
18 import sun.security.util.ManifestEntryVerifier;
19 import sun.misc.SharedSecrets;
20
21 /**
22  * The <code>JarFile</code> class is used to read the contents of a jar file
23  * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
24  * It extends the class <code>java.util.zip.ZipFile</code> with support
25  * for reading an optional <code>Manifest</code> entry. The
26  * <code>Manifest</code> can be used to specify meta-information about the
27  * jar file and its entries.
28  *
29  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
30  * or method in this class will cause a {@link NullPointerException} to be
31  * thrown.
32  *
33  * @author David Connelly
34  * @version 1.63, 12/09/05
35  * @see Manifest
36  * @see java.util.zip.ZipFile
37  * @see java.util.jar.JarEntry
38  * @since 1.2
39  */

40 public
41 class JarFile extends ZipFile {
42     private SoftReference JavaDoc<Manifest JavaDoc> manRef;
43     private JarEntry JavaDoc manEntry;
44     private JarVerifier JavaDoc jv;
45     private boolean jvInitialized;
46     private boolean verify;
47     private boolean computedHasClassPathAttribute;
48     private boolean hasClassPathAttribute;
49
50     // Set up JavaUtilJarAccess in SharedSecrets
51
static {
52         SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl JavaDoc());
53     }
54
55     /**
56      * The JAR manifest file name.
57      */

58     public static final String JavaDoc MANIFEST_NAME = "META-INF/MANIFEST.MF";
59
60     /**
61      * Creates a new <code>JarFile</code> to read from the specified
62      * file <code>name</code>. The <code>JarFile</code> will be verified if
63      * it is signed.
64      * @param name the name of the jar file to be opened for reading
65      * @throws IOException if an I/O error has occurred
66      * @throws SecurityException if access to the file is denied
67      * by the SecurityManager
68      */

69     public JarFile(String JavaDoc name) throws IOException {
70     this(new File(name), true, ZipFile.OPEN_READ);
71     }
72
73     /**
74      * Creates a new <code>JarFile</code> to read from the specified
75      * file <code>name</code>.
76      * @param name the name of the jar file to be opened for reading
77      * @param verify whether or not to verify the jar file if
78      * it is signed.
79      * @throws IOException if an I/O error has occurred
80      * @throws SecurityException if access to the file is denied
81      * by the SecurityManager
82      */

83     public JarFile(String JavaDoc name, boolean verify) throws IOException {
84         this(new File(name), verify, ZipFile.OPEN_READ);
85     }
86
87     /**
88      * Creates a new <code>JarFile</code> to read from the specified
89      * <code>File</code> object. The <code>JarFile</code> will be verified if
90      * it is signed.
91      * @param file the jar file to be opened for reading
92      * @throws IOException if an I/O error has occurred
93      * @throws SecurityException if access to the file is denied
94      * by the SecurityManager
95      */

96     public JarFile(File file) throws IOException {
97     this(file, true, ZipFile.OPEN_READ);
98     }
99
100
101     /**
102      * Creates a new <code>JarFile</code> to read from the specified
103      * <code>File</code> object.
104      * @param file the jar file to be opened for reading
105      * @param verify whether or not to verify the jar file if
106      * it is signed.
107      * @throws IOException if an I/O error has occurred
108      * @throws SecurityException if access to the file is denied
109      * by the SecurityManager.
110      */

111     public JarFile(File file, boolean verify) throws IOException {
112     this(file, verify, ZipFile.OPEN_READ);
113     }
114
115
116     /**
117      * Creates a new <code>JarFile</code> to read from the specified
118      * <code>File</code> object in the specified mode. The mode argument
119      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
120      *
121      * @param file the jar file to be opened for reading
122      * @param verify whether or not to verify the jar file if
123      * it is signed.
124      * @param mode the mode in which the file is to be opened
125      * @throws IOException if an I/O error has occurred
126      * @throws IllegalArgumentException
127      * if the <tt>mode</tt> argument is invalid
128      * @throws SecurityException if access to the file is denied
129      * by the SecurityManager
130      */

131     public JarFile(File file, boolean verify, int mode) throws IOException {
132     super(file, mode);
133     this.verify = verify;
134     }
135
136     /**
137      * Returns the jar file manifest, or <code>null</code> if none.
138      *
139      * @return the jar file manifest, or <code>null</code> if none
140      *
141      * @throws IllegalStateException
142      * may be thrown if the jar file has been closed
143      */

144     public Manifest JavaDoc getManifest() throws IOException {
145     return getManifestFromReference();
146     }
147
148     private Manifest JavaDoc getManifestFromReference() throws IOException {
149     Manifest JavaDoc man = manRef != null ? manRef.get() : null;
150       
151     if (man == null) {
152         JarEntry JavaDoc manEntry = getManEntry();
153         
154         // If found then load the manifest
155
if (manEntry != null) {
156         if (verify) {
157             byte[] b = getBytes(manEntry);
158             man = new Manifest JavaDoc(new ByteArrayInputStream(b));
159             if (!jvInitialized) {
160                 jv = new JarVerifier JavaDoc(b);
161             }
162         } else {
163             man = new Manifest JavaDoc(super.getInputStream(manEntry));
164         }
165         manRef = new SoftReference JavaDoc(man);
166         }
167     }
168     return man;
169     }
170
171     private native String JavaDoc[] getMetaInfEntryNames();
172
173     /**
174      * Returns the <code>JarEntry</code> for the given entry name or
175      * <code>null</code> if not found.
176      *
177      * @param name the jar file entry name
178      * @return the <code>JarEntry</code> for the given entry name or
179      * <code>null</code> if not found.
180      *
181      * @throws IllegalStateException
182      * may be thrown if the jar file has been closed
183      *
184      * @see java.util.jar.JarEntry
185      */

186     public JarEntry JavaDoc getJarEntry(String JavaDoc name) {
187     return (JarEntry JavaDoc)getEntry(name);
188     }
189
190     /**
191      * Returns the <code>ZipEntry</code> for the given entry name or
192      * <code>null</code> if not found.
193      *
194      * @param name the jar file entry name
195      * @return the <code>ZipEntry</code> for the given entry name or
196      * <code>null</code> if not found
197      *
198      * @throws IllegalStateException
199      * may be thrown if the jar file has been closed
200      *
201      * @see java.util.zip.ZipEntry
202      */

203     public ZipEntry getEntry(String JavaDoc name) {
204     ZipEntry ze = super.getEntry(name);
205     if (ze != null) {
206         return new JarFileEntry(ze);
207     }
208     return null;
209     }
210
211     /**
212      * Returns an enumeration of the zip file entries.
213      */

214     public Enumeration<JarEntry JavaDoc> entries() {
215     final Enumeration enum_ = super.entries();
216     return new Enumeration<JarEntry JavaDoc>() {
217         public boolean hasMoreElements() {
218         return enum_.hasMoreElements();
219         }
220         public JarFileEntry nextElement() {
221         ZipEntry ze = (ZipEntry)enum_.nextElement();
222         return new JarFileEntry(ze);
223         }
224     };
225     }
226
227     private class JarFileEntry extends JarEntry JavaDoc {
228     JarFileEntry(ZipEntry ze) {
229         super(ze);
230     }
231     public Attributes JavaDoc getAttributes() throws IOException {
232         Manifest JavaDoc man = JarFile.this.getManifest();
233         if (man != null) {
234         return man.getAttributes(getName());
235         } else {
236         return null;
237         }
238     }
239     public java.security.cert.Certificate JavaDoc[] getCertificates() {
240             try {
241                 maybeInstantiateVerifier();
242             } catch (IOException e) {
243                 throw new RuntimeException JavaDoc(e);
244             }
245         if (certs == null && jv != null) {
246         Certificate JavaDoc[] cs = jv.getCerts(getName());
247         if (cs != null) {
248             certs = (Certificate JavaDoc[])cs.clone();
249         }
250         }
251         return certs;
252     }
253     public CodeSigner JavaDoc[] getCodeSigners() {
254         try {
255         maybeInstantiateVerifier();
256         } catch (IOException e) {
257         throw new RuntimeException JavaDoc(e);
258         }
259         if (signers == null && jv != null) {
260         CodeSigner JavaDoc[] csg = jv.getCodeSigners(getName());
261         if (csg != null) {
262             signers = (CodeSigner JavaDoc[])csg.clone();
263         }
264         }
265         return signers;
266     }
267     }
268         
269     /*
270      * Ensures that the JarVerifier has been created if one is
271      * necessary (i.e., the jar appears to be signed.) This is done as
272      * a quick check to avoid processing of the manifest for unsigned
273      * jars.
274      */

275     private void maybeInstantiateVerifier() throws IOException {
276         if (jv != null) {
277             return;
278         }
279
280         if (verify) {
281             String JavaDoc[] names = getMetaInfEntryNames();
282             if (names != null) {
283                 for (int i = 0; i < names.length; i++) {
284                     String JavaDoc name = names[i].toUpperCase(Locale.ENGLISH);
285                     if (name.endsWith(".DSA") ||
286                         name.endsWith(".RSA") ||
287                         name.endsWith(".SF")) {
288                         // Assume since we found a signature-related file
289
// that the jar is signed and that we therefore
290
// need a JarVerifier and Manifest
291
getManifest();
292                         return;
293                     }
294                 }
295             }
296             // No signature-related files; don't instantiate a
297
// verifier
298
verify = false;
299         }
300     }
301
302
303     /*
304      * Initializes the verifier object by reading all the manifest
305      * entries and passing them to the verifier.
306      */

307     private void initializeVerifier() {
308     ManifestEntryVerifier mev = null;
309
310     // Verify "META-INF/" entries...
311
try {
312         String JavaDoc[] names = getMetaInfEntryNames();
313         if (names != null) {
314         for (int i = 0; i < names.length; i++) {
315             JarEntry JavaDoc e = getJarEntry(names[i]);
316             if (!e.isDirectory()) {
317             if (mev == null) {
318                 mev = new ManifestEntryVerifier
319                 (getManifestFromReference());
320             }
321             byte[] b = getBytes(e);
322             if (b != null && b.length > 0) {
323                 jv.beginEntry(e, mev);
324                 jv.update(b.length, b, 0, b.length, mev);
325                 jv.update(-1, null, 0, 0, mev);
326             }
327             }
328         }
329         }
330     } catch (IOException ex) {
331         // if we had an error parsing any blocks, just
332
// treat the jar file as being unsigned
333
jv = null;
334             verify = false;
335     }
336
337     // if after initializing the verifier we have nothing
338
// signed, we null it out.
339

340     if (jv != null) {
341
342         jv.doneWithMeta();
343         if (JarVerifier.debug != null) {
344         JarVerifier.debug.println("done with meta!");
345         }
346
347         if (jv.nothingToVerify()) {
348         if (JarVerifier.debug != null) {
349             JarVerifier.debug.println("nothing to verify!");
350         }
351         jv = null;
352                 verify = false;
353         }
354     }
355     }
356
357     /*
358      * Reads all the bytes for a given entry. Used to process the
359      * META-INF files.
360      */

361     private byte[] getBytes(ZipEntry ze) throws IOException {
362     byte[] b = new byte[(int)ze.getSize()];
363     DataInputStream is = new DataInputStream(super.getInputStream(ze));
364     is.readFully(b, 0, b.length);
365     is.close();
366     return b;
367     }
368
369     /**
370      * Returns an input stream for reading the contents of the specified
371      * zip file entry.
372      * @param ze the zip file entry
373      * @return an input stream for reading the contents of the specified
374      * zip file entry
375      * @throws ZipException if a zip file format error has occurred
376      * @throws IOException if an I/O error has occurred
377      * @throws SecurityException if any of the jar file entries
378      * are incorrectly signed.
379      * @throws IllegalStateException
380      * may be thrown if the jar file has been closed
381      */

382     public synchronized InputStream getInputStream(ZipEntry ze)
383     throws IOException
384     {
385         maybeInstantiateVerifier();
386     if (jv == null) {
387         return super.getInputStream(ze);
388     }
389     if (!jvInitialized) {
390         initializeVerifier();
391         jvInitialized = true;
392         // could be set to null after a call to
393
// initializeVerifier if we have nothing to
394
// verify
395
if (jv == null)
396         return super.getInputStream(ze);
397     }
398
399     // wrap a verifier stream around the real stream
400
return new JarVerifier.VerifierStream JavaDoc(
401         getManifestFromReference(),
402         ze instanceof JarFileEntry ?
403         (JarEntry JavaDoc) ze : getJarEntry(ze.getName()),
404         super.getInputStream(ze),
405         jv);
406     }
407
408     // Statics for hand-coded Boyer-Moore search in hasClassPathAttribute()
409
// The bad character shift for "class-path"
410
private static int[] lastOcc;
411     // The good suffix shift for "class-path"
412
private static int[] optoSft;
413     // Initialize the shift arrays to search for "class-path"
414
private static char[] src = {'c','l','a','s','s','-','p','a','t','h'};
415     static {
416     lastOcc = new int[128];
417     optoSft = new int[10];
418     lastOcc[(int)'c']=1;
419     lastOcc[(int)'l']=2;
420     lastOcc[(int)'s']=5;
421     lastOcc[(int)'-']=6;
422     lastOcc[(int)'p']=7;
423     lastOcc[(int)'a']=8;
424     lastOcc[(int)'t']=9;
425     lastOcc[(int)'h']=10;
426     for (int i=0; i<9; i++)
427         optoSft[i]=10;
428     optoSft[9]=1;
429     }
430  
431     private JarEntry JavaDoc getManEntry() {
432     if (manEntry == null) {
433         // First look up manifest entry using standard name
434
manEntry = getJarEntry(MANIFEST_NAME);
435             if (manEntry == null) {
436                 // If not found, then iterate through all the "META-INF/"
437
// entries to find a match.
438
String JavaDoc[] names = getMetaInfEntryNames();
439                 if (names != null) {
440                     for (int i = 0; i < names.length; i++) {
441                         if (MANIFEST_NAME.equals(
442                                                  names[i].toUpperCase(Locale.ENGLISH))) {
443                             manEntry = getJarEntry(names[i]);
444                             break;
445                         }
446                     }
447                 }
448             }
449     }
450     return manEntry;
451     }
452
453     // Returns true iff this jar file has a manifest with a class path
454
// attribute. Returns false if there is no manifest or the manifest
455
// does not contain a "Class-Path" attribute. Currently exported to
456
// core libraries via sun.misc.SharedSecrets.
457
boolean hasClassPathAttribute() throws IOException {
458         if (computedHasClassPathAttribute) {
459             return hasClassPathAttribute;
460         }
461
462         hasClassPathAttribute = false;
463         if (!isKnownToNotHaveClassPathAttribute()) {
464             JarEntry JavaDoc manEntry = getManEntry();
465             if (manEntry != null) {
466                 byte[] b = new byte[(int)manEntry.getSize()];
467                 DataInputStream dis = new DataInputStream(
468                                                           super.getInputStream(manEntry));
469                 dis.readFully(b, 0, b.length);
470                 dis.close();
471  
472                 int last = b.length - src.length;
473                 int i = 0;
474                 next:
475                 while (i<=last) {
476                     for (int j=9; j>=0; j--) {
477                         char c = (char) b[i+j];
478                         c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
479                         if (c != src[j]) {
480                             i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
481                             continue next;
482                         }
483                     }
484                     hasClassPathAttribute = true;
485                     break;
486                 }
487             }
488         }
489         computedHasClassPathAttribute = true;
490         return hasClassPathAttribute;
491     }
492
493     private static String JavaDoc javaHome;
494     private boolean isKnownToNotHaveClassPathAttribute() {
495         // Optimize away even scanning of manifest for jar files we
496
// deliver which don't have a class-path attribute. If one of
497
// these jars is changed to include such an attribute this code
498
// must be changed.
499
if (javaHome == null) {
500             javaHome = (String JavaDoc) AccessController.doPrivileged(
501                 new GetPropertyAction("java.home"));
502         }
503         String JavaDoc name = getName();
504         String JavaDoc localJavaHome = javaHome;
505         if (name.startsWith(localJavaHome)) {
506             if (name.endsWith("rt.jar") ||
507                 name.endsWith("sunrsasign.jar") ||
508                 name.endsWith("jsse.jar") ||
509                 name.endsWith("jce.jar") ||
510                 name.endsWith("charsets.jar") ||
511                 name.endsWith("dnsns.jar") ||
512                 name.endsWith("ldapsec.jar") ||
513                 name.endsWith("localedata.jar") ||
514                 name.endsWith("sunjce_provider.jar")) {
515                 return true;
516             }
517         }
518         return false;
519     }
520 }
521
Popular Tags