View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.util.jar;
27  
28  import java.io.*;
29  import java.lang.ref.SoftReference;
30  import java.net.URL;
31  import java.util.*;
32  import java.util.stream.Stream;
33  import java.util.stream.StreamSupport;
34  import java.util.zip.*;
35  import java.security.CodeSigner;
36  import java.security.cert.Certificate;
37  import java.security.AccessController;
38  import java.security.CodeSource;
39  import sun.misc.IOUtils;
40  import sun.security.action.GetPropertyAction;
41  import sun.security.util.ManifestEntryVerifier;
42  import sun.misc.SharedSecrets;
43  import sun.security.util.SignatureFileVerifier;
44  
45  /**
46   * The <code>JarFile</code> class is used to read the contents of a jar file
47   * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
48   * It extends the class <code>java.util.zip.ZipFile</code> with support
49   * for reading an optional <code>Manifest</code> entry. The
50   * <code>Manifest</code> can be used to specify meta-information about the
51   * jar file and its entries.
52   *
53   * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
54   * or method in this class will cause a {@link NullPointerException} to be
55   * thrown.
56   *
57   * If the verify flag is on when opening a signed jar file, the content of the
58   * file is verified against its signature embedded inside the file. Please note
59   * that the verification process does not include validating the signer's
60   * certificate. A caller should inspect the return value of
61   * {@link JarEntry#getCodeSigners()} to further determine if the signature
62   * can be trusted.
63   *
64   * @author  David Connelly
65   * @see     Manifest
66   * @see     java.util.zip.ZipFile
67   * @see     java.util.jar.JarEntry
68   * @since   1.2
69   */
70  public
71  class JarFile extends ZipFile {
72      private SoftReference<Manifest> manRef;
73      private JarEntry manEntry;
74      private JarVerifier jv;
75      private boolean jvInitialized;
76      private boolean verify;
77  
78      // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
79      private boolean hasClassPathAttribute;
80      // true if manifest checked for special attributes
81      private volatile boolean hasCheckedSpecialAttributes;
82  
83      // Set up JavaUtilJarAccess in SharedSecrets
84      static {
85          SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
86      }
87  
88      /**
89       * The JAR manifest file name.
90       */
91      public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
92  
93      /**
94       * Creates a new <code>JarFile</code> to read from the specified
95       * file <code>name</code>. The <code>JarFile</code> will be verified if
96       * it is signed.
97       * @param name the name of the jar file to be opened for reading
98       * @throws IOException if an I/O error has occurred
99       * @throws SecurityException if access to the file is denied
100      *         by the SecurityManager
101      */
102     public JarFile(String name) throws IOException {
103         this(new File(name), true, ZipFile.OPEN_READ);
104     }
105 
106     /**
107      * Creates a new <code>JarFile</code> to read from the specified
108      * file <code>name</code>.
109      * @param name the name of the jar file to be opened for reading
110      * @param verify whether or not to verify the jar file if
111      * it is signed.
112      * @throws IOException if an I/O error has occurred
113      * @throws SecurityException if access to the file is denied
114      *         by the SecurityManager
115      */
116     public JarFile(String name, boolean verify) throws IOException {
117         this(new File(name), verify, ZipFile.OPEN_READ);
118     }
119 
120     /**
121      * Creates a new <code>JarFile</code> to read from the specified
122      * <code>File</code> object. The <code>JarFile</code> will be verified if
123      * it is signed.
124      * @param file the jar file to be opened for reading
125      * @throws IOException if an I/O error has occurred
126      * @throws SecurityException if access to the file is denied
127      *         by the SecurityManager
128      */
129     public JarFile(File file) throws IOException {
130         this(file, true, ZipFile.OPEN_READ);
131     }
132 
133 
134     /**
135      * Creates a new <code>JarFile</code> to read from the specified
136      * <code>File</code> object.
137      * @param file the jar file to be opened for reading
138      * @param verify whether or not to verify the jar file if
139      * it is signed.
140      * @throws IOException if an I/O error has occurred
141      * @throws SecurityException if access to the file is denied
142      *         by the SecurityManager.
143      */
144     public JarFile(File file, boolean verify) throws IOException {
145         this(file, verify, ZipFile.OPEN_READ);
146     }
147 
148 
149     /**
150      * Creates a new <code>JarFile</code> to read from the specified
151      * <code>File</code> object in the specified mode.  The mode argument
152      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
153      *
154      * @param file the jar file to be opened for reading
155      * @param verify whether or not to verify the jar file if
156      * it is signed.
157      * @param mode the mode in which the file is to be opened
158      * @throws IOException if an I/O error has occurred
159      * @throws IllegalArgumentException
160      *         if the <tt>mode</tt> argument is invalid
161      * @throws SecurityException if access to the file is denied
162      *         by the SecurityManager
163      * @since 1.3
164      */
165     public JarFile(File file, boolean verify, int mode) throws IOException {
166         super(file, mode);
167         this.verify = verify;
168     }
169 
170     /**
171      * Returns the jar file manifest, or <code>null</code> if none.
172      *
173      * @return the jar file manifest, or <code>null</code> if none
174      *
175      * @throws IllegalStateException
176      *         may be thrown if the jar file has been closed
177      * @throws IOException  if an I/O error has occurred
178      */
179     public Manifest getManifest() throws IOException {
180         return getManifestFromReference();
181     }
182 
183     private Manifest getManifestFromReference() throws IOException {
184         Manifest man = manRef != null ? manRef.get() : null;
185 
186         if (man == null) {
187 
188             JarEntry manEntry = getManEntry();
189 
190             // If found then load the manifest
191             if (manEntry != null) {
192                 if (verify) {
193                     byte[] b = getBytes(manEntry);
194                     man = new Manifest(new ByteArrayInputStream(b));
195                     if (!jvInitialized) {
196                         jv = new JarVerifier(b);
197                     }
198                 } else {
199                     man = new Manifest(super.getInputStream(manEntry));
200                 }
201                 manRef = new SoftReference<>(man);
202             }
203         }
204         return man;
205     }
206 
207     private native String[] getMetaInfEntryNames();
208 
209     /**
210      * Returns the <code>JarEntry</code> for the given entry name or
211      * <code>null</code> if not found.
212      *
213      * @param name the jar file entry name
214      * @return the <code>JarEntry</code> for the given entry name or
215      *         <code>null</code> if not found.
216      *
217      * @throws IllegalStateException
218      *         may be thrown if the jar file has been closed
219      *
220      * @see java.util.jar.JarEntry
221      */
222     public JarEntry getJarEntry(String name) {
223         return (JarEntry)getEntry(name);
224     }
225 
226     /**
227      * Returns the <code>ZipEntry</code> for the given entry name or
228      * <code>null</code> if not found.
229      *
230      * @param name the jar file entry name
231      * @return the <code>ZipEntry</code> for the given entry name or
232      *         <code>null</code> if not found
233      *
234      * @throws IllegalStateException
235      *         may be thrown if the jar file has been closed
236      *
237      * @see java.util.zip.ZipEntry
238      */
239     public ZipEntry getEntry(String name) {
240         ZipEntry ze = super.getEntry(name);
241         if (ze != null) {
242             return new JarFileEntry(ze);
243         }
244         return null;
245     }
246 
247     private class JarEntryIterator implements Enumeration<JarEntry>,
248             Iterator<JarEntry>
249     {
250         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
251 
252         public boolean hasNext() {
253             return e.hasMoreElements();
254         }
255 
256         public JarEntry next() {
257             ZipEntry ze = e.nextElement();
258             return new JarFileEntry(ze);
259         }
260 
261         public boolean hasMoreElements() {
262             return hasNext();
263         }
264 
265         public JarEntry nextElement() {
266             return next();
267         }
268     }
269 
270     /**
271      * Returns an enumeration of the zip file entries.
272      */
273     public Enumeration<JarEntry> entries() {
274         return new JarEntryIterator();
275     }
276 
277     @Override
278     public Stream<JarEntry> stream() {
279         return StreamSupport.stream(Spliterators.spliterator(
280                 new JarEntryIterator(), size(),
281                 Spliterator.ORDERED | Spliterator.DISTINCT |
282                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
283     }
284 
285     private class JarFileEntry extends JarEntry {
286         JarFileEntry(ZipEntry ze) {
287             super(ze);
288         }
289         public Attributes getAttributes() throws IOException {
290             Manifest man = JarFile.this.getManifest();
291             if (man != null) {
292                 return man.getAttributes(getName());
293             } else {
294                 return null;
295             }
296         }
297         public Certificate[] getCertificates() {
298             try {
299                 maybeInstantiateVerifier();
300             } catch (IOException e) {
301                 throw new RuntimeException(e);
302             }
303             if (certs == null && jv != null) {
304                 certs = jv.getCerts(JarFile.this, this);
305             }
306             return certs == null ? null : certs.clone();
307         }
308         public CodeSigner[] getCodeSigners() {
309             try {
310                 maybeInstantiateVerifier();
311             } catch (IOException e) {
312                 throw new RuntimeException(e);
313             }
314             if (signers == null && jv != null) {
315                 signers = jv.getCodeSigners(JarFile.this, this);
316             }
317             return signers == null ? null : signers.clone();
318         }
319     }
320 
321     /*
322      * Ensures that the JarVerifier has been created if one is
323      * necessary (i.e., the jar appears to be signed.) This is done as
324      * a quick check to avoid processing of the manifest for unsigned
325      * jars.
326      */
327     private void maybeInstantiateVerifier() throws IOException {
328         if (jv != null) {
329             return;
330         }
331 
332         if (verify) {
333             String[] names = getMetaInfEntryNames();
334             if (names != null) {
335                 for (int i = 0; i < names.length; i++) {
336                     String name = names[i].toUpperCase(Locale.ENGLISH);
337                     if (name.endsWith(".DSA") ||
338                         name.endsWith(".RSA") ||
339                         name.endsWith(".EC") ||
340                         name.endsWith(".SF")) {
341                         // Assume since we found a signature-related file
342                         // that the jar is signed and that we therefore
343                         // need a JarVerifier and Manifest
344                         getManifest();
345                         return;
346                     }
347                 }
348             }
349             // No signature-related files; don't instantiate a
350             // verifier
351             verify = false;
352         }
353     }
354 
355 
356     /*
357      * Initializes the verifier object by reading all the manifest
358      * entries and passing them to the verifier.
359      */
360     private void initializeVerifier() {
361         ManifestEntryVerifier mev = null;
362 
363         // Verify "META-INF/" entries...
364         try {
365             String[] names = getMetaInfEntryNames();
366             if (names != null) {
367                 for (int i = 0; i < names.length; i++) {
368                     String uname = names[i].toUpperCase(Locale.ENGLISH);
369                     if (MANIFEST_NAME.equals(uname)
370                             || SignatureFileVerifier.isBlockOrSF(uname)) {
371                         JarEntry e = getJarEntry(names[i]);
372                         if (e == null) {
373                             throw new JarException("corrupted jar file");
374                         }
375                         if (mev == null) {
376                             mev = new ManifestEntryVerifier
377                                 (getManifestFromReference());
378                         }
379                         byte[] b = getBytes(e);
380                         if (b != null && b.length > 0) {
381                             jv.beginEntry(e, mev);
382                             jv.update(b.length, b, 0, b.length, mev);
383                             jv.update(-1, null, 0, 0, mev);
384                         }
385                     }
386                 }
387             }
388         } catch (IOException ex) {
389             // if we had an error parsing any blocks, just
390             // treat the jar file as being unsigned
391             jv = null;
392             verify = false;
393             if (JarVerifier.debug != null) {
394                 JarVerifier.debug.println("jarfile parsing error!");
395                 ex.printStackTrace();
396             }
397         }
398 
399         // if after initializing the verifier we have nothing
400         // signed, we null it out.
401 
402         if (jv != null) {
403 
404             jv.doneWithMeta();
405             if (JarVerifier.debug != null) {
406                 JarVerifier.debug.println("done with meta!");
407             }
408 
409             if (jv.nothingToVerify()) {
410                 if (JarVerifier.debug != null) {
411                     JarVerifier.debug.println("nothing to verify!");
412                 }
413                 jv = null;
414                 verify = false;
415             }
416         }
417     }
418 
419     /*
420      * Reads all the bytes for a given entry. Used to process the
421      * META-INF files.
422      */
423     private byte[] getBytes(ZipEntry ze) throws IOException {
424         try (InputStream is = super.getInputStream(ze)) {
425             return IOUtils.readFully(is, (int)ze.getSize(), true);
426         }
427     }
428 
429     /**
430      * Returns an input stream for reading the contents of the specified
431      * zip file entry.
432      * @param ze the zip file entry
433      * @return an input stream for reading the contents of the specified
434      *         zip file entry
435      * @throws ZipException if a zip file format error has occurred
436      * @throws IOException if an I/O error has occurred
437      * @throws SecurityException if any of the jar file entries
438      *         are incorrectly signed.
439      * @throws IllegalStateException
440      *         may be thrown if the jar file has been closed
441      */
442     public synchronized InputStream getInputStream(ZipEntry ze)
443         throws IOException
444     {
445         maybeInstantiateVerifier();
446         if (jv == null) {
447             return super.getInputStream(ze);
448         }
449         if (!jvInitialized) {
450             initializeVerifier();
451             jvInitialized = true;
452             // could be set to null after a call to
453             // initializeVerifier if we have nothing to
454             // verify
455             if (jv == null)
456                 return super.getInputStream(ze);
457         }
458 
459         // wrap a verifier stream around the real stream
460         return new JarVerifier.VerifierStream(
461             getManifestFromReference(),
462             ze instanceof JarFileEntry ?
463             (JarEntry) ze : getJarEntry(ze.getName()),
464             super.getInputStream(ze),
465             jv);
466     }
467 
468     // Statics for hand-coded Boyer-Moore search
469     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
470     // The bad character shift for "class-path"
471     private static final int[] CLASSPATH_LASTOCC;
472     // The good suffix shift for "class-path"
473     private static final int[] CLASSPATH_OPTOSFT;
474 
475     static {
476         CLASSPATH_LASTOCC = new int[128];
477         CLASSPATH_OPTOSFT = new int[10];
478         CLASSPATH_LASTOCC[(int)'c'] = 1;
479         CLASSPATH_LASTOCC[(int)'l'] = 2;
480         CLASSPATH_LASTOCC[(int)'s'] = 5;
481         CLASSPATH_LASTOCC[(int)'-'] = 6;
482         CLASSPATH_LASTOCC[(int)'p'] = 7;
483         CLASSPATH_LASTOCC[(int)'a'] = 8;
484         CLASSPATH_LASTOCC[(int)'t'] = 9;
485         CLASSPATH_LASTOCC[(int)'h'] = 10;
486         for (int i=0; i<9; i++)
487             CLASSPATH_OPTOSFT[i] = 10;
488         CLASSPATH_OPTOSFT[9]=1;
489     }
490 
491     private JarEntry getManEntry() {
492         if (manEntry == null) {
493             // First look up manifest entry using standard name
494             manEntry = getJarEntry(MANIFEST_NAME);
495             if (manEntry == null) {
496                 // If not found, then iterate through all the "META-INF/"
497                 // entries to find a match.
498                 String[] names = getMetaInfEntryNames();
499                 if (names != null) {
500                     for (int i = 0; i < names.length; i++) {
501                         if (MANIFEST_NAME.equals(
502                                                  names[i].toUpperCase(Locale.ENGLISH))) {
503                             manEntry = getJarEntry(names[i]);
504                             break;
505                         }
506                     }
507                 }
508             }
509         }
510         return manEntry;
511     }
512 
513    /**
514     * Returns {@code true} iff this JAR file has a manifest with the
515     * Class-Path attribute
516     */
517     boolean hasClassPathAttribute() throws IOException {
518         checkForSpecialAttributes();
519         return hasClassPathAttribute;
520     }
521 
522     /**
523      * Returns true if the pattern {@code src} is found in {@code b}.
524      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
525      * bad character and good suffix shifts.
526      */
527     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
528         int len = src.length;
529         int last = b.length - len;
530         int i = 0;
531         next:
532         while (i<=last) {
533             for (int j=(len-1); j>=0; j--) {
534                 char c = (char) b[i+j];
535                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
536                 if (c != src[j]) {
537                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
538                     continue next;
539                  }
540             }
541             return true;
542         }
543         return false;
544     }
545 
546     /**
547      * On first invocation, check if the JAR file has the Class-Path
548      * attribute. A no-op on subsequent calls.
549      */
550     private void checkForSpecialAttributes() throws IOException {
551         if (hasCheckedSpecialAttributes) return;
552         if (!isKnownNotToHaveSpecialAttributes()) {
553             JarEntry manEntry = getManEntry();
554             if (manEntry != null) {
555                 byte[] b = getBytes(manEntry);
556                 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
557                     hasClassPathAttribute = true;
558             }
559         }
560         hasCheckedSpecialAttributes = true;
561     }
562 
563     private static String javaHome;
564     private static volatile String[] jarNames;
565     private boolean isKnownNotToHaveSpecialAttributes() {
566         // Optimize away even scanning of manifest for jar files we
567         // deliver which don't have a class-path attribute. If one of
568         // these jars is changed to include such an attribute this code
569         // must be changed.
570         if (javaHome == null) {
571             javaHome = AccessController.doPrivileged(
572                 new GetPropertyAction("java.home"));
573         }
574         if (jarNames == null) {
575             String[] names = new String[11];
576             String fileSep = File.separator;
577             int i = 0;
578             names[i++] = fileSep + "rt.jar";
579             names[i++] = fileSep + "jsse.jar";
580             names[i++] = fileSep + "jce.jar";
581             names[i++] = fileSep + "charsets.jar";
582             names[i++] = fileSep + "dnsns.jar";
583             names[i++] = fileSep + "zipfs.jar";
584             names[i++] = fileSep + "localedata.jar";
585             names[i++] = fileSep = "cldrdata.jar";
586             names[i++] = fileSep + "sunjce_provider.jar";
587             names[i++] = fileSep + "sunpkcs11.jar";
588             names[i++] = fileSep + "sunec.jar";
589             jarNames = names;
590         }
591 
592         String name = getName();
593         String localJavaHome = javaHome;
594         if (name.startsWith(localJavaHome)) {
595             String[] names = jarNames;
596             for (int i = 0; i < names.length; i++) {
597                 if (name.endsWith(names[i])) {
598                     return true;
599                 }
600             }
601         }
602         return false;
603     }
604 
605     private synchronized void ensureInitialization() {
606         try {
607             maybeInstantiateVerifier();
608         } catch (IOException e) {
609             throw new RuntimeException(e);
610         }
611         if (jv != null && !jvInitialized) {
612             initializeVerifier();
613             jvInitialized = true;
614         }
615     }
616 
617     JarEntry newEntry(ZipEntry ze) {
618         return new JarFileEntry(ze);
619     }
620 
621     Enumeration<String> entryNames(CodeSource[] cs) {
622         ensureInitialization();
623         if (jv != null) {
624             return jv.entryNames(this, cs);
625         }
626 
627         /*
628          * JAR file has no signed content. Is there a non-signing
629          * code source?
630          */
631         boolean includeUnsigned = false;
632         for (int i = 0; i < cs.length; i++) {
633             if (cs[i].getCodeSigners() == null) {
634                 includeUnsigned = true;
635                 break;
636             }
637         }
638         if (includeUnsigned) {
639             return unsignedEntryNames();
640         } else {
641             return new Enumeration<String>() {
642 
643                 public boolean hasMoreElements() {
644                     return false;
645                 }
646 
647                 public String nextElement() {
648                     throw new NoSuchElementException();
649                 }
650             };
651         }
652     }
653 
654     /**
655      * Returns an enumeration of the zip file entries
656      * excluding internal JAR mechanism entries and including
657      * signed entries missing from the ZIP directory.
658      */
659     Enumeration<JarEntry> entries2() {
660         ensureInitialization();
661         if (jv != null) {
662             return jv.entries2(this, super.entries());
663         }
664 
665         // screen out entries which are never signed
666         final Enumeration<? extends ZipEntry> enum_ = super.entries();
667         return new Enumeration<JarEntry>() {
668 
669             ZipEntry entry;
670 
671             public boolean hasMoreElements() {
672                 if (entry != null) {
673                     return true;
674                 }
675                 while (enum_.hasMoreElements()) {
676                     ZipEntry ze = enum_.nextElement();
677                     if (JarVerifier.isSigningRelated(ze.getName())) {
678                         continue;
679                     }
680                     entry = ze;
681                     return true;
682                 }
683                 return false;
684             }
685 
686             public JarFileEntry nextElement() {
687                 if (hasMoreElements()) {
688                     ZipEntry ze = entry;
689                     entry = null;
690                     return new JarFileEntry(ze);
691                 }
692                 throw new NoSuchElementException();
693             }
694         };
695     }
696 
697     CodeSource[] getCodeSources(URL url) {
698         ensureInitialization();
699         if (jv != null) {
700             return jv.getCodeSources(this, url);
701         }
702 
703         /*
704          * JAR file has no signed content. Is there a non-signing
705          * code source?
706          */
707         Enumeration<String> unsigned = unsignedEntryNames();
708         if (unsigned.hasMoreElements()) {
709             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
710         } else {
711             return null;
712         }
713     }
714 
715     private Enumeration<String> unsignedEntryNames() {
716         final Enumeration<JarEntry> entries = entries();
717         return new Enumeration<String>() {
718 
719             String name;
720 
721             /*
722              * Grab entries from ZIP directory but screen out
723              * metadata.
724              */
725             public boolean hasMoreElements() {
726                 if (name != null) {
727                     return true;
728                 }
729                 while (entries.hasMoreElements()) {
730                     String value;
731                     ZipEntry e = entries.nextElement();
732                     value = e.getName();
733                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
734                         continue;
735                     }
736                     name = value;
737                     return true;
738                 }
739                 return false;
740             }
741 
742             public String nextElement() {
743                 if (hasMoreElements()) {
744                     String value = name;
745                     name = null;
746                     return value;
747                 }
748                 throw new NoSuchElementException();
749             }
750         };
751     }
752 
753     CodeSource getCodeSource(URL url, String name) {
754         ensureInitialization();
755         if (jv != null) {
756             if (jv.eagerValidation) {
757                 CodeSource cs = null;
758                 JarEntry je = getJarEntry(name);
759                 if (je != null) {
760                     cs = jv.getCodeSource(url, this, je);
761                 } else {
762                     cs = jv.getCodeSource(url, name);
763                 }
764                 return cs;
765             } else {
766                 return jv.getCodeSource(url, name);
767             }
768         }
769 
770         return JarVerifier.getUnsignedCS(url);
771     }
772 
773     void setEagerValidation(boolean eager) {
774         try {
775             maybeInstantiateVerifier();
776         } catch (IOException e) {
777             throw new RuntimeException(e);
778         }
779         if (jv != null) {
780             jv.setEagerValidation(eager);
781         }
782     }
783 
784     List<Object> getManifestDigests() {
785         ensureInitialization();
786         if (jv != null) {
787             return jv.getManifestDigests();
788         }
789         return new ArrayList<Object>();
790     }
791 }