rev 13123 : 8132734: JDK 9 runtime changes to support multi-release jar files
Summary: JEP 238 Multi-Release JAR Files runtime support
Contributed-by: steve.drach@oracle.com
rev 12882 : 8139706: JarFile.getBytes could use InputStream.readNBytes
Reviewed-by: sherman, chegar, alanb
rev 12860 : 8138978: Examine usages of sun.misc.IOUtils
Reviewed-by: alanb, mullan, psandoz, rriggs, weijun
rev 12807 : 8137056: Move SharedSecrets and interface friends out of sun.misc
Reviewed-by: alanb, mchung, psandoz, rriggs
rev 12522 : 8133115: docs: replace <tt> tags (obsolete in html5) for java.util.logging, java.util.prefs, java.util.zip, java.util.jar
Reviewed-by: lancea
rev 12298 : 8081678: Add Stream returning methods to classes where there currently exist only Enumeration returning methods
Reviewed-by: lancea, alanb, chegar, dfuchs, mullan, smarks
rev 12022 : 8064736: Part of java.util.jar.JarFile spec looks confusing with references to Zip
Summary: update the api doc for entries()/stream() accordingly
Reviewed-by: alanb
rev 11804 : 8078467: Update core libraries to use diamond with anonymous classes
Reviewed-by: mchung, alanb
rev 11051 : 8049367: Modular Run-Time Images
Reviewed-by: chegar, dfuchs, ihse, joehw, mullan, psandoz, wetmore
Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, bradford.wetmore@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, james.laskey@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com, sundararajan.athijegannathan@oracle.com
rev 10469 : 8054834: Modular Source Code
Reviewed-by: alanb, chegar, ihse, mduigou
Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com

   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.security.action.GetPropertyAction;
  41 import sun.security.util.ManifestEntryVerifier;
  42 import sun.security.util.SignatureFileVerifier;
  43 


  44 /**
  45  * The {@code JarFile} class is used to read the contents of a jar file
  46  * from any file that can be opened with {@code java.io.RandomAccessFile}.
  47  * It extends the class {@code java.util.zip.ZipFile} with support
  48  * for reading an optional {@code Manifest} entry. The
  49  * {@code Manifest} can be used to specify meta-information about the
  50  * jar file and its entries.
  51  *
  52  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  53  * or method in this class will cause a {@link NullPointerException} to be
  54  * thrown.





  55  *
  56  * If the verify flag is on when opening a signed jar file, the content of the
  57  * file is verified against its signature embedded inside the file. Please note
  58  * that the verification process does not include validating the signer's


















  59  * certificate. A caller should inspect the return value of
  60  * {@link JarEntry#getCodeSigners()} to further determine if the signature
  61  * can be trusted.
  62  *






































  63  * @author  David Connelly
  64  * @see     Manifest
  65  * @see     java.util.zip.ZipFile
  66  * @see     java.util.jar.JarEntry
  67  * @since   1.2
  68  */
  69 public
  70 class JarFile extends ZipFile {





  71     private SoftReference<Manifest> manRef;
  72     private JarEntry manEntry;
  73     private JarVerifier jv;
  74     private boolean jvInitialized;
  75     private boolean verify;
  76 
  77     // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
  78     private boolean hasClassPathAttribute;
  79     // true if manifest checked for special attributes
  80     private volatile boolean hasCheckedSpecialAttributes;
























  81 
  82     // Set up JavaUtilJarAccess in SharedSecrets
  83     static {

  84         SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());


































  85     }
  86 




  87     /**
  88      * The JAR manifest file name.
  89      */
  90     public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
  91 
  92     /**
  93      * Creates a new {@code JarFile} to read from the specified
  94      * file {@code name}. The {@code JarFile} will be verified if
  95      * it is signed.
  96      * @param name the name of the jar file to be opened for reading
  97      * @throws IOException if an I/O error has occurred
  98      * @throws SecurityException if access to the file is denied
  99      *         by the SecurityManager
 100      */
 101     public JarFile(String name) throws IOException {
 102         this(new File(name), true, ZipFile.OPEN_READ);
 103     }
 104 
 105     /**
 106      * Creates a new {@code JarFile} to read from the specified
 107      * file {@code name}.
 108      * @param name the name of the jar file to be opened for reading
 109      * @param verify whether or not to verify the jar file if
 110      * it is signed.
 111      * @throws IOException if an I/O error has occurred
 112      * @throws SecurityException if access to the file is denied
 113      *         by the SecurityManager
 114      */
 115     public JarFile(String name, boolean verify) throws IOException {
 116         this(new File(name), verify, ZipFile.OPEN_READ);
 117     }
 118 
 119     /**
 120      * Creates a new {@code JarFile} to read from the specified
 121      * {@code File} object. The {@code JarFile} will be verified if
 122      * it is signed.
 123      * @param file the jar file to be opened for reading
 124      * @throws IOException if an I/O error has occurred
 125      * @throws SecurityException if access to the file is denied
 126      *         by the SecurityManager
 127      */
 128     public JarFile(File file) throws IOException {
 129         this(file, true, ZipFile.OPEN_READ);
 130     }
 131 
 132 
 133     /**
 134      * Creates a new {@code JarFile} to read from the specified
 135      * {@code File} object.
 136      * @param file the jar file to be opened for reading
 137      * @param verify whether or not to verify the jar file if
 138      * it is signed.
 139      * @throws IOException if an I/O error has occurred
 140      * @throws SecurityException if access to the file is denied
 141      *         by the SecurityManager.
 142      */
 143     public JarFile(File file, boolean verify) throws IOException {
 144         this(file, verify, ZipFile.OPEN_READ);
 145     }
 146 
 147 
 148     /**
 149      * Creates a new {@code JarFile} to read from the specified
 150      * {@code File} object in the specified mode.  The mode argument
 151      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
 152      *
 153      * @param file the jar file to be opened for reading
 154      * @param verify whether or not to verify the jar file if
 155      * it is signed.
 156      * @param mode the mode in which the file is to be opened
 157      * @throws IOException if an I/O error has occurred
 158      * @throws IllegalArgumentException
 159      *         if the {@code mode} argument is invalid
 160      * @throws SecurityException if access to the file is denied
 161      *         by the SecurityManager
 162      * @since 1.3
 163      */
 164     public JarFile(File file, boolean verify, int mode) throws IOException {
 165         super(file, mode);
 166         this.verify = verify;






















































































 167     }
 168 
 169     /**







































 170      * Returns the jar file manifest, or {@code null} if none.
 171      *
 172      * @return the jar file manifest, or {@code null} if none
 173      *
 174      * @throws IllegalStateException
 175      *         may be thrown if the jar file has been closed
 176      * @throws IOException  if an I/O error has occurred
 177      */
 178     public Manifest getManifest() throws IOException {
 179         return getManifestFromReference();
 180     }
 181 
 182     private Manifest getManifestFromReference() throws IOException {
 183         Manifest man = manRef != null ? manRef.get() : null;
 184 
 185         if (man == null) {
 186 
 187             JarEntry manEntry = getManEntry();
 188 
 189             // If found then load the manifest
 190             if (manEntry != null) {
 191                 if (verify) {
 192                     byte[] b = getBytes(manEntry);
 193                     man = new Manifest(new ByteArrayInputStream(b));
 194                     if (!jvInitialized) {
 195                         jv = new JarVerifier(b);
 196                     }
 197                 } else {
 198                     man = new Manifest(super.getInputStream(manEntry));
 199                 }
 200                 manRef = new SoftReference<>(man);
 201             }
 202         }
 203         return man;
 204     }
 205 
 206     private native String[] getMetaInfEntryNames();
 207 
 208     /**
 209      * Returns the {@code JarEntry} for the given entry name or
 210      * {@code null} if not found.
 211      *












 212      * @param name the jar file entry name
 213      * @return the {@code JarEntry} for the given entry name or
 214      *         {@code null} if not found.
 215      *
 216      * @throws IllegalStateException
 217      *         may be thrown if the jar file has been closed
 218      *
 219      * @see java.util.jar.JarEntry
 220      */
 221     public JarEntry getJarEntry(String name) {
 222         return (JarEntry)getEntry(name);
 223     }
 224 
 225     /**
 226      * Returns the {@code ZipEntry} for the given entry name or
 227      * {@code null} if not found.
 228      *












 229      * @param name the jar file entry name
 230      * @return the {@code ZipEntry} for the given entry name or
 231      *         {@code null} if not found
 232      *
 233      * @throws IllegalStateException
 234      *         may be thrown if the jar file has been closed
 235      *
 236      * @see java.util.zip.ZipEntry
 237      */
 238     public ZipEntry getEntry(String name) {
 239         ZipEntry ze = super.getEntry(name);
 240         if (ze != null) {
 241             return new JarFileEntry(ze);
 242         }








 243         return null;
 244     }
 245 
 246     private class JarEntryIterator implements Enumeration<JarEntry>,
 247             Iterator<JarEntry>
 248     {
 249         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
 250 
 251         public boolean hasNext() {
 252             return e.hasMoreElements();
 253         }
 254 
 255         public JarEntry next() {
 256             ZipEntry ze = e.nextElement();
 257             return new JarFileEntry(ze);
 258         }
 259 
 260         public boolean hasMoreElements() {
 261             return hasNext();
 262         }
 263 
 264         public JarEntry nextElement() {
 265             return next();
 266         }
 267 
 268         public Iterator<JarEntry> asIterator() {
 269             return this;
 270         }
 271     }
 272 
 273     /**
 274      * Returns an enumeration of the jar file entries.
 275      *
 276      * @return an enumeration of the jar file entries
 277      * @throws IllegalStateException
 278      *         may be thrown if the jar file has been closed
 279      */
 280     public Enumeration<JarEntry> entries() {
 281         return new JarEntryIterator();
 282     }
 283 
 284     /**
 285      * Returns an ordered {@code Stream} over the jar file entries.
 286      * Entries appear in the {@code Stream} in the order they appear in
 287      * the central directory of the jar file.
 288      *
 289      * @return an ordered {@code Stream} of entries in this jar file
 290      * @throws IllegalStateException if the jar file has been closed
 291      * @since 1.8
 292      */
 293     public Stream<JarEntry> stream() {
 294         return StreamSupport.stream(Spliterators.spliterator(
 295                 new JarEntryIterator(), size(),
 296                 Spliterator.ORDERED | Spliterator.DISTINCT |
 297                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
 298     }
 299 



































































 300     private class JarFileEntry extends JarEntry {


 301         JarFileEntry(ZipEntry ze) {
 302             super(ze);





 303         }
 304         public Attributes getAttributes() throws IOException {
 305             Manifest man = JarFile.this.getManifest();
 306             if (man != null) {
 307                 return man.getAttributes(getName());
 308             } else {
 309                 return null;
 310             }
 311         }
 312         public Certificate[] getCertificates() {
 313             try {
 314                 maybeInstantiateVerifier();
 315             } catch (IOException e) {
 316                 throw new RuntimeException(e);
 317             }
 318             if (certs == null && jv != null) {
 319                 certs = jv.getCerts(JarFile.this, this);
 320             }
 321             return certs == null ? null : certs.clone();
 322         }
 323         public CodeSigner[] getCodeSigners() {
 324             try {
 325                 maybeInstantiateVerifier();
 326             } catch (IOException e) {
 327                 throw new RuntimeException(e);
 328             }
 329             if (signers == null && jv != null) {
 330                 signers = jv.getCodeSigners(JarFile.this, this);
 331             }
 332             return signers == null ? null : signers.clone();
 333         }












 334     }
 335 
 336     /*
 337      * Ensures that the JarVerifier has been created if one is
 338      * necessary (i.e., the jar appears to be signed.) This is done as
 339      * a quick check to avoid processing of the manifest for unsigned
 340      * jars.
 341      */
 342     private void maybeInstantiateVerifier() throws IOException {
 343         if (jv != null) {
 344             return;
 345         }
 346 
 347         if (verify) {
 348             String[] names = getMetaInfEntryNames();
 349             if (names != null) {
 350                 for (String nameLower : names) {
 351                     String name = nameLower.toUpperCase(Locale.ENGLISH);
 352                     if (name.endsWith(".DSA") ||
 353                         name.endsWith(".RSA") ||
 354                         name.endsWith(".EC") ||
 355                         name.endsWith(".SF")) {
 356                         // Assume since we found a signature-related file
 357                         // that the jar is signed and that we therefore
 358                         // need a JarVerifier and Manifest
 359                         getManifest();
 360                         return;
 361                     }
 362                 }
 363             }
 364             // No signature-related files; don't instantiate a
 365             // verifier
 366             verify = false;
 367         }
 368     }
 369 
 370 
 371     /*
 372      * Initializes the verifier object by reading all the manifest
 373      * entries and passing them to the verifier.
 374      */
 375     private void initializeVerifier() {
 376         ManifestEntryVerifier mev = null;
 377 
 378         // Verify "META-INF/" entries...
 379         try {
 380             String[] names = getMetaInfEntryNames();
 381             if (names != null) {
 382                 for (String name : names) {
 383                     String uname = name.toUpperCase(Locale.ENGLISH);
 384                     if (MANIFEST_NAME.equals(uname)
 385                             || SignatureFileVerifier.isBlockOrSF(uname)) {
 386                         JarEntry e = getJarEntry(name);
 387                         if (e == null) {
 388                             throw new JarException("corrupted jar file");
 389                         }
 390                         if (mev == null) {
 391                             mev = new ManifestEntryVerifier
 392                                 (getManifestFromReference());
 393                         }
 394                         byte[] b = getBytes(e);
 395                         if (b != null && b.length > 0) {
 396                             jv.beginEntry(e, mev);
 397                             jv.update(b.length, b, 0, b.length, mev);
 398                             jv.update(-1, null, 0, 0, mev);
 399                         }
 400                     }
 401                 }
 402             }
 403         } catch (IOException ex) {
 404             // if we had an error parsing any blocks, just
 405             // treat the jar file as being unsigned
 406             jv = null;
 407             verify = false;
 408             if (JarVerifier.debug != null) {
 409                 JarVerifier.debug.println("jarfile parsing error!");
 410                 ex.printStackTrace();
 411             }
 412         }
 413 
 414         // if after initializing the verifier we have nothing
 415         // signed, we null it out.
 416 
 417         if (jv != null) {
 418 
 419             jv.doneWithMeta();
 420             if (JarVerifier.debug != null) {
 421                 JarVerifier.debug.println("done with meta!");
 422             }
 423 
 424             if (jv.nothingToVerify()) {
 425                 if (JarVerifier.debug != null) {
 426                     JarVerifier.debug.println("nothing to verify!");
 427                 }
 428                 jv = null;
 429                 verify = false;
 430             }
 431         }
 432     }
 433 
 434     /*
 435      * Reads all the bytes for a given entry. Used to process the
 436      * META-INF files.
 437      */
 438     private byte[] getBytes(ZipEntry ze) throws IOException {
 439         try (InputStream is = super.getInputStream(ze)) {
 440             int len = (int)ze.getSize();
 441             int bytesRead;
 442             byte[] b;
 443             // trust specified entry sizes when reasonably small
 444             if (len != -1 && len <= 65535) {
 445                 b = new byte[len];
 446                 bytesRead = is.readNBytes(b, 0, len);
 447             } else {
 448                 b = is.readAllBytes();
 449                 bytesRead = b.length;
 450             }
 451             if (len != -1 && len != bytesRead) {
 452                 throw new EOFException("Expected:" + len + ", read:" + bytesRead);
 453             }
 454             return b;
 455         }
 456     }
 457 
 458     /**
 459      * Returns an input stream for reading the contents of the specified
 460      * zip file entry.
 461      * @param ze the zip file entry
 462      * @return an input stream for reading the contents of the specified
 463      *         zip file entry
 464      * @throws ZipException if a zip file format error has occurred
 465      * @throws IOException if an I/O error has occurred
 466      * @throws SecurityException if any of the jar file entries
 467      *         are incorrectly signed.
 468      * @throws IllegalStateException
 469      *         may be thrown if the jar file has been closed
 470      */
 471     public synchronized InputStream getInputStream(ZipEntry ze)
 472         throws IOException
 473     {
 474         maybeInstantiateVerifier();
 475         if (jv == null) {
 476             return super.getInputStream(ze);
 477         }
 478         if (!jvInitialized) {
 479             initializeVerifier();
 480             jvInitialized = true;
 481             // could be set to null after a call to
 482             // initializeVerifier if we have nothing to
 483             // verify
 484             if (jv == null)
 485                 return super.getInputStream(ze);
 486         }
 487 
 488         // wrap a verifier stream around the real stream
 489         return new JarVerifier.VerifierStream(
 490             getManifestFromReference(),
 491             ze instanceof JarFileEntry ?
 492             (JarEntry) ze : getJarEntry(ze.getName()),
 493             super.getInputStream(ze),
 494             jv);
 495     }
 496 








 497     // Statics for hand-coded Boyer-Moore search
 498     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
 499     // The bad character shift for "class-path"
 500     private static final int[] CLASSPATH_LASTOCC;
 501     // The good suffix shift for "class-path"
 502     private static final int[] CLASSPATH_OPTOSFT;
 503 
 504     static {
 505         CLASSPATH_LASTOCC = new int[128];
 506         CLASSPATH_OPTOSFT = new int[10];
 507         CLASSPATH_LASTOCC[(int)'c'] = 1;
 508         CLASSPATH_LASTOCC[(int)'l'] = 2;
 509         CLASSPATH_LASTOCC[(int)'s'] = 5;
 510         CLASSPATH_LASTOCC[(int)'-'] = 6;
 511         CLASSPATH_LASTOCC[(int)'p'] = 7;
 512         CLASSPATH_LASTOCC[(int)'a'] = 8;
 513         CLASSPATH_LASTOCC[(int)'t'] = 9;
 514         CLASSPATH_LASTOCC[(int)'h'] = 10;
 515         for (int i=0; i<9; i++)
 516             CLASSPATH_OPTOSFT[i] = 10;
 517         CLASSPATH_OPTOSFT[9]=1;
 518     }
 519 
 520     private JarEntry getManEntry() {
 521         if (manEntry == null) {
 522             // First look up manifest entry using standard name
 523             manEntry = getJarEntry(MANIFEST_NAME);
 524             if (manEntry == null) {
 525                 // If not found, then iterate through all the "META-INF/"
 526                 // entries to find a match.
 527                 String[] names = getMetaInfEntryNames();
 528                 if (names != null) {
 529                     for (String name : names) {
 530                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 531                             manEntry = getJarEntry(name);
 532                             break;
 533                         }
 534                     }
 535                 }
 536             }

 537         }
 538         return manEntry;
 539     }
 540 
 541    /**
 542     * Returns {@code true} iff this JAR file has a manifest with the
 543     * Class-Path attribute
 544     */
 545     boolean hasClassPathAttribute() throws IOException {
 546         checkForSpecialAttributes();
 547         return hasClassPathAttribute;
 548     }
 549 
 550     /**
 551      * Returns true if the pattern {@code src} is found in {@code b}.
 552      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
 553      * bad character and good suffix shifts.
 554      */
 555     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
 556         int len = src.length;
 557         int last = b.length - len;
 558         int i = 0;
 559         next:
 560         while (i<=last) {
 561             for (int j=(len-1); j>=0; j--) {
 562                 char c = (char) b[i+j];
 563                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
 564                 if (c != src[j]) {
 565                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
 566                     continue next;
 567                  }
 568             }
 569             return true;
 570         }
 571         return false;
 572     }
 573 
 574     /**
 575      * On first invocation, check if the JAR file has the Class-Path
 576      * attribute. A no-op on subsequent calls.
 577      */
 578     private void checkForSpecialAttributes() throws IOException {
 579         if (hasCheckedSpecialAttributes) return;
 580         JarEntry manEntry = getManEntry();
 581         if (manEntry != null) {
 582             byte[] b = getBytes(manEntry);
 583             if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
 584                 hasClassPathAttribute = true;
 585         }
 586         hasCheckedSpecialAttributes = true;
 587     }
 588 
 589     private synchronized void ensureInitialization() {
 590         try {
 591             maybeInstantiateVerifier();
 592         } catch (IOException e) {
 593             throw new RuntimeException(e);
 594         }
 595         if (jv != null && !jvInitialized) {
 596             initializeVerifier();
 597             jvInitialized = true;
 598         }
 599     }
 600 
 601     JarEntry newEntry(ZipEntry ze) {
 602         return new JarFileEntry(ze);
 603     }
 604 
 605     Enumeration<String> entryNames(CodeSource[] cs) {
 606         ensureInitialization();
 607         if (jv != null) {
 608             return jv.entryNames(this, cs);
 609         }
 610 
 611         /*
 612          * JAR file has no signed content. Is there a non-signing
 613          * code source?
 614          */
 615         boolean includeUnsigned = false;
 616         for (CodeSource c : cs) {
 617             if (c.getCodeSigners() == null) {
 618                 includeUnsigned = true;
 619                 break;
 620             }
 621         }
 622         if (includeUnsigned) {
 623             return unsignedEntryNames();
 624         } else {
 625             return new Enumeration<>() {
 626 
 627                 public boolean hasMoreElements() {
 628                     return false;
 629                 }
 630 
 631                 public String nextElement() {
 632                     throw new NoSuchElementException();
 633                 }
 634             };
 635         }
 636     }
 637 
 638     /**
 639      * Returns an enumeration of the zip file entries
 640      * excluding internal JAR mechanism entries and including
 641      * signed entries missing from the ZIP directory.
 642      */
 643     Enumeration<JarEntry> entries2() {
 644         ensureInitialization();
 645         if (jv != null) {
 646             return jv.entries2(this, super.entries());
 647         }
 648 
 649         // screen out entries which are never signed
 650         final Enumeration<? extends ZipEntry> enum_ = super.entries();
 651         return new Enumeration<>() {
 652 
 653             ZipEntry entry;
 654 
 655             public boolean hasMoreElements() {
 656                 if (entry != null) {
 657                     return true;
 658                 }
 659                 while (enum_.hasMoreElements()) {
 660                     ZipEntry ze = enum_.nextElement();
 661                     if (JarVerifier.isSigningRelated(ze.getName())) {
 662                         continue;
 663                     }
 664                     entry = ze;
 665                     return true;
 666                 }
 667                 return false;
 668             }
 669 
 670             public JarFileEntry nextElement() {
 671                 if (hasMoreElements()) {
 672                     ZipEntry ze = entry;
 673                     entry = null;
 674                     return new JarFileEntry(ze);
 675                 }
 676                 throw new NoSuchElementException();
 677             }
 678         };
 679     }
 680 
 681     CodeSource[] getCodeSources(URL url) {
 682         ensureInitialization();
 683         if (jv != null) {
 684             return jv.getCodeSources(this, url);
 685         }
 686 
 687         /*
 688          * JAR file has no signed content. Is there a non-signing
 689          * code source?
 690          */
 691         Enumeration<String> unsigned = unsignedEntryNames();
 692         if (unsigned.hasMoreElements()) {
 693             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
 694         } else {
 695             return null;
 696         }
 697     }
 698 
 699     private Enumeration<String> unsignedEntryNames() {
 700         final Enumeration<JarEntry> entries = entries();
 701         return new Enumeration<>() {
 702 
 703             String name;
 704 
 705             /*
 706              * Grab entries from ZIP directory but screen out
 707              * metadata.
 708              */
 709             public boolean hasMoreElements() {
 710                 if (name != null) {
 711                     return true;
 712                 }
 713                 while (entries.hasMoreElements()) {
 714                     String value;
 715                     ZipEntry e = entries.nextElement();
 716                     value = e.getName();
 717                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
 718                         continue;
 719                     }
 720                     name = value;
 721                     return true;
 722                 }
 723                 return false;
 724             }
 725 
 726             public String nextElement() {
 727                 if (hasMoreElements()) {
 728                     String value = name;
 729                     name = null;
 730                     return value;
 731                 }
 732                 throw new NoSuchElementException();
 733             }
 734         };
 735     }
 736 
 737     CodeSource getCodeSource(URL url, String name) {
 738         ensureInitialization();
 739         if (jv != null) {
 740             if (jv.eagerValidation) {
 741                 CodeSource cs = null;
 742                 JarEntry je = getJarEntry(name);
 743                 if (je != null) {
 744                     cs = jv.getCodeSource(url, this, je);
 745                 } else {
 746                     cs = jv.getCodeSource(url, name);
 747                 }
 748                 return cs;
 749             } else {
 750                 return jv.getCodeSource(url, name);
 751             }
 752         }
 753 
 754         return JarVerifier.getUnsignedCS(url);
 755     }
 756 
 757     void setEagerValidation(boolean eager) {
 758         try {
 759             maybeInstantiateVerifier();
 760         } catch (IOException e) {
 761             throw new RuntimeException(e);
 762         }
 763         if (jv != null) {
 764             jv.setEagerValidation(eager);
 765         }
 766     }
 767 
 768     List<Object> getManifestDigests() {
 769         ensureInitialization();
 770         if (jv != null) {
 771             return jv.getManifestDigests();
 772         }
 773         return new ArrayList<>();
 774     }
 775 }
--- EOF ---