< prev index next >

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

Print this page
rev 13960 : 8152733: Avoid creating Manifest when checking for Multi-Release attribute
Reviewed-by: psandoz, sdrach, dchuyko
Contributed-by: claes.redestad@oracle.com, steve.drach@oracle.com

*** 26,50 **** 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 --- 26,48 ---- 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, and support for
*** 142,180 **** private boolean jvInitialized; private boolean verify; private final int version; private boolean notVersioned; private final boolean runtimeVersioned; ! // 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; static { // Set up JavaUtilJarAccess in SharedSecrets SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); BASE_VERSION = 8; // one less than lowest version for versioned entries ! RUNTIME_VERSION = AccessController.doPrivileged( ! new PrivilegedAction<Integer>() { ! public Integer run() { ! Integer v = jdk.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; --- 140,173 ---- private boolean jvInitialized; private boolean verify; private final int version; private boolean notVersioned; private final boolean runtimeVersioned; + private boolean isMultiRelease; // is jar multi-release? ! // indicates if Class-Path attribute present private boolean hasClassPathAttribute; // true if manifest checked for special attributes private volatile boolean hasCheckedSpecialAttributes; static { // Set up JavaUtilJarAccess in SharedSecrets SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); BASE_VERSION = 8; // one less than lowest version for versioned entries ! int runtimeVersion = jdk.Version.current().major(); ! String jarVersion = AccessController.doPrivileged( ! new GetPropertyAction("jdk.util.jar.version")); ! if (jarVersion != null) { ! int jarVer = Integer.parseInt(jarVersion); ! runtimeVersion = (jarVer > runtimeVersion) ! ? runtimeVersion : Math.max(jarVer, 0); ! } ! RUNTIME_VERSION = runtimeVersion; ! String enableMultiRelease = AccessController.doPrivileged( ! new GetPropertyAction("jdk.util.jar.enableMultiRelease", "true")); ! switch (enableMultiRelease) { case "true": default: MULTI_RELEASE_ENABLED = true; MULTI_RELEASE_FORCED = false; break;
*** 351,362 **** public JarFile(File file, boolean verify, int mode, Release version) throws IOException { super(file, mode); Objects.requireNonNull(version); this.verify = verify; // version applies to multi-release jar files, ignored for regular jar files ! this.version = MULTI_RELEASE_FORCED ? RUNTIME_VERSION : version.value(); this.runtimeVersioned = version == Release.RUNTIME; assert runtimeVersionExists(); } private boolean runtimeVersionExists() { int version = jdk.Version.current().major(); --- 344,361 ---- public JarFile(File file, boolean verify, int mode, Release version) throws IOException { super(file, mode); Objects.requireNonNull(version); this.verify = verify; // version applies to multi-release jar files, ignored for regular jar files ! if (MULTI_RELEASE_FORCED) { ! this.version = RUNTIME_VERSION; ! version = Release.RUNTIME; ! } else { ! this.version = version.value(); ! } this.runtimeVersioned = version == Release.RUNTIME; + assert runtimeVersionExists(); } private boolean runtimeVersionExists() { int version = jdk.Version.current().major();
*** 390,428 **** * * @return true if this JarFile is a multi-release jar file * @since 9 */ public final 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 --- 389,410 ---- * * @return true if this JarFile is a multi-release jar file * @since 9 */ public final boolean isMultiRelease() { ! if (isMultiRelease) { ! return true; } + if (MULTI_RELEASE_ENABLED && version != BASE_VERSION) { + try { + checkForSpecialAttributes(); + } catch (IOException io) { + isMultiRelease = false; } } + return isMultiRelease; } /** * Returns the jar file manifest, or {@code null} if none. * * @return the jar file manifest, or {@code null} if none
*** 903,932 **** } return (JarEntry)ze; } // 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" ! private static final int[] CLASSPATH_OPTOSFT; static { ! CLASSPATH_LASTOCC = new int[128]; ! CLASSPATH_OPTOSFT = new int[10]; CLASSPATH_LASTOCC[(int)'c'] = 1; CLASSPATH_LASTOCC[(int)'l'] = 2; CLASSPATH_LASTOCC[(int)'s'] = 5; CLASSPATH_LASTOCC[(int)'-'] = 6; CLASSPATH_LASTOCC[(int)'p'] = 7; CLASSPATH_LASTOCC[(int)'a'] = 8; CLASSPATH_LASTOCC[(int)'t'] = 9; CLASSPATH_LASTOCC[(int)'h'] = 10; ! for (int i=0; i<9; i++) ! CLASSPATH_OPTOSFT[i] = 10; ! CLASSPATH_OPTOSFT[9]=1; } private JarEntry getManEntry() { if (manEntry == null) { // First look up manifest entry using standard name --- 885,928 ---- } return (JarEntry)ze; } // Statics for hand-coded Boyer-Moore search ! private static final byte[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h', ':', ' '}; ! // The bad character shift for "class-path:" ! private static final byte[] CLASSPATH_LASTOCC; ! ! private static final byte[] MULTIRELEASE_CHARS = {'m','u','l','t','i','-','r','e','l','e', 'a', 's', 'e', ':', ' '}; ! // The bad character shift for "multi-release: " ! private static final byte[] MULTIRELEASE_LASTOCC; static { ! CLASSPATH_LASTOCC = new byte[128]; CLASSPATH_LASTOCC[(int)'c'] = 1; CLASSPATH_LASTOCC[(int)'l'] = 2; CLASSPATH_LASTOCC[(int)'s'] = 5; CLASSPATH_LASTOCC[(int)'-'] = 6; CLASSPATH_LASTOCC[(int)'p'] = 7; CLASSPATH_LASTOCC[(int)'a'] = 8; CLASSPATH_LASTOCC[(int)'t'] = 9; CLASSPATH_LASTOCC[(int)'h'] = 10; ! CLASSPATH_LASTOCC[(int)':'] = 11; ! CLASSPATH_LASTOCC[(int)' '] = 12; ! ! MULTIRELEASE_LASTOCC = new byte[128]; ! MULTIRELEASE_LASTOCC[(int)'m'] = 1; ! MULTIRELEASE_LASTOCC[(int)'u'] = 2; ! MULTIRELEASE_LASTOCC[(int)'t'] = 4; ! MULTIRELEASE_LASTOCC[(int)'i'] = 5; ! MULTIRELEASE_LASTOCC[(int)'-'] = 6; ! MULTIRELEASE_LASTOCC[(int)'r'] = 7; ! MULTIRELEASE_LASTOCC[(int)'l'] = 9; ! MULTIRELEASE_LASTOCC[(int)'a'] = 11; ! MULTIRELEASE_LASTOCC[(int)'s'] = 12; ! MULTIRELEASE_LASTOCC[(int)'e'] = 13; ! MULTIRELEASE_LASTOCC[(int)':'] = 14; ! MULTIRELEASE_LASTOCC[(int)' '] = 15; } private JarEntry getManEntry() { if (manEntry == null) { // First look up manifest entry using standard name
*** 960,1005 **** return hasClassPathAttribute; } /** * Returns true if the pattern {@code src} is found in {@code b}. ! * The {@code lastOcc} and {@code optoSft} arrays are the precomputed ! * bad character and good suffix shifts. */ ! private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) { int len = src.length; int last = b.length - len; int i = 0; next: ! while (i<=last) { ! for (int j=(len-1); j>=0; j--) { ! char c = (char) b[i+j]; ! c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c; if (c != src[j]) { ! i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]); continue next; } } return true; } return false; } /** * On first invocation, check if the JAR file has the Class-Path ! * attribute. A no-op on subsequent calls. */ private void checkForSpecialAttributes() throws IOException { ! if (hasCheckedSpecialAttributes) return; JarEntry manEntry = getManEntry(); if (manEntry != null) { byte[] b = getBytes(manEntry); ! if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT)) ! hasClassPathAttribute = true; } hasCheckedSpecialAttributes = true; } private synchronized void ensureInitialization() { try { maybeInstantiateVerifier(); } catch (IOException e) { --- 956,1014 ---- return hasClassPathAttribute; } /** * Returns true if the pattern {@code src} is found in {@code b}. ! * The {@code lastOcc} array is the precomputed bad character shifts. */ ! private boolean match(byte[] src, byte[] b, byte[] lastOcc) { int len = src.length; int last = b.length - len; int i = 0; next: ! while (i <= last) { ! for (int j = (len - 1); j >= 0; j--) { ! byte c = b[i + j]; ! if (c >= 'A' && c <= 'Z') { ! c += 32; ! } if (c != src[j]) { ! i += Math.max(j + 1 - lastOcc[c&0x7F], j < len - 1 ? len : 1); continue next; } } return true; } return false; } /** * On first invocation, check if the JAR file has the Class-Path ! * and the Multi-Release attribute. A no-op on subsequent calls. */ private void checkForSpecialAttributes() throws IOException { ! if (hasCheckedSpecialAttributes) { ! return; ! } ! synchronized (this) { ! if (hasCheckedSpecialAttributes) { ! return; ! } JarEntry manEntry = getManEntry(); if (manEntry != null) { byte[] b = getBytes(manEntry); ! hasClassPathAttribute = match(CLASSPATH_CHARS, b, ! CLASSPATH_LASTOCC); ! // is this a multi-release jar file ! if (MULTI_RELEASE_ENABLED && version != BASE_VERSION) { ! isMultiRelease = match(MULTIRELEASE_CHARS, b, ! MULTIRELEASE_LASTOCC); ! } } hasCheckedSpecialAttributes = true; } + } private synchronized void ensureInitialization() { try { maybeInstantiateVerifier(); } catch (IOException e) {
< prev index next >