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.