< prev index next >

src/java.base/share/classes/java/util/jar/JarFile.java

Print this page
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

*** 26,95 **** package java.util.jar; import java.io.*; import java.lang.ref.SoftReference; import java.net.URL; import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; import java.util.zip.*; import java.security.CodeSigner; import java.security.cert.Certificate; import java.security.AccessController; import java.security.CodeSource; import jdk.internal.misc.SharedSecrets; - import sun.security.action.GetPropertyAction; import sun.security.util.ManifestEntryVerifier; import sun.security.util.SignatureFileVerifier; /** * The {@code JarFile} class is used to read the contents of a jar file * from any file that can be opened with {@code java.io.RandomAccessFile}. * It extends the class {@code java.util.zip.ZipFile} with support ! * for reading an optional {@code Manifest} entry. The ! * {@code Manifest} can be used to specify meta-information about the ! * jar file and its entries. * ! * <p> Unless otherwise noted, passing a {@code null} argument to a constructor ! * or method in this class will cause a {@link NullPointerException} to be ! * thrown. * ! * If the verify flag is on when opening a signed jar file, the content of the ! * file is verified against its signature embedded inside the file. Please note ! * that the verification process does not include validating the signer's * certificate. A caller should inspect the return value of * {@link JarEntry#getCodeSigners()} to further determine if the signature * can be trusted. * * @author David Connelly * @see Manifest * @see java.util.zip.ZipFile * @see java.util.jar.JarEntry * @since 1.2 */ public class JarFile extends ZipFile { private SoftReference<Manifest> manRef; private JarEntry manEntry; private JarVerifier jv; private boolean jvInitialized; private boolean verify; // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true) private boolean hasClassPathAttribute; // true if manifest checked for special attributes private volatile boolean hasCheckedSpecialAttributes; - // Set up JavaUtilJarAccess in SharedSecrets static { SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); } /** * The JAR manifest file name. */ ! public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; /** * Creates a new {@code JarFile} to read from the specified * file {@code name}. The {@code JarFile} will be verified if * it is signed. --- 26,225 ---- package java.util.jar; import java.io.*; import java.lang.ref.SoftReference; import java.net.URL; + import java.security.PrivilegedAction; import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; import java.util.zip.*; import java.security.CodeSigner; import java.security.cert.Certificate; import java.security.AccessController; import java.security.CodeSource; import jdk.internal.misc.SharedSecrets; import sun.security.util.ManifestEntryVerifier; import sun.security.util.SignatureFileVerifier; + import static java.util.jar.Attributes.Name.MULTI_RELEASE; + /** * The {@code JarFile} class is used to read the contents of a jar file * from any file that can be opened with {@code java.io.RandomAccessFile}. * It extends the class {@code java.util.zip.ZipFile} with support ! * for reading an optional {@code Manifest} entry, and support for ! * processing multi-release jar files. The {@code Manifest} can be used ! * to specify meta-information about the jar file and its entries. * ! * <p>A multi-release jar file is a jar file that contains a manifest with a ! * main attribute named "Multi-Release", and also contains a directory ! * "META-INF/versions" with subdirectories that contain versioned entries ! * segregated by the major version of Java platform releases, starting with ! * release 9. A versioned entry, with a version {@code n}, {@code 8 < n}, in ! * the "META-INF/versions/{n}" directory overrides the unversioned root entry ! * as well as any entry with a version number {@code i} where ! * {@code 8 < i < n}. * ! * <p>By default, a {@code JarFile} for a multi-release jar file is configured ! * to process the multi-release jar file as if it were a plain (unversioned) jar ! * file, and as such an entry name is associated with at most one root entry. ! * The {@code JarFile} may be configured to process a ! * multi-release jar file by setting a maximum version used when searching for ! * versioned entries (see methods {@link JarFile#setVersioned(int)} and ! * {@link JarFile#setRuntimeVersioned()}). When so configured, an entry name ! * can correspond with at most one root entry and zero or more versioned ! * entries. A search is required to associate the entry name with the latest ! * versioned entry whose version is less than or equal to the maximum version ! * (see {@link #getEntry(String)}). ! * ! * <p>Class loaders that utilize {@code JarFile} to load classes from the ! * contents of {@code JarFile} entries should, after construction of a ! * {@code JarFile}, call {@link JarFile#setRuntimeVersioned()} prior to any ! * subsequent operations. This assures that classes compatible with the major ! * version of the running JVM are loaded from multi-release jar files. ! * ! * <p>If the verify flag is on when opening a signed jar file, the content of ! * the file is verified against its signature embedded inside the file. Please ! * note that the verification process does not include validating the signer's * certificate. A caller should inspect the return value of * {@link JarEntry#getCodeSigners()} to further determine if the signature * can be trusted. * + * <p> Unless otherwise noted, passing a {@code null} argument to a constructor + * or method in this class will cause a {@link NullPointerException} to be + * thrown. + * + * @implNote + * <div class="block"> + * If the API can not be used to configure a {@code JarFile} (e.g. to override + * the configuration of a compiled application or library), two {@code System} + * properties are available. + * <ul> + * <li> + * {@code jdk.util.jar.version} can be assigned a value that is the + * {@code String} representation of a non-negative integer + * {@code <= Version.current().major()}. The value is used to set the effective + * runtime version to something other than the default value obtained by + * evaluating {@code Version.current().major()}. The effective runtime version + * is the version that the method {@link JarFile#setRuntimeVersioned()} uses. + * This makes the following two method invocations equivalent: + * {@code jf.setRuntimeVersioned()} and {@code jf.setVersioned(n)} where + * {@code jf} is a reference to a {@code JarFile} and {@code n} is the + * value assigned to the {@code jdk.util.jar.version} property. + * </li> + * <li> + * {@code jdk.util.jar.enableMultiRelease} can be assigned one of the three + * {@code String} values <em>true</em>, <em>false</em>, or <em>force</em>. The + * value <em>true</em>, the default value, enables multi-release jar file + * processing. The value <em>false</em> disables multi-release jar processing, + * ignoring the "Multi-Release" manifest attribute, and the versioned + * directories in a multi-release jar file if they exist. Furthermore, + * the method {@link JarFile#isMultiRelease()} returns <em>false</em> and the + * method {@link JarFile#setVersioned(int)} does nothing. The value + * <em>force</em> causes the {@code JarFile} to be initialized to runtime + * versioning after construction. It effectively does the same as this code: + * {@code (new JarFile(param)).setRuntimeVersioned()}. + * </li> + * </ul> + * </div> + * * @author David Connelly * @see Manifest * @see java.util.zip.ZipFile * @see java.util.jar.JarEntry * @since 1.2 */ public class JarFile extends ZipFile { + private final static int BASE_VERSION; + private final static int RUNTIME_VERSION; + private final static boolean MULTI_RELEASE_ENABLED; + private final static boolean MULTI_RELEASE_FORCED; + private volatile int version; private SoftReference<Manifest> manRef; private JarEntry manEntry; private JarVerifier jv; private boolean jvInitialized; private boolean verify; // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true) private boolean hasClassPathAttribute; // true if manifest checked for special attributes private volatile boolean hasCheckedSpecialAttributes; + // configuration lock for multi-release jars, prevent version changes after reading an entry + private boolean configured; + + /* + * Temporary place holder until java.util.Version (JEP 223) is integrated. + */ + private static class Version { + private static Version instance; + + private Version() { + super(); + } + + public static Version current() { + if (instance == null) { + instance = new Version(); + } + return instance; + } + + public int major() { + return sun.misc.Version.jdkMinorVersion(); + } + } static { + // Set up JavaUtilJarAccess in SharedSecrets SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); + + BASE_VERSION = 8; // current JDK implementation version minus 1 + RUNTIME_VERSION = AccessController.doPrivileged( + new PrivilegedAction<Integer>() { + public Integer run() { + Integer v = Version.current().major(); + Integer i = Integer.getInteger("jdk.util.jar.version", v); + i = i < 0 ? 0 : i; + return i > v ? v : i; + } + } + ); + String multi_release = AccessController.doPrivileged( + new PrivilegedAction<String>() { + public String run() { + return System.getProperty("jdk.util.jar.enableMultiRelease", "true"); + } + } + ); + switch (multi_release) { + case "true": + default: + MULTI_RELEASE_ENABLED = true; + MULTI_RELEASE_FORCED = false; + break; + case "false": + MULTI_RELEASE_ENABLED = false; + MULTI_RELEASE_FORCED = false; + break; + case "force": + MULTI_RELEASE_ENABLED = true; + MULTI_RELEASE_FORCED = true; + break; + } } + private static final String META_INF = "META-INF/"; + + private static final String META_INF_VERSIONS = META_INF + "versions/"; + /** * The JAR manifest file name. */ ! public static final String MANIFEST_NAME = META_INF + "MANIFEST.MF"; /** * Creates a new {@code JarFile} to read from the specified * file {@code name}. The {@code JarFile} will be verified if * it is signed.
*** 162,174 **** --- 292,429 ---- * @since 1.3 */ public JarFile(File file, boolean verify, int mode) throws IOException { super(file, mode); this.verify = verify; + this.version = MULTI_RELEASE_FORCED ? RUNTIME_VERSION : BASE_VERSION; + } + + /** + * If this {@code JarFile} is a multi-release jar file, sets the maximum + * version used when searching for versioned entries to the specified + * version. If the specified version is less than 9, then this + * {@code JarFile} is configured to be processed as if it is an unversioned + * jar file. + * + * <p>This method may be called one or more times after construction and + * before the first operation that obtains an entry, after which the + * {@code JarFile} configuration is considered immutable, and subsequent + * calls to this method will result in an {@code IllegalStateException}. + * + * <p>This method has no effect if this {@code JarFile} is not a + * multi-release jar file. + * + * @param version the maximum version + * + * @return this {@code JarFile} configured so that the search for versioned + * entries starts with the specified version + * + * @throws IllegalStateException if called after an entry has been obtained + * + * @see #getVersioned() + * @see #setRuntimeVersioned() + * @since 9 + **/ + public JarFile setVersioned(int version) { + if (isMultiRelease()) { + synchronized (this) { // see getVersionedEntry + if (configured) + throw new IllegalStateException("multi-release configuration locked"); + this.version = (version > BASE_VERSION) ? version : BASE_VERSION; + } + } + return this; + } + + /** + * If this {@code JarFile} is a multi-release jar file, sets the maximum + * version used when searching for versioned entries to the major version + * of the running JVM. + * + * <p>This method may be called one or more times after construction and + * before the first operation that obtains an entry, after which the + * {@code JarFile} configuration is considered immutable, and subsequent + * calls to this method will result in an {@code IllegalStateException}. + * + * <p>This method has no effect if this {@code JarFile} is not a + * multi-release jar file. + * + * @apiNote + * This method behaves the same as + * {@code JarFile.setVersioned(Version.current().major())}. + * + * @return this {@code JarFile} configured so that the search for versioned + * entries starts with the major version of the running JVM + * + * @throws IllegalStateException if called after an entry has been obtained + * + * @see #getVersioned() + * @see #setVersioned(int) + * @since 9 + */ + public JarFile setRuntimeVersioned() { + return setVersioned(RUNTIME_VERSION); + } + + /** + * Returns the maximum version used when searching for versioned entries. + * + * @return the maximum version, or {@code 0} if this jar file is + * processed as if it is an unversioned jar file or is not a + * multi-release jar file + * + * @see #setVersioned(int) + * @see #setRuntimeVersioned() + * @since 9 + */ + public int getVersioned() { + if (!isMultiRelease()) { + version = BASE_VERSION; // guard against inappropriate use of MULTI_RELEASE_FORCED + } + return version <= BASE_VERSION ? 0 : version; } /** + * Indicates whether or not this jar file is a multi-release jar file. + * + * @return true if this JarFile is a multi-release jar file + * @since 9 + */ + public boolean isMultiRelease() { + // do not call this code in a constructor because some subclasses use + // lazy loading of manifest so it won't be available at construction time + if (MULTI_RELEASE_ENABLED) { + // Doubled-checked locking pattern + Boolean result = isMultiRelease; + if (result == null) { + synchronized (this) { + result = isMultiRelease; + if (result == null) { + Manifest man = null; + try { + man = getManifest(); + } catch (IOException e) { + //Ignored, manifest cannot be read + } + isMultiRelease = result = (man != null) + && man.getMainAttributes().containsKey(MULTI_RELEASE) + ? Boolean.TRUE : Boolean.FALSE; + } + } + } + return result == Boolean.TRUE; + } else { + return false; + } + } + // the following field, isMultiRelease, should only be used in the method + // isMultiRelease(), like a static local + private volatile Boolean isMultiRelease; // is jar multi-release? + + + + /** * Returns the jar file manifest, or {@code null} if none. * * @return the jar file manifest, or {@code null} if none * * @throws IllegalStateException
*** 204,219 **** } private native String[] getMetaInfEntryNames(); /** ! * Returns the {@code JarEntry} for the given entry name or * {@code null} if not found. * * @param name the jar file entry name ! * @return the {@code JarEntry} for the given entry name or ! * {@code null} if not found. * * @throws IllegalStateException * may be thrown if the jar file has been closed * * @see java.util.jar.JarEntry --- 459,486 ---- } private native String[] getMetaInfEntryNames(); /** ! * Returns the {@code JarEntry} for the given root entry name or * {@code null} if not found. * + * <p>If this {@code JarFile} is a multi-release jar file and is configured + * to be processed as such, then a search is performed to find and return + * a {@code JarEntry} that is the latest versioned entry associated with the + * given entry name. The returned {@code JarEntry} is the versioned entry + * corresponding to the given root entry name prefixed with the string + * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for + * which an entry exists. If such a versioned entry does not exist, then + * the {@code JarEntry} for the root entry is returned, otherwise + * {@code null} is returned if no entries are found. The initial value for + * the version {@code n} is the maximum version as returned by the method + * {@link JarFile#getVersioned()}. + * * @param name the jar file entry name ! * @return the {@code JarEntry} for the given entry name, or ! * the versioned entry name, or {@code null} if not found * * @throws IllegalStateException * may be thrown if the jar file has been closed * * @see java.util.jar.JarEntry
*** 221,236 **** public JarEntry getJarEntry(String name) { return (JarEntry)getEntry(name); } /** ! * Returns the {@code ZipEntry} for the given entry name or * {@code null} if not found. * * @param name the jar file entry name * @return the {@code ZipEntry} for the given entry name or ! * {@code null} if not found * * @throws IllegalStateException * may be thrown if the jar file has been closed * * @see java.util.zip.ZipEntry --- 488,515 ---- public JarEntry getJarEntry(String name) { return (JarEntry)getEntry(name); } /** ! * Returns the {@code ZipEntry} for the given root entry name or * {@code null} if not found. * + * <p>If this {@code JarFile} is a multi-release jar file and is configured + * to be processed as such, then a search is performed to find and return + * a {@code ZipEntry} that is the latest versioned entry associated with the + * given entry name. The returned {@code ZipEntry} is the versioned entry + * corresponding to the given root entry name prefixed with the string + * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for + * which an entry exists. If such a versioned entry does not exist, then + * the {@code ZipEntry} for the root entry is returned, otherwise + * {@code null} is returned if no entries are found. The initial value for + * the version {@code n} is the maximum version as returned by the method + * {@link JarFile#getVersioned()}. + * * @param name the jar file entry name * @return the {@code ZipEntry} for the given entry name or ! * the versioned entry name or {@code null} if not found * * @throws IllegalStateException * may be thrown if the jar file has been closed * * @see java.util.zip.ZipEntry
*** 238,247 **** --- 517,534 ---- public ZipEntry getEntry(String name) { ZipEntry ze = super.getEntry(name); if (ze != null) { return new JarFileEntry(ze); } + // no matching base entry, but maybe there is a versioned entry, like a new private class + if (isMultiRelease()) { + ze = new ZipEntry(name); + ZipEntry vze = getVersionedEntry(ze); + if (ze != vze) { + return new JarFileEntry(name, vze); + } + } return null; } private class JarEntryIterator implements Enumeration<JarEntry>, Iterator<JarEntry>
*** 295,312 **** new JarEntryIterator(), size(), Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL), false); } private class JarFileEntry extends JarEntry { JarFileEntry(ZipEntry ze) { ! super(ze); } public Attributes getAttributes() throws IOException { Manifest man = JarFile.this.getManifest(); if (man != null) { ! return man.getAttributes(getName()); } else { return null; } } public Certificate[] getCertificates() { --- 582,673 ---- new JarEntryIterator(), size(), Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL), false); } + private ZipEntry searchForVersionedEntry(int version, String name) { + ZipEntry vze = null; + String sname = "/" + name; + int i = version; + do { + vze = JarFile.super.getEntry(META_INF_VERSIONS + i + sname); + if (vze != null) break; + } while (--i > BASE_VERSION); + return vze; + } + + /** + * Returns the runtime versioned {@code JarEntry} for the given root entry + * name or {@code null} if not found. + * + * <p>If this {@code JarFile} is a multi-release jar file, then a + * search is performed to find and return a {@code JarEntry} that + * is the runtime versioned entry associated with the + * given entry name. The runtime version is obtained by invoking + * {@code Version.current().major()}, perhaps modified by the two + * {@code System} properties described in the {@code JarFile} overview. + * The returned {@code JarEntry} is the versioned entry corresponding to the + * given root entry name prefixed with the string + * {@code "META-INF/versions/{n}/"}, for the largest value of + * {@code n, 8 < n <= runtime version}, for which an entry exists. If such a + * versioned entry does not exist, then the root {@code JarEntry} is returned + * if it exists, else {@code null} is returned. + * + * @param name the jar file entry name + * @return the {@code JarEntry} for the given versioned entry name, or + * {@code null} if not found + * + * @throws IllegalStateException + * may be thrown if the jar file has been closed + * + * @see java.util.jar.JarEntry + * @since 9 + */ + public JarEntry getRuntimeVersionedEntry(String name) { + ZipEntry vze = null; + if (RUNTIME_VERSION > BASE_VERSION && isMultiRelease() && !name.startsWith(META_INF)) { + vze = searchForVersionedEntry(RUNTIME_VERSION, name); + } + if (vze == null) { + vze = JarFile.super.getEntry(name); + } + return vze == null ? null : new JarFileEntry(name, vze); + } + + private ZipEntry getVersionedEntry(ZipEntry ze) { + ZipEntry vze = null; + if (!configured) { + synchronized (this) { // see setVersioned + configured = true; + } + } + // we know that version will not change at this point, but don't read it twice + int v = version; + if (v > BASE_VERSION && !ze.isDirectory()) { + String name = ze.getName(); + if (!name.startsWith(META_INF)) { + vze = searchForVersionedEntry(v, name); + } + } + return vze == null ? ze : vze; + } + private class JarFileEntry extends JarEntry { + final private String name; + JarFileEntry(ZipEntry ze) { ! super(isMultiRelease() ? getVersionedEntry(ze) : ze); ! this.name = ze.getName(); ! } ! JarFileEntry(String name, ZipEntry vze) { ! super(vze); ! this.name = name; } public Attributes getAttributes() throws IOException { Manifest man = JarFile.this.getManifest(); if (man != null) { ! return man.getAttributes(super.getName()); } else { return null; } } public Certificate[] getCertificates() {
*** 314,338 **** maybeInstantiateVerifier(); } catch (IOException e) { throw new RuntimeException(e); } if (certs == null && jv != null) { ! certs = jv.getCerts(JarFile.this, this); } return certs == null ? null : certs.clone(); } public CodeSigner[] getCodeSigners() { try { maybeInstantiateVerifier(); } catch (IOException e) { throw new RuntimeException(e); } if (signers == null && jv != null) { ! signers = jv.getCodeSigners(JarFile.this, this); } return signers == null ? null : signers.clone(); } } /* * Ensures that the JarVerifier has been created if one is * necessary (i.e., the jar appears to be signed.) This is done as --- 675,711 ---- maybeInstantiateVerifier(); } catch (IOException e) { throw new RuntimeException(e); } if (certs == null && jv != null) { ! certs = jv.getCerts(JarFile.this, reifiedEntry()); } return certs == null ? null : certs.clone(); } public CodeSigner[] getCodeSigners() { try { maybeInstantiateVerifier(); } catch (IOException e) { throw new RuntimeException(e); } if (signers == null && jv != null) { ! signers = jv.getCodeSigners(JarFile.this, reifiedEntry()); } return signers == null ? null : signers.clone(); } + JarFileEntry reifiedEntry() { + if (isMultiRelease()) { + String entryName = super.getName(); + return entryName.equals(this.name) ? this : new JarFileEntry(entryName, this); + } + return this; + } + + @Override + public String getName() { + return name; + } } /* * Ensures that the JarVerifier has been created if one is * necessary (i.e., the jar appears to be signed.) This is done as
*** 486,501 **** } // wrap a verifier stream around the real stream return new JarVerifier.VerifierStream( getManifestFromReference(), ! ze instanceof JarFileEntry ? ! (JarEntry) ze : getJarEntry(ze.getName()), super.getInputStream(ze), jv); } // Statics for hand-coded Boyer-Moore search private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'}; // The bad character shift for "class-path" private static final int[] CLASSPATH_LASTOCC; // The good suffix shift for "class-path" --- 859,881 ---- } // wrap a verifier stream around the real stream return new JarVerifier.VerifierStream( getManifestFromReference(), ! verifiableEntry(ze), super.getInputStream(ze), jv); } + private JarEntry verifiableEntry(ZipEntry ze) { + if (!(ze instanceof JarFileEntry)) { + ze = getJarEntry(ze.getName()); + } + // assure the name and entry match for verification + return ze == null ? null : ((JarFileEntry)ze).reifiedEntry(); + } + // Statics for hand-coded Boyer-Moore search private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'}; // The bad character shift for "class-path" private static final int[] CLASSPATH_LASTOCC; // The good suffix shift for "class-path"
*** 518,541 **** } private JarEntry getManEntry() { if (manEntry == null) { // First look up manifest entry using standard name ! manEntry = getJarEntry(MANIFEST_NAME); if (manEntry == null) { // If not found, then iterate through all the "META-INF/" // entries to find a match. String[] names = getMetaInfEntryNames(); if (names != null) { for (String name : names) { if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) { ! manEntry = getJarEntry(name); break; } } } } } return manEntry; } /** --- 898,922 ---- } private JarEntry getManEntry() { if (manEntry == null) { // First look up manifest entry using standard name ! ZipEntry manEntry = super.getEntry(MANIFEST_NAME); if (manEntry == null) { // If not found, then iterate through all the "META-INF/" // entries to find a match. String[] names = getMetaInfEntryNames(); if (names != null) { for (String name : names) { if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) { ! manEntry = super.getEntry(name); break; } } } } + this.manEntry = manEntry == null ? null : new JarFileEntry(manEntry.getName(), manEntry); } return manEntry; } /**
< prev index next >