1 /*
   2  * Copyright (c) 1997, 2015, 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 jdk.internal.misc.SharedSecrets;
  40 import sun.misc.IOUtils;
  41 import sun.security.action.GetPropertyAction;
  42 import sun.security.util.ManifestEntryVerifier;
  43 import sun.security.util.SignatureFileVerifier;
  44 
  45 /**
  46  * The {@code JarFile} class is used to read the contents of a jar file
  47  * from any file that can be opened with {@code java.io.RandomAccessFile}.
  48  * It extends the class {@code java.util.zip.ZipFile} with support
  49  * for reading an optional {@code Manifest} entry. The
  50  * {@code Manifest} can be used to specify meta-information about the
  51  * jar file and its entries.
  52  *
  53  * <p> Unless otherwise noted, passing a {@code null} 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} to read from the specified
  95      * file {@code name}. The {@code JarFile} 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} to read from the specified
 108      * file {@code name}.
 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} to read from the specified
 122      * {@code File} object. The {@code JarFile} 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} to read from the specified
 136      * {@code File} 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} to read from the specified
 151      * {@code File} object in the specified mode.  The mode argument
 152      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
 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 {@code mode} 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} if none.
 172      *
 173      * @return the jar file manifest, or {@code null} 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} for the given entry name or
 211      * {@code null} if not found.
 212      *
 213      * @param name the jar file entry name
 214      * @return the {@code JarEntry} for the given entry name or
 215      *         {@code null} 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} for the given entry name or
 228      * {@code null} if not found.
 229      *
 230      * @param name the jar file entry name
 231      * @return the {@code ZipEntry} for the given entry name or
 232      *         {@code null} 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         public Iterator<JarEntry> asIterator() {
 270             return this;
 271         }
 272     }
 273 
 274     /**
 275      * Returns an enumeration of the jar file entries.
 276      *
 277      * @return an enumeration of the jar file entries
 278      * @throws IllegalStateException
 279      *         may be thrown if the jar file has been closed
 280      */
 281     public Enumeration<JarEntry> entries() {
 282         return new JarEntryIterator();
 283     }
 284 
 285     /**
 286      * Returns an ordered {@code Stream} over the jar file entries.
 287      * Entries appear in the {@code Stream} in the order they appear in
 288      * the central directory of the jar file.
 289      *
 290      * @return an ordered {@code Stream} of entries in this jar file
 291      * @throws IllegalStateException if the jar file has been closed
 292      * @since 1.8
 293      */
 294     public Stream<JarEntry> stream() {
 295         return StreamSupport.stream(Spliterators.spliterator(
 296                 new JarEntryIterator(), size(),
 297                 Spliterator.ORDERED | Spliterator.DISTINCT |
 298                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
 299     }
 300 
 301     private class JarFileEntry extends JarEntry {
 302         JarFileEntry(ZipEntry ze) {
 303             super(ze);
 304         }
 305         public Attributes getAttributes() throws IOException {
 306             Manifest man = JarFile.this.getManifest();
 307             if (man != null) {
 308                 return man.getAttributes(getName());
 309             } else {
 310                 return null;
 311             }
 312         }
 313         public Certificate[] getCertificates() {
 314             try {
 315                 maybeInstantiateVerifier();
 316             } catch (IOException e) {
 317                 throw new RuntimeException(e);
 318             }
 319             if (certs == null && jv != null) {
 320                 certs = jv.getCerts(JarFile.this, this);
 321             }
 322             return certs == null ? null : certs.clone();
 323         }
 324         public CodeSigner[] getCodeSigners() {
 325             try {
 326                 maybeInstantiateVerifier();
 327             } catch (IOException e) {
 328                 throw new RuntimeException(e);
 329             }
 330             if (signers == null && jv != null) {
 331                 signers = jv.getCodeSigners(JarFile.this, this);
 332             }
 333             return signers == null ? null : signers.clone();
 334         }
 335     }
 336 
 337     /*
 338      * Ensures that the JarVerifier has been created if one is
 339      * necessary (i.e., the jar appears to be signed.) This is done as
 340      * a quick check to avoid processing of the manifest for unsigned
 341      * jars.
 342      */
 343     private void maybeInstantiateVerifier() throws IOException {
 344         if (jv != null) {
 345             return;
 346         }
 347 
 348         if (verify) {
 349             String[] names = getMetaInfEntryNames();
 350             if (names != null) {
 351                 for (String nameLower : names) {
 352                     String name = nameLower.toUpperCase(Locale.ENGLISH);
 353                     if (name.endsWith(".DSA") ||
 354                         name.endsWith(".RSA") ||
 355                         name.endsWith(".EC") ||
 356                         name.endsWith(".SF")) {
 357                         // Assume since we found a signature-related file
 358                         // that the jar is signed and that we therefore
 359                         // need a JarVerifier and Manifest
 360                         getManifest();
 361                         return;
 362                     }
 363                 }
 364             }
 365             // No signature-related files; don't instantiate a
 366             // verifier
 367             verify = false;
 368         }
 369     }
 370 
 371 
 372     /*
 373      * Initializes the verifier object by reading all the manifest
 374      * entries and passing them to the verifier.
 375      */
 376     private void initializeVerifier() {
 377         ManifestEntryVerifier mev = null;
 378 
 379         // Verify "META-INF/" entries...
 380         try {
 381             String[] names = getMetaInfEntryNames();
 382             if (names != null) {
 383                 for (String name : names) {
 384                     String uname = name.toUpperCase(Locale.ENGLISH);
 385                     if (MANIFEST_NAME.equals(uname)
 386                             || SignatureFileVerifier.isBlockOrSF(uname)) {
 387                         JarEntry e = getJarEntry(name);
 388                         if (e == null) {
 389                             throw new JarException("corrupted jar file");
 390                         }
 391                         if (mev == null) {
 392                             mev = new ManifestEntryVerifier
 393                                 (getManifestFromReference());
 394                         }
 395                         byte[] b = getBytes(e);
 396                         if (b != null && b.length > 0) {
 397                             jv.beginEntry(e, mev);
 398                             jv.update(b.length, b, 0, b.length, mev);
 399                             jv.update(-1, null, 0, 0, mev);
 400                         }
 401                     }
 402                 }
 403             }
 404         } catch (IOException ex) {
 405             // if we had an error parsing any blocks, just
 406             // treat the jar file as being unsigned
 407             jv = null;
 408             verify = false;
 409             if (JarVerifier.debug != null) {
 410                 JarVerifier.debug.println("jarfile parsing error!");
 411                 ex.printStackTrace();
 412             }
 413         }
 414 
 415         // if after initializing the verifier we have nothing
 416         // signed, we null it out.
 417 
 418         if (jv != null) {
 419 
 420             jv.doneWithMeta();
 421             if (JarVerifier.debug != null) {
 422                 JarVerifier.debug.println("done with meta!");
 423             }
 424 
 425             if (jv.nothingToVerify()) {
 426                 if (JarVerifier.debug != null) {
 427                     JarVerifier.debug.println("nothing to verify!");
 428                 }
 429                 jv = null;
 430                 verify = false;
 431             }
 432         }
 433     }
 434 
 435     /*
 436      * Reads all the bytes for a given entry. Used to process the
 437      * META-INF files.
 438      */
 439     private byte[] getBytes(ZipEntry ze) throws IOException {
 440         try (InputStream is = super.getInputStream(ze)) {
 441             return IOUtils.readFully(is, (int)ze.getSize(), true);
 442         }
 443     }
 444 
 445     /**
 446      * Returns an input stream for reading the contents of the specified
 447      * zip file entry.
 448      * @param ze the zip file entry
 449      * @return an input stream for reading the contents of the specified
 450      *         zip file entry
 451      * @throws ZipException if a zip file format error has occurred
 452      * @throws IOException if an I/O error has occurred
 453      * @throws SecurityException if any of the jar file entries
 454      *         are incorrectly signed.
 455      * @throws IllegalStateException
 456      *         may be thrown if the jar file has been closed
 457      */
 458     public synchronized InputStream getInputStream(ZipEntry ze)
 459         throws IOException
 460     {
 461         maybeInstantiateVerifier();
 462         if (jv == null) {
 463             return super.getInputStream(ze);
 464         }
 465         if (!jvInitialized) {
 466             initializeVerifier();
 467             jvInitialized = true;
 468             // could be set to null after a call to
 469             // initializeVerifier if we have nothing to
 470             // verify
 471             if (jv == null)
 472                 return super.getInputStream(ze);
 473         }
 474 
 475         // wrap a verifier stream around the real stream
 476         return new JarVerifier.VerifierStream(
 477             getManifestFromReference(),
 478             ze instanceof JarFileEntry ?
 479             (JarEntry) ze : getJarEntry(ze.getName()),
 480             super.getInputStream(ze),
 481             jv);
 482     }
 483 
 484     // Statics for hand-coded Boyer-Moore search
 485     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
 486     // The bad character shift for "class-path"
 487     private static final int[] CLASSPATH_LASTOCC;
 488     // The good suffix shift for "class-path"
 489     private static final int[] CLASSPATH_OPTOSFT;
 490 
 491     static {
 492         CLASSPATH_LASTOCC = new int[128];
 493         CLASSPATH_OPTOSFT = new int[10];
 494         CLASSPATH_LASTOCC[(int)'c'] = 1;
 495         CLASSPATH_LASTOCC[(int)'l'] = 2;
 496         CLASSPATH_LASTOCC[(int)'s'] = 5;
 497         CLASSPATH_LASTOCC[(int)'-'] = 6;
 498         CLASSPATH_LASTOCC[(int)'p'] = 7;
 499         CLASSPATH_LASTOCC[(int)'a'] = 8;
 500         CLASSPATH_LASTOCC[(int)'t'] = 9;
 501         CLASSPATH_LASTOCC[(int)'h'] = 10;
 502         for (int i=0; i<9; i++)
 503             CLASSPATH_OPTOSFT[i] = 10;
 504         CLASSPATH_OPTOSFT[9]=1;
 505     }
 506 
 507     private JarEntry getManEntry() {
 508         if (manEntry == null) {
 509             // First look up manifest entry using standard name
 510             manEntry = getJarEntry(MANIFEST_NAME);
 511             if (manEntry == null) {
 512                 // If not found, then iterate through all the "META-INF/"
 513                 // entries to find a match.
 514                 String[] names = getMetaInfEntryNames();
 515                 if (names != null) {
 516                     for (String name : names) {
 517                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 518                             manEntry = getJarEntry(name);
 519                             break;
 520                         }
 521                     }
 522                 }
 523             }
 524         }
 525         return manEntry;
 526     }
 527 
 528    /**
 529     * Returns {@code true} iff this JAR file has a manifest with the
 530     * Class-Path attribute
 531     */
 532     boolean hasClassPathAttribute() throws IOException {
 533         checkForSpecialAttributes();
 534         return hasClassPathAttribute;
 535     }
 536 
 537     /**
 538      * Returns true if the pattern {@code src} is found in {@code b}.
 539      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
 540      * bad character and good suffix shifts.
 541      */
 542     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
 543         int len = src.length;
 544         int last = b.length - len;
 545         int i = 0;
 546         next:
 547         while (i<=last) {
 548             for (int j=(len-1); j>=0; j--) {
 549                 char c = (char) b[i+j];
 550                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
 551                 if (c != src[j]) {
 552                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
 553                     continue next;
 554                  }
 555             }
 556             return true;
 557         }
 558         return false;
 559     }
 560 
 561     /**
 562      * On first invocation, check if the JAR file has the Class-Path
 563      * attribute. A no-op on subsequent calls.
 564      */
 565     private void checkForSpecialAttributes() throws IOException {
 566         if (hasCheckedSpecialAttributes) return;
 567         JarEntry manEntry = getManEntry();
 568         if (manEntry != null) {
 569             byte[] b = getBytes(manEntry);
 570             if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
 571                 hasClassPathAttribute = true;
 572         }
 573         hasCheckedSpecialAttributes = true;
 574     }
 575 
 576     private synchronized void ensureInitialization() {
 577         try {
 578             maybeInstantiateVerifier();
 579         } catch (IOException e) {
 580             throw new RuntimeException(e);
 581         }
 582         if (jv != null && !jvInitialized) {
 583             initializeVerifier();
 584             jvInitialized = true;
 585         }
 586     }
 587 
 588     JarEntry newEntry(ZipEntry ze) {
 589         return new JarFileEntry(ze);
 590     }
 591 
 592     Enumeration<String> entryNames(CodeSource[] cs) {
 593         ensureInitialization();
 594         if (jv != null) {
 595             return jv.entryNames(this, cs);
 596         }
 597 
 598         /*
 599          * JAR file has no signed content. Is there a non-signing
 600          * code source?
 601          */
 602         boolean includeUnsigned = false;
 603         for (CodeSource c : cs) {
 604             if (c.getCodeSigners() == null) {
 605                 includeUnsigned = true;
 606                 break;
 607             }
 608         }
 609         if (includeUnsigned) {
 610             return unsignedEntryNames();
 611         } else {
 612             return new Enumeration<>() {
 613 
 614                 public boolean hasMoreElements() {
 615                     return false;
 616                 }
 617 
 618                 public String nextElement() {
 619                     throw new NoSuchElementException();
 620                 }
 621             };
 622         }
 623     }
 624 
 625     /**
 626      * Returns an enumeration of the zip file entries
 627      * excluding internal JAR mechanism entries and including
 628      * signed entries missing from the ZIP directory.
 629      */
 630     Enumeration<JarEntry> entries2() {
 631         ensureInitialization();
 632         if (jv != null) {
 633             return jv.entries2(this, super.entries());
 634         }
 635 
 636         // screen out entries which are never signed
 637         final Enumeration<? extends ZipEntry> enum_ = super.entries();
 638         return new Enumeration<>() {
 639 
 640             ZipEntry entry;
 641 
 642             public boolean hasMoreElements() {
 643                 if (entry != null) {
 644                     return true;
 645                 }
 646                 while (enum_.hasMoreElements()) {
 647                     ZipEntry ze = enum_.nextElement();
 648                     if (JarVerifier.isSigningRelated(ze.getName())) {
 649                         continue;
 650                     }
 651                     entry = ze;
 652                     return true;
 653                 }
 654                 return false;
 655             }
 656 
 657             public JarFileEntry nextElement() {
 658                 if (hasMoreElements()) {
 659                     ZipEntry ze = entry;
 660                     entry = null;
 661                     return new JarFileEntry(ze);
 662                 }
 663                 throw new NoSuchElementException();
 664             }
 665         };
 666     }
 667 
 668     CodeSource[] getCodeSources(URL url) {
 669         ensureInitialization();
 670         if (jv != null) {
 671             return jv.getCodeSources(this, url);
 672         }
 673 
 674         /*
 675          * JAR file has no signed content. Is there a non-signing
 676          * code source?
 677          */
 678         Enumeration<String> unsigned = unsignedEntryNames();
 679         if (unsigned.hasMoreElements()) {
 680             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
 681         } else {
 682             return null;
 683         }
 684     }
 685 
 686     private Enumeration<String> unsignedEntryNames() {
 687         final Enumeration<JarEntry> entries = entries();
 688         return new Enumeration<>() {
 689 
 690             String name;
 691 
 692             /*
 693              * Grab entries from ZIP directory but screen out
 694              * metadata.
 695              */
 696             public boolean hasMoreElements() {
 697                 if (name != null) {
 698                     return true;
 699                 }
 700                 while (entries.hasMoreElements()) {
 701                     String value;
 702                     ZipEntry e = entries.nextElement();
 703                     value = e.getName();
 704                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
 705                         continue;
 706                     }
 707                     name = value;
 708                     return true;
 709                 }
 710                 return false;
 711             }
 712 
 713             public String nextElement() {
 714                 if (hasMoreElements()) {
 715                     String value = name;
 716                     name = null;
 717                     return value;
 718                 }
 719                 throw new NoSuchElementException();
 720             }
 721         };
 722     }
 723 
 724     CodeSource getCodeSource(URL url, String name) {
 725         ensureInitialization();
 726         if (jv != null) {
 727             if (jv.eagerValidation) {
 728                 CodeSource cs = null;
 729                 JarEntry je = getJarEntry(name);
 730                 if (je != null) {
 731                     cs = jv.getCodeSource(url, this, je);
 732                 } else {
 733                     cs = jv.getCodeSource(url, name);
 734                 }
 735                 return cs;
 736             } else {
 737                 return jv.getCodeSource(url, name);
 738             }
 739         }
 740 
 741         return JarVerifier.getUnsignedCS(url);
 742     }
 743 
 744     void setEagerValidation(boolean eager) {
 745         try {
 746             maybeInstantiateVerifier();
 747         } catch (IOException e) {
 748             throw new RuntimeException(e);
 749         }
 750         if (jv != null) {
 751             jv.setEagerValidation(eager);
 752         }
 753     }
 754 
 755     List<Object> getManifestDigests() {
 756         ensureInitialization();
 757         if (jv != null) {
 758             return jv.getManifestDigests();
 759         }
 760         return new ArrayList<>();
 761     }
 762 }