src/share/classes/sun/misc/URLClassPath.java

Print this page
rev 10187 : mq

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -36,10 +36,11 @@
 import java.util.jar.Attributes;
 import java.util.jar.Attributes.Name;
 import java.net.JarURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLClassLoader;
 import java.net.URLConnection;
 import java.net.HttpURLConnection;
 import java.net.URLStreamHandler;
 import java.net.URLStreamHandlerFactory;
 import java.io.*;

@@ -50,10 +51,11 @@
 import java.security.PrivilegedAction;
 import java.security.PrivilegedExceptionAction;
 import java.security.cert.Certificate;
 import sun.misc.FileURLMapper;
 import sun.net.util.URLUtil;
+import sun.security.action.GetPropertyAction;
 
 /**
  * This class is used to maintain a search path of URLs for loading classes
  * and resources from both JAR files and directories.
  *

@@ -61,19 +63,22 @@
  */
 public class URLClassPath {
     final static String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
     final static String JAVA_VERSION;
     private static final boolean DEBUG;
+    private static final boolean DEBUG_LOOKUP_CACHE;
     private static final boolean DISABLE_JAR_CHECKING;
 
     static {
         JAVA_VERSION = java.security.AccessController.doPrivileged(
-            new sun.security.action.GetPropertyAction("java.version"));
+            new GetPropertyAction("java.version"));
         DEBUG        = (java.security.AccessController.doPrivileged(
-            new sun.security.action.GetPropertyAction("sun.misc.URLClassPath.debug")) != null);
+            new GetPropertyAction("sun.misc.URLClassPath.debug")) != null);
+        DEBUG_LOOKUP_CACHE = (java.security.AccessController.doPrivileged(
+            new GetPropertyAction("sun.misc.URLClassPath.debugLookupCache")) != null);
         String p = java.security.AccessController.doPrivileged(
-            new sun.security.action.GetPropertyAction("sun.misc.URLClassPath.disableJarChecking"));
+            new GetPropertyAction("sun.misc.URLClassPath.disableJarChecking"));
         DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.equals("") : false;
     }
 
     /* The original search path of URLs. */
     private ArrayList<URL> path = new ArrayList<URL>();

@@ -147,10 +152,16 @@
             if (url == null || path.contains(url))
                 return;
 
             urls.add(0, url);
             path.add(url);
+
+            if (lookupCacheURLs != null) {
+                // The lookup cache is no longer valid, since getLookupCache()
+                // does not consider the newly added url.
+                disableAllLookupCaches();
+            }
         }
     }
 
     /**
      * Returns the original search path of URLs.

@@ -170,11 +181,12 @@
      * @return a <code>URL</code> for the resource, or <code>null</code>
      * if the resource could not be found.
      */
     public URL findResource(String name, boolean check) {
         Loader loader;
-        for (int i = 0; (loader = getLoader(i)) != null; i++) {
+        int[] cache = getLookupCache(name);
+        for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) {
             URL url = loader.findResource(name, check);
             if (url != null) {
                 return url;
             }
         }

@@ -193,11 +205,12 @@
         if (DEBUG) {
             System.err.println("URLClassPath.getResource(\"" + name + "\")");
         }
 
         Loader loader;
-        for (int i = 0; (loader = getLoader(i)) != null; i++) {
+        int[] cache = getLookupCache(name);
+        for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) {
             Resource res = loader.getResource(name, check);
             if (res != null) {
                 return res;
             }
         }

@@ -213,18 +226,19 @@
      */
     public Enumeration<URL> findResources(final String name,
                                      final boolean check) {
         return new Enumeration<URL>() {
             private int index = 0;
+            private int[] cache = getLookupCache(name);
             private URL url = null;
 
             private boolean next() {
                 if (url != null) {
                     return true;
                 } else {
                     Loader loader;
-                    while ((loader = getLoader(index++)) != null) {
+                    while ((loader = getNextLoader(cache, index++)) != null) {
                         url = loader.findResource(name, check);
                         if (url != null) {
                             return true;
                         }
                     }

@@ -260,18 +274,19 @@
      */
     public Enumeration<Resource> getResources(final String name,
                                     final boolean check) {
         return new Enumeration<Resource>() {
             private int index = 0;
+            private int[] cache = getLookupCache(name);
             private Resource res = null;
 
             private boolean next() {
                 if (res != null) {
                     return true;
                 } else {
                     Loader loader;
-                    while ((loader = getLoader(index++)) != null) {
+                    while ((loader = getNextLoader(cache, index++)) != null) {
                         res = loader.getResource(name, check);
                         if (res != null) {
                             return true;
                         }
                     }

@@ -296,10 +311,155 @@
 
     public Enumeration<Resource> getResources(final String name) {
         return getResources(name, true);
     }
 
+    private static volatile boolean lookupCacheEnabled
+        = "true".equals(VM.getSavedProperty("sun.cds.enableSharedLookupCache"));
+    private URL[] lookupCacheURLs;
+    private ClassLoader lookupCacheLoader;
+
+    synchronized void initLookupCache(ClassLoader loader) {
+        if ((lookupCacheURLs = getLookupCacheURLs(loader)) != null) {
+            lookupCacheLoader = loader;
+        } else {
+            // This JVM instance does not support lookup cache.
+            disableAllLookupCaches();
+        }
+    }
+
+    static void disableAllLookupCaches() {
+        lookupCacheEnabled = false;
+    }
+
+    private static native URL[] getLookupCacheURLs(ClassLoader loader);
+    private static native int[] getLookupCacheForClassLoader(ClassLoader loader,
+                                                             String name);
+    private static native boolean knownToNotExist0(ClassLoader loader,
+                                                   String className);
+
+    synchronized boolean knownToNotExist(String className) {
+        if (lookupCacheURLs != null && lookupCacheEnabled) {
+            return knownToNotExist0(lookupCacheLoader, className);
+        }
+
+        // Don't know if this class exists or not -- need to do a full search.
+        return false;
+    }
+
+    /**
+     * Returns an array of the index to lookupCacheURLs that may
+     * contain the specified resource. The values in the returned
+     * array are in strictly ascending order and must be a valid index
+     * to lookupCacheURLs array.
+     *
+     * This method returns an empty array if the specified resource
+     * cannot be found in this URLClassPath. If there is no lookup
+     * cache or it's disabled, this method returns null and the lookup
+     * should search the entire classpath.
+     *
+     * Example: if lookupCacheURLs contains {a.jar, b.jar, c.jar, d.jar}
+     * and package "foo" only exists in a.jar and c.jar,
+     * getLookupCache("foo/Bar.class") will return {0, 2}
+     *
+     * @param name the resource name
+     * @return an array of the index to lookupCacheURLs that may contain the
+     *         specified resource; or null if no lookup cache is used.
+     */
+    private synchronized int[] getLookupCache(String name) {
+        if (lookupCacheURLs == null || !lookupCacheEnabled) {
+            return null;
+        }
+
+        int[] cache = getLookupCacheForClassLoader(lookupCacheLoader, name);
+        if (cache != null && cache.length > 0) {
+            int maxindex = cache[cache.length - 1]; // cache[] is strictly ascending.
+            if (!ensureLoaderOpened(maxindex)) {
+                if (DEBUG_LOOKUP_CACHE) {
+                    System.out.println("Expanded loaders FAILED " +
+                                       loaders.size() + " for maxindex=" + maxindex);
+                }
+                return null;
+            }
+        }
+
+        return cache;
+    }
+
+    private boolean ensureLoaderOpened(int index) {
+        if (loaders.size() <= index) {
+            // Open all Loaders up to, and including, index
+            if (getLoader(index) == null) {
+                return false;
+            }
+            if (!lookupCacheEnabled) {
+                // cache was invalidated as the result of the above call.
+                return false;
+            }
+            if (DEBUG_LOOKUP_CACHE) {
+                System.out.println("Expanded loaders " + loaders.size() +
+                                   " to index=" + index);
+            }
+        }
+        return true;
+    }
+
+    /*
+     * The CLASS-PATH attribute was expanded by the VM when building
+     * the resource lookup cache in the same order as the getLoader
+     * method does. This method validates if the URL from the lookup
+     * cache matches the URL of the Loader at the given index;
+     * otherwise, this method disables the lookup cache.
+     */
+    private synchronized void validateLookupCache(int index,
+                                                  String urlNoFragString) {
+        if (lookupCacheURLs != null && lookupCacheEnabled) {
+            if (index < lookupCacheURLs.length &&
+                urlNoFragString.equals(
+                    URLUtil.urlNoFragString(lookupCacheURLs[index]))) {
+                return;
+            }
+            if (DEBUG || DEBUG_LOOKUP_CACHE) {
+                System.out.println("WARNING: resource lookup cache invalidated "
+                                   + "for lookupCacheLoader at " + index);
+            }
+            disableAllLookupCaches();
+        }
+    }
+
+    /**
+     * Returns the next Loader that may contain the resource to
+     * lookup. If the given cache is null, return loaders.get(index)
+     * that may be lazily created; otherwise, cache[index] is the next
+     * Loader that may contain the resource to lookup and so returns
+     * loaders.get(cache[index]).
+     *
+     * If cache is non-null, loaders.get(cache[index]) must be present.
+     *
+     * @param cache lookup cache. If null, search the entire class path
+     * @param index index to the given cache array; or to the loaders list.
+     */
+    private synchronized Loader getNextLoader(int[] cache, int index) {
+        if (closed) {
+            return null;
+        }
+        if (cache != null) {
+            if (index < cache.length) {
+                Loader loader = loaders.get(cache[index]);
+                if (DEBUG_LOOKUP_CACHE) {
+                    System.out.println("HASCACHE: Loading from : " + cache[index]
+                                       + " = " + loader.getBaseURL());
+                }
+                return loader;
+            } else {
+                return null; // finished iterating over cache[]
+            }
+        } else {
+            return getLoader(index);
+        }
+    }
+
     /*
      * Returns the Loader at the specified position in the URL search
      * path. The URLs are opened and expanded as needed. Returns null
      * if the specified index is out of range.
      */

@@ -339,13 +499,17 @@
             } catch (IOException e) {
                 // Silently ignore for now...
                 continue;
             }
             // Finally, add the Loader to the search path.
+            validateLookupCache(loaders.size(), urlNoFragString);
             loaders.add(loader);
             lmap.put(urlNoFragString, loader);
         }
+        if (DEBUG_LOOKUP_CACHE) {
+            System.out.println("NOCACHE: Loading from : " + index );
+        }
         return loaders.get(index);
     }
 
     /*
      * Returns the Loader for the specified base URL.