src/java.base/share/classes/java/lang/Package.java

Print this page
rev 10855 : 8060130: Simplify the synchronization of defining and getting java.lang.Package
Reviewed-by: mchung, plevart, shade

*** 24,54 **** */ package java.lang; import java.lang.reflect.AnnotatedElement; - import java.io.InputStream; - import java.util.Enumeration; - import java.util.StringTokenizer; import java.io.File; import java.io.FileInputStream; - import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; - import java.util.jar.JarException; import java.util.Map; - import java.util.HashMap; - import java.util.Iterator; import sun.net.www.ParseUtil; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; --- 24,48 ---- */ package java.lang; import java.lang.reflect.AnnotatedElement; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.security.AccessController; import java.security.PrivilegedAction; + import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.util.Map; import sun.net.www.ParseUtil; import sun.reflect.CallerSensitive; import sun.reflect.Reflection;
*** 536,632 **** /* * Returns the loaded system package for the specified name. */ static Package getSystemPackage(String name) { - synchronized (pkgs) { Package pkg = pkgs.get(name); if (pkg == null) { name = name.replace('.', '/').concat("/"); String fn = getSystemPackage0(name); if (fn != null) { pkg = defineSystemPackage(name, fn); } } return pkg; } - } /* * Return an array of loaded system packages. */ static Package[] getSystemPackages() { // First, update the system package map with new package names String[] names = getSystemPackages0(); - synchronized (pkgs) { for (String name : names) { defineSystemPackage(name, getSystemPackage0(name)); } - return pkgs.values().toArray(new Package[pkgs.size()]); } } private static Package defineSystemPackage(final String iname, final String fn) { ! return AccessController.doPrivileged(new PrivilegedAction<Package>() { ! public Package run() { ! String name = iname; ! // Get the cached code source url for the file name ! URL url = urls.get(fn); ! if (url == null) { ! // URL not found, so create one ! File file = new File(fn); ! try { ! url = ParseUtil.fileToEncodedURL(file); ! } catch (MalformedURLException e) { } ! if (url != null) { ! urls.put(fn, url); ! // If loading a JAR file, then also cache the manifest ! if (file.isFile()) { ! mans.put(fn, loadManifest(fn)); } } } - // Convert to "."-separated package name - name = name.substring(0, name.length() - 1).replace('/', '.'); - Package pkg; - Manifest man = mans.get(fn); - if (man != null) { - pkg = new Package(name, man, url, null); - } else { - pkg = new Package(name, null, null, null, - null, null, null, null, null); } ! pkgs.put(name, pkg); ! return pkg; } }); } ! /* ! * Returns the Manifest for the specified JAR file name. ! */ ! private static Manifest loadManifest(String fn) { ! try (FileInputStream fis = new FileInputStream(fn); ! JarInputStream jis = new JarInputStream(fis, false)) ! { return jis.getManifest(); } catch (IOException e) { return null; } } ! ! // The map of loaded system packages ! private static Map<String, Package> pkgs = new HashMap<>(31); ! ! // Maps each directory or zip file name to its corresponding url ! private static Map<String, URL> urls = new HashMap<>(10); ! ! // Maps each code source url for a jar file to its manifest ! private static Map<String, Manifest> mans = new HashMap<>(10); private static native String getSystemPackage0(String name); private static native String[] getSystemPackages0(); /* --- 530,648 ---- /* * Returns the loaded system package for the specified name. */ static Package getSystemPackage(String name) { Package pkg = pkgs.get(name); if (pkg == null) { name = name.replace('.', '/').concat("/"); String fn = getSystemPackage0(name); if (fn != null) { pkg = defineSystemPackage(name, fn); } } return pkg; } /* * Return an array of loaded system packages. */ static Package[] getSystemPackages() { // First, update the system package map with new package names String[] names = getSystemPackages0(); for (String name : names) { + if (!pkgs.containsKey(name)) { defineSystemPackage(name, getSystemPackage0(name)); } } + return pkgs.values().toArray(new Package[pkgs.size()]); } private static Package defineSystemPackage(final String iname, final String fn) { ! // Convert to "."-separated package name ! String name = iname.substring(0, iname.length() - 1).replace('/', '.'); ! // Creates a cached manifest for the file name, allowing ! // only-once, lazy reads of manifest from jar files ! CachedManifest cachedManifest = createCachedManifest(fn); ! pkgs.putIfAbsent(name, new Package(name, cachedManifest.getManifest(), ! cachedManifest.getURL(), null)); ! // Ensure we only expose one Package object ! return pkgs.get(name); } ! ! private static CachedManifest createCachedManifest(String fn) { ! if (!manifests.containsKey(fn)) { ! manifests.putIfAbsent(fn, new CachedManifest(fn)); } + return manifests.get(fn); } + + // The map of loaded system packages + private static final ConcurrentHashMap<String, Package> pkgs + = new ConcurrentHashMap<>(); + + // Maps each directory or zip file name to its corresponding manifest, if + // it exists + private static final ConcurrentHashMap<String, CachedManifest> manifests + = new ConcurrentHashMap<>(); + + private static class CachedManifest { + private static final Manifest EMPTY_MANIFEST = new Manifest(); + private final String fileName; + private final URL url; + private volatile Manifest manifest; + + CachedManifest(final String fileName) { + this.fileName = fileName; + this.url = AccessController.doPrivileged(new PrivilegedAction<URL>() { + public URL run() { + final File file = new File(fileName); + if (file.isFile()) { + try { + return ParseUtil.fileToEncodedURL(file); + } catch (MalformedURLException e) { } } ! return null; } }); } ! public URL getURL() { ! return url; ! } ! ! public Manifest getManifest() { ! if (url == null) { ! return EMPTY_MANIFEST; ! } ! Manifest m = manifest; ! if (m != null) { ! return m; ! } ! synchronized (this) { ! m = manifest; ! if (m != null) { ! return m; ! } ! m = AccessController.doPrivileged(new PrivilegedAction<Manifest>() { ! public Manifest run() { ! try (FileInputStream fis = new FileInputStream(fileName); ! JarInputStream jis = new JarInputStream(fis, false)) { return jis.getManifest(); } catch (IOException e) { return null; } } ! }); ! manifest = m = (m == null ? EMPTY_MANIFEST : m); ! } ! return m; ! } ! } private static native String getSystemPackage0(String name); private static native String[] getSystemPackages0(); /*