diff options
author | Tom Tromey <tromey@redhat.com> | 2006-05-19 20:58:58 +0000 |
---|---|---|
committer | Tom Tromey <tromey@redhat.com> | 2006-05-19 20:58:58 +0000 |
commit | eb537850a6328e1669b935f6b7241e49854a015c (patch) | |
tree | 9459d11fdf72a9116f8da940a19c8d07d2ae2b9e | |
parent | dd1904f2bc79d6debc857c04c9a0b814b7b63a8f (diff) | |
download | classpath-eb537850a6328e1669b935f6b7241e49854a015c.tar.gz |
PR classpath/27444:
* gnu/java/net/loader/URLLoader.java (getClassPath): Documented.
Changed return type.
* java/net/URLClassLoader.java (urlloaders): Removed.
(addURLImpl): Updated.
* gnu/java/net/loader/JarURLLoader.java (initialized): New field.
(indexSet): Likewise.
(classPath): Changed type.
(JarURLLoader): New constructor.
(initialize): New method.
(getResource): Use index set if it exists.
(getClassPath): Updated.
* gnu/java/net/IndexListParser.java (IndexListParser): Avoid NPE.
(prefixes): New field.
(headers): Removed.
(IndexListParser): Fill in prefixes.
(clearAll): Clear prefixes.
(getHeaders): Changed return type.
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | gnu/java/net/IndexListParser.java | 47 | ||||
-rw-r--r-- | gnu/java/net/loader/JarURLLoader.java | 165 | ||||
-rw-r--r-- | gnu/java/net/loader/URLLoader.java | 8 | ||||
-rw-r--r-- | java/net/URLClassLoader.java | 225 |
5 files changed, 286 insertions, 180 deletions
@@ -1,3 +1,24 @@ +2006-05-19 Tom Tromey <tromey@redhat.com> + + PR classpath/27444: + * gnu/java/net/loader/URLLoader.java (getClassPath): Documented. + Changed return type. + * java/net/URLClassLoader.java (urlloaders): Removed. + (addURLImpl): Updated. + * gnu/java/net/loader/JarURLLoader.java (initialized): New field. + (indexSet): Likewise. + (classPath): Changed type. + (JarURLLoader): New constructor. + (initialize): New method. + (getResource): Use index set if it exists. + (getClassPath): Updated. + * gnu/java/net/IndexListParser.java (IndexListParser): Avoid NPE. + (prefixes): New field. + (headers): Removed. + (IndexListParser): Fill in prefixes. + (clearAll): Clear prefixes. + (getHeaders): Changed return type. + 2006-05-19 Audrius Meskauskas <AudriusA@Bioinformatics.org> * javax/swing/plaf/basic/BasicTableHeaderUI.java diff --git a/gnu/java/net/IndexListParser.java b/gnu/java/net/IndexListParser.java index cd4ca556b..23d2aa660 100644 --- a/gnu/java/net/IndexListParser.java +++ b/gnu/java/net/IndexListParser.java @@ -41,7 +41,8 @@ package gnu.java.net; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; -import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.jar.JarFile; /** @@ -67,7 +68,9 @@ public class IndexListParser public static final String JAR_INDEX_VERSION_KEY = "JarIndex-Version: "; double versionNumber; - ArrayList headers = new ArrayList(); + // Map each jar to the prefixes defined for the jar. + // This is intentionally kept in insertion order. + LinkedHashMap prefixes = new LinkedHashMap(); /** * Parses the given jarfile's INDEX.LIST file if it exists. @@ -94,22 +97,33 @@ public class IndexListParser // Blank line must be next line = br.readLine(); - if (!line.equals("")) + if (! "".equals(line)) { clearAll(); return; } - // May contain sections - line = br.readLine(); - while (line != null) + // May contain sections. + while ((line = br.readLine()) != null) { - headers.add(new URL(baseURL, line)); + URL jarURL = new URL(baseURL, line); + HashSet values = new HashSet(); - // Skip all names in the section - while (! (line = br.readLine()).equals("")); - line = br.readLine(); + // Read the names in the section. + while ((line = br.readLine()) != null) + { + // Stop at section boundary. + if ("".equals(line)) + break; + values.add(line.trim()); + } + prefixes.put(jarURL, values); + // Might have seen an early EOF. + if (line == null) + break; } + + br.close(); } // else INDEX.LIST does not exist } @@ -125,7 +139,7 @@ public class IndexListParser void clearAll() { versionNumber = 0; - headers.clear(); + prefixes = null; } /** @@ -149,12 +163,15 @@ public class IndexListParser } /** - * Gets the array list of all the headers found in the file. + * Gets the map of all the headers found in the file. + * The keys in the map are URLs of jars. The values in the map + * are Sets of package prefixes (and top-level file names), as + * specifed in INDEX.LIST. * - * @return an array list of all the headers. + * @return an map of all the headers, or null if no INDEX.LIST was found */ - public ArrayList getHeaders() + public LinkedHashMap getHeaders() { - return headers; + return prefixes; } } diff --git a/gnu/java/net/loader/JarURLLoader.java b/gnu/java/net/loader/JarURLLoader.java index e07b87b0d..130c6fc95 100644 --- a/gnu/java/net/loader/JarURLLoader.java +++ b/gnu/java/net/loader/JarURLLoader.java @@ -4,12 +4,15 @@ import gnu.java.net.IndexListParser; import java.io.IOException; import java.net.JarURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandlerFactory; import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Set; import java.util.StringTokenizer; -import java.util.Vector; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -21,26 +24,62 @@ import java.util.jar.Manifest; */ public final class JarURLLoader extends URLLoader { - final JarFile jarfile; // The jar file for this url - final URL baseJarURL; // Base jar: url for all resources loaded from jar + // True if we've initialized -- i.e., tried open the jar file. + boolean initialized; + // The jar file for this url. + JarFile jarfile; + // Base jar: url for all resources loaded from jar. + final URL baseJarURL; + // The "Class-Path" attribute of this Jar's manifest. + ArrayList classPath; + // If not null, a mapping from INDEX.LIST for this jar only. + // This is a set of all prefixes and top-level files that + // ought to be available in this jar. + Set indexSet; - Vector classPath; // The "Class-Path" attribute of this Jar's manifest + // This constructor is used internally. It purposely does not open + // the jar file -- it defers this until later. This allows us to + // implement INDEX.LIST lazy-loading semantics. + private JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL baseURL, URL absoluteUrl, + Set indexSet) + { + super(classloader, cache, factory, baseURL, absoluteUrl); + URL newBaseURL = null; + try + { + // Cache url prefix for all resources in this jar url. + String base = baseURL.toExternalForm() + "!/"; + newBaseURL = new URL("jar", "", -1, base, cache.get(factory, "jar")); + } + catch (MalformedURLException ignore) + { + // Ignore. + } + this.baseJarURL = newBaseURL; + this.classPath = null; + this.indexSet = indexSet; + } + + // This constructor is used by URLClassLoader. It will immediately + // try to read the jar file, in case we've found an index or a class-path + // setting. FIXME: it would be nice to defer this as well, but URLClassLoader + // makes this hard. public JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache, URLStreamHandlerFactory factory, URL baseURL, URL absoluteUrl) { - super(classloader, cache, factory, baseURL, absoluteUrl); + this(classloader, cache, factory, baseURL, absoluteUrl, null); + initialize(); + } - this.classPath = null; - URL baseJarURL = null; + private void initialize() + { JarFile jarfile = null; try { - // Cache url prefix for all resources in this jar url. - String base = baseURL.toExternalForm() + "!/"; - baseJarURL = new URL("jar", "", -1, base, cache.get(factory, "jar")); - jarfile = ((JarURLConnection) baseJarURL.openConnection()).getJarFile(); @@ -48,30 +87,66 @@ public final class JarURLLoader extends URLLoader Attributes attributes; String classPathString; - this.classPath = new Vector(); - - ArrayList indexListHeaders = new IndexListParser(jarfile, baseJarURL, baseURL).getHeaders(); - if (indexListHeaders.size() > 0) - this.classPath.addAll(indexListHeaders); + IndexListParser parser = new IndexListParser(jarfile, baseJarURL, + baseURL); + LinkedHashMap indexMap = parser.getHeaders(); + if (indexMap != null) + { + // Note that the index also computes + // the resulting Class-Path -- there are jars out there + // where the index lists some required jars which do + // not appear in the Class-Path attribute in the manifest. + this.classPath = new ArrayList(); + Iterator it = indexMap.entrySet().iterator(); + while (it.hasNext()) + { + LinkedHashMap.Entry entry = (LinkedHashMap.Entry) it.next(); + URL subURL = (URL) entry.getKey(); + Set prefixes = (Set) entry.getValue(); + if (subURL.equals(baseURL)) + this.indexSet = prefixes; + else + { + JarURLLoader subLoader = new JarURLLoader(classloader, + cache, + factory, subURL, + subURL, + prefixes); + // Note that we don't care if the sub-loader itself has an + // index or a class-path -- only the top-level jar + // file gets this treatment; its index should cover + // everything. + this.classPath.add(subLoader); + } + } + } else if ((manifest = jarfile.getManifest()) != null - && (attributes = manifest.getMainAttributes()) != null - && ((classPathString - = attributes.getValue(Attributes.Name.CLASS_PATH)) - != null)) - { + && (attributes = manifest.getMainAttributes()) != null + && ((classPathString + = attributes.getValue(Attributes.Name.CLASS_PATH)) + != null)) + { + this.classPath = new ArrayList(); StringTokenizer st = new StringTokenizer(classPathString, " "); while (st.hasMoreElements ()) - { - String e = st.nextToken (); - try - { - this.classPath.add(new URL(baseURL, e)); - } - catch (java.net.MalformedURLException xx) - { - // Give up - } - } + { + String e = st.nextToken (); + try + { + URL subURL = new URL(baseURL, e); + JarURLLoader subLoader = new JarURLLoader(classloader, + cache, factory, + subURL, subURL); + this.classPath.add(subLoader); + ArrayList extra = subLoader.getClassPath(); + if (extra != null) + this.classPath.addAll(extra); + } + catch (java.net.MalformedURLException xx) + { + // Give up + } + } } } catch (IOException ioe) @@ -79,18 +154,34 @@ public final class JarURLLoader extends URLLoader /* ignored */ } - this.baseJarURL = baseJarURL; this.jarfile = jarfile; + this.initialized = true; } /** get resource with the name "name" in the jar url */ public Resource getResource(String name) { - if (jarfile == null) - return null; - if (name.startsWith("/")) name = name.substring(1); + if (indexSet != null) + { + // Trust the index. + String basename = name; + int offset = basename.lastIndexOf('/'); + if (offset != -1) + basename = basename.substring(0, offset); + if (! indexSet.contains(basename)) + return null; + // FIXME: if the index claim to hold the resource, and jar file + // doesn't have it, we're supposed to throw an exception. However, + // in our model this is tricky to implement, as another URLLoader from + // the same top-level jar may have an overlapping index entry. + } + + if (! initialized) + initialize(); + if (jarfile == null) + return null; JarEntry je = jarfile.getJarEntry(name); if (je != null) @@ -111,7 +202,7 @@ public final class JarURLLoader extends URLLoader } } - public Vector getClassPath() + public ArrayList getClassPath() { return classPath; } diff --git a/gnu/java/net/loader/URLLoader.java b/gnu/java/net/loader/URLLoader.java index 1415f3f79..d073c5429 100644 --- a/gnu/java/net/loader/URLLoader.java +++ b/gnu/java/net/loader/URLLoader.java @@ -42,7 +42,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandlerFactory; import java.security.CodeSource; -import java.util.Vector; +import java.util.ArrayList; import java.util.jar.Manifest; /** @@ -136,7 +136,11 @@ public abstract class URLLoader return null; } - public Vector getClassPath() + /** + * Return a list of new URLLoader objects representing any + * class path entries added by this container. + */ + public ArrayList getClassPath() { return null; } diff --git a/java/net/URLClassLoader.java b/java/net/URLClassLoader.java index 34d8af067..403f7485c 100644 --- a/java/net/URLClassLoader.java +++ b/java/net/URLClassLoader.java @@ -61,9 +61,8 @@ import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.SecureClassLoader; import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; import java.util.Vector; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -131,14 +130,6 @@ public class URLClassLoader extends SecureClassLoader // Class Variables /** - * A global cache to store mappings between URLLoader and URL, - * so we can avoid do all the homework each time the same URL - * comes. - * XXX - Keeps these loaders forever which prevents garbage collection. - */ - private static HashMap urlloaders = new HashMap(); - - /** * A cache to store mappings between handler factory and its * private protocol handler cache (also a HashMap), so we can avoid * creating handlers each time the same protocol comes. @@ -299,132 +290,114 @@ public class URLClassLoader extends SecureClassLoader // Reset the toString() value. thisString = null; - // Check global cache to see if there're already url loader - // for this url. - URLLoader loader = (URLLoader) urlloaders.get(newUrl); - if (loader == null) - { - String file = newUrl.getFile(); - String protocol = newUrl.getProtocol(); - - // If we have a file: URL, we want to make it absolute - // here, before we decide whether it is really a jar. - URL absoluteURL; - if ("file".equals (protocol)) - { - File dir = new File(file); - URL absUrl; - try - { - absoluteURL = dir.getCanonicalFile().toURL(); - } - catch (IOException ignore) - { - try - { - absoluteURL = dir.getAbsoluteFile().toURL(); - } - catch (MalformedURLException _) - { - // This really should not happen. - absoluteURL = newUrl; - } - } - } - else - { - // This doesn't hurt, and it simplifies the logic a - // little. - absoluteURL = newUrl; - } + // Create a loader for this URL. + URLLoader loader = null; + String file = newUrl.getFile(); + String protocol = newUrl.getProtocol(); - // First see if we can find a handler with the correct name. + // If we have a file: URL, we want to make it absolute + // here, before we decide whether it is really a jar. + URL absoluteURL; + if ("file".equals (protocol)) + { + File dir = new File(file); + URL absUrl; try { - Class handler = Class.forName(URL_LOADER_PREFIX + protocol); - Class[] argTypes = new Class[] { URLClassLoader.class, - URLStreamHandlerCache.class, - URLStreamHandlerFactory.class, - URL.class, - URL.class }; - Constructor k = handler.getDeclaredConstructor(argTypes); - loader - = (URLLoader) k.newInstance(new Object[] { this, - factoryCache, - factory, - newUrl, - absoluteURL }); - } - catch (ClassNotFoundException ignore) - { - // Fall through. - } - catch (NoSuchMethodException nsme) - { - // Programming error in the class library. - InternalError vme - = new InternalError("couldn't find URLLoader constructor"); - vme.initCause(nsme); - throw vme; - } - catch (InstantiationException inste) - { - // Programming error in the class library. - InternalError vme - = new InternalError("couldn't instantiate URLLoader"); - vme.initCause(inste); - throw vme; - } - catch (InvocationTargetException ite) - { - // Programming error in the class library. - InternalError vme - = new InternalError("error instantiating URLLoader"); - vme.initCause(ite); - throw vme; + absoluteURL = dir.getCanonicalFile().toURL(); } - catch (IllegalAccessException illae) + catch (IOException ignore) { - // Programming error in the class library. - InternalError vme - = new InternalError("invalid access to URLLoader"); - vme.initCause(illae); - throw vme; - } - - if (loader == null) - { - // If it is not a directory, use the jar loader. - if (! (file.endsWith("/") || file.endsWith(File.separator))) - loader = new JarURLLoader(this, factoryCache, factory, - newUrl, absoluteURL); - else if ("file".equals(protocol)) - loader = new FileURLLoader(this, factoryCache, factory, - newUrl, absoluteURL); - else - loader = new RemoteURLLoader(this, factoryCache, factory, - newUrl); + try + { + absoluteURL = dir.getAbsoluteFile().toURL(); + } + catch (MalformedURLException _) + { + // This really should not happen. + absoluteURL = newUrl; + } } - - // Cache it. - urlloaders.put(newUrl, loader); + } + else + { + // This doesn't hurt, and it simplifies the logic a + // little. + absoluteURL = newUrl; } - urlinfos.add(loader); + // First see if we can find a handler with the correct name. + try + { + Class handler = Class.forName(URL_LOADER_PREFIX + protocol); + Class[] argTypes = new Class[] { URLClassLoader.class, + URLStreamHandlerCache.class, + URLStreamHandlerFactory.class, + URL.class, + URL.class }; + Constructor k = handler.getDeclaredConstructor(argTypes); + loader + = (URLLoader) k.newInstance(new Object[] { this, + factoryCache, + factory, + newUrl, + absoluteURL }); + } + catch (ClassNotFoundException ignore) + { + // Fall through. + } + catch (NoSuchMethodException nsme) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't find URLLoader constructor"); + vme.initCause(nsme); + throw vme; + } + catch (InstantiationException inste) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't instantiate URLLoader"); + vme.initCause(inste); + throw vme; + } + catch (InvocationTargetException ite) + { + // Programming error in the class library. + InternalError vme + = new InternalError("error instantiating URLLoader"); + vme.initCause(ite); + throw vme; + } + catch (IllegalAccessException illae) + { + // Programming error in the class library. + InternalError vme + = new InternalError("invalid access to URLLoader"); + vme.initCause(illae); + throw vme; + } - Vector extraUrls = loader.getClassPath(); - if (extraUrls != null) - { - Iterator it = extraUrls.iterator(); - while (it.hasNext()) - { - URL url = (URL)it.next(); - URLLoader extraLoader = (URLLoader) urlloaders.get(url); - if (! urlinfos.contains (extraLoader)) - addURLImpl(url); - } - } + if (loader == null) + { + // If it is not a directory, use the jar loader. + if (! (file.endsWith("/") || file.endsWith(File.separator))) + loader = new JarURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); + else if ("file".equals(protocol)) + loader = new FileURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); + else + loader = new RemoteURLLoader(this, factoryCache, factory, + newUrl); + } + urlinfos.add(loader); + ArrayList extra = loader.getClassPath(); + if (extra != null) + urlinfos.addAll(extra); } } |