# HG changeset patch # User redestad # Date 1458866386 -3600 # Fri Mar 25 01:39:46 2016 +0100 # Node ID af66adb85269592326d470056a605cd52af0ed1e # Parent 3abd25870915496e0e06ba35349b726aab248e5d 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 diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -28,7 +28,6 @@ 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; @@ -38,11 +37,10 @@ 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; -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}. @@ -144,8 +142,9 @@ private final int version; private boolean notVersioned; private final boolean runtimeVersioned; + private boolean isMultiRelease; // is jar multi-release? - // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true) + // indicates if Class-Path attribute present private boolean hasClassPathAttribute; // true if manifest checked for special attributes private volatile boolean hasCheckedSpecialAttributes; @@ -155,24 +154,18 @@ SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); BASE_VERSION = 8; // one less than lowest version for versioned entries - RUNTIME_VERSION = AccessController.doPrivileged( - new PrivilegedAction() { - 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() { - public String run() { - return System.getProperty("jdk.util.jar.enableMultiRelease", "true"); - } - } - ); - switch (multi_release) { + 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; @@ -353,8 +346,14 @@ 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(); + if (MULTI_RELEASE_FORCED) { + this.version = RUNTIME_VERSION; + version = Release.RUNTIME; + } else { + this.version = version.value(); + } this.runtimeVersioned = version == Release.RUNTIME; + assert runtimeVersionExists(); } @@ -392,35 +391,18 @@ * @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; - } - } + if (isMultiRelease) { + return true; + } + if (MULTI_RELEASE_ENABLED && version != BASE_VERSION) { + try { + checkForSpecialAttributes(); + } catch (IOException io) { + isMultiRelease = false; } - return result == Boolean.TRUE; - } else { - return false; } + return isMultiRelease; } - // 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. @@ -905,15 +887,16 @@ } // 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; + 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 int[128]; - CLASSPATH_OPTOSFT = new int[10]; + CLASSPATH_LASTOCC = new byte[128]; CLASSPATH_LASTOCC[(int)'c'] = 1; CLASSPATH_LASTOCC[(int)'l'] = 2; CLASSPATH_LASTOCC[(int)'s'] = 5; @@ -922,9 +905,22 @@ 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; + 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() { @@ -962,22 +958,23 @@ /** * 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. + * The {@code lastOcc} array is the precomputed bad character shifts. */ - private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) { + 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--) { - char c = (char) b[i+j]; - c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c; + 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], optoSft[j]); + i += Math.max(j + 1 - lastOcc[c&0x7F], j < len - 1 ? len : 1); continue next; - } + } } return true; } @@ -986,17 +983,29 @@ /** * On first invocation, check if the JAR file has the Class-Path - * attribute. A no-op on subsequent calls. + * and the Multi-Release 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; + if (hasCheckedSpecialAttributes) { + return; } - hasCheckedSpecialAttributes = true; + 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() { diff --git a/test/java/util/jar/JarFile/MultiReleaseJarAPI.java b/test/java/util/jar/JarFile/MultiReleaseJarAPI.java --- a/test/java/util/jar/JarFile/MultiReleaseJarAPI.java +++ b/test/java/util/jar/JarFile/MultiReleaseJarAPI.java @@ -83,6 +83,10 @@ } try (JarFile jf = new JarFile(multirelease)) { + Assert.assertFalse(jf.isMultiRelease()); + } + + try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Release.RUNTIME)) { Assert.assertTrue(jf.isMultiRelease()); } }