aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2002-12-09 00:04:00 +0000
committerMark Wielaard <mark@klomp.org>2002-12-09 00:04:00 +0000
commit767c1d6b16fdcfd927c50a46b70dc0776525955b (patch)
tree6e19dd042b0e115509080d56c741014c959ca437
parentc88d1e688a7c7bf4ba527c2275af245e5118108e (diff)
* gnu/gcj/protocol/jar/Connection.java (getJarFile): download and
cache remote jar files. * gnu/gcj/runtime/VMClassLoader.java: Don't construct jar URL, only add File.separator to URL when it is a directory. * java/lang/ClassLoader.java: Add Classpath javadoc. (parent): final. (getParent): Add (disabled) security check. (findLibrary): New default method. * java/net/JarURLConnection.java (getManifest): Implement. (getInputStream): Only create InputStream when entry exists. (getHeaders): Only use jarFileURLConnection or JarEntry to set length when they exist. * java/net/URLClassLoader.java: New/Rewritten version from Classpath. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@59949 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--libjava/ChangeLog16
-rw-r--r--libjava/gnu/gcj/protocol/jar/Connection.java31
-rw-r--r--libjava/gnu/gcj/runtime/VMClassLoader.java21
-rw-r--r--libjava/java/lang/ClassLoader.java124
-rw-r--r--libjava/java/net/JarURLConnection.java20
-rw-r--r--libjava/java/net/URLClassLoader.java1184
6 files changed, 1065 insertions, 331 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog
index 9e255b2cd57..15ce27c6165 100644
--- a/libjava/ChangeLog
+++ b/libjava/ChangeLog
@@ -1,5 +1,21 @@
2002-12-08 Mark Wielaard <mark@klomp.org>
+ * gnu/gcj/protocol/jar/Connection.java (getJarFile): download and
+ cache remote jar files.
+ * gnu/gcj/runtime/VMClassLoader.java: Don't construct jar URL, only
+ add File.separator to URL when it is a directory.
+ * java/lang/ClassLoader.java: Add Classpath javadoc.
+ (parent): final.
+ (getParent): Add (disabled) security check.
+ (findLibrary): New default method.
+ * java/net/JarURLConnection.java (getManifest): Implement.
+ (getInputStream): Only create InputStream when entry exists.
+ (getHeaders): Only use jarFileURLConnection or JarEntry to set length
+ when they exist.
+ * java/net/URLClassLoader.java: New/Rewritten version from Classpath.
+
+2002-12-08 Mark Wielaard <mark@klomp.org>
+
* java/util/ResourceBundle.java (resourceBundleCache): Not final.
(lastDefaultLocale): New field.
(getBundle): When Locale.getDefault != lastDefaultLocale reset
diff --git a/libjava/gnu/gcj/protocol/jar/Connection.java b/libjava/gnu/gcj/protocol/jar/Connection.java
index 60641182f3a..c9db04997dc 100644
--- a/libjava/gnu/gcj/protocol/jar/Connection.java
+++ b/libjava/gnu/gcj/protocol/jar/Connection.java
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999 Free Software Foundation
+/* Copyright (C) 1999, 2002 Free Software Foundation
This file is part of libgcj.
@@ -9,19 +9,21 @@ details. */
package gnu.gcj.protocol.jar;
import java.net.URL;
+import java.net.URLConnection;
import java.net.JarURLConnection;
import java.net.URLStreamHandler;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.util.jar.JarFile;
+import java.util.zip.ZipFile;
import java.util.Hashtable;
/**
* Written using on-line Java Platform 1.2 API Specification.
- * Status: Needs a way to download jar files and store them in the local file
- * system. I don't know how to do that in a portable way. For now, it can only handle
- * connections to a jar:file: url's.
*
* @author Kresten Krab Thorup <krab@gnu.org>
* @date Aug 10, 1999.
@@ -70,14 +72,19 @@ public class Connection extends JarURLConnection
}
else
{
- /*
- FIXME: Here we need to download and cache the jar
- file in the local file system! Stupid design. Why
- can't we just create a JarFile from a bag of bytes?
- */
-
- throw new java.io.IOException("cannot create jar file from " +
- jarFileURL);
+ URLConnection urlconn = jarFileURL.openConnection();
+ InputStream is = urlconn.getInputStream();
+ byte[] buf = new byte[4*1024];
+ File f = File.createTempFile("cache", "jar");
+ FileOutputStream fos = new FileOutputStream(f);
+ int len = 0;
+ while((len = is.read(buf)) != -1)
+ fos.write(buf, 0, len);
+ fos.close();
+ // Always verify the Manifest, open read only and delete when done.
+ // XXX ZipFile.OPEN_DELETE not yet implemented.
+ // jf = new JarFile(f, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
+ jarfile = new JarFile(f, true, ZipFile.OPEN_READ);
}
return jarfile;
diff --git a/libjava/gnu/gcj/runtime/VMClassLoader.java b/libjava/gnu/gcj/runtime/VMClassLoader.java
index 77d21606621..fd0c32c2c57 100644
--- a/libjava/gnu/gcj/runtime/VMClassLoader.java
+++ b/libjava/gnu/gcj/runtime/VMClassLoader.java
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999, 2001 Free Software Foundation
+/* Copyright (C) 1999, 2001, 2002 Free Software Foundation
This file is part of libgcj.
@@ -33,23 +33,10 @@ public final class VMClassLoader extends java.net.URLClassLoader
String e = st.nextToken ();
try
{
- if (e.endsWith(".jar") || e.endsWith (".zip"))
- {
- File archive = new File (e);
- try {
- p.addElement(new URL("jar", "", -1, "file://"
- + archive.getCanonicalPath ()
- + "!/"));
- } catch (IOException ex) {
- // empty
- }
- }
- else if (e.endsWith ("/"))
- p.addElement (new URL("file", "", -1, e));
- else if (new File (e).isDirectory ())
- p.addElement (new URL("file", "", -1, e + "/"));
+ if (!e.endsWith (File.separator) && new File (e).isDirectory ())
+ p.addElement (new URL("file", "", -1, e + File.separator));
else
- /* Ignore path element. */;
+ p.addElement (new URL("file", "", -1, e));
}
catch (java.net.MalformedURLException x)
{
diff --git a/libjava/java/lang/ClassLoader.java b/libjava/java/lang/ClassLoader.java
index 1b3b310cab3..ea6546cf6ee 100644
--- a/libjava/java/lang/ClassLoader.java
+++ b/libjava/java/lang/ClassLoader.java
@@ -13,7 +13,6 @@ package java.lang;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
-import java.net.URLConnection;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
@@ -23,13 +22,68 @@ import java.security.ProtectionDomain;
import java.util.*;
/**
- * The class <code>ClassLoader</code> is intended to be subclassed by
- * applications in order to describe new ways of loading classes,
- * such as over the network.
+ * The ClassLoader is a way of customizing the way Java gets its classes
+ * and loads them into memory. The verifier and other standard Java things
+ * still run, but the ClassLoader is allowed great flexibility in determining
+ * where to get the classfiles and when to load and resolve them. For that
+ * matter, a custom ClassLoader can perform on-the-fly code generation or
+ * modification!
*
- * @author Kresten Krab Thorup
+ * <p>Every classloader has a parent classloader that is consulted before
+ * the 'child' classloader when classes or resources should be loaded.
+ * This is done to make sure that classes can be loaded from an hierarchy of
+ * multiple classloaders and classloaders do not accidentially redefine
+ * already loaded classes by classloaders higher in the hierarchy.
+ *
+ * <p>The grandparent of all classloaders is the bootstrap classloader, which
+ * loads all the standard system classes as implemented by GNU Classpath. The
+ * other special classloader is the system classloader (also called
+ * application classloader) that loads all classes from the CLASSPATH
+ * (<code>java.class.path</code> system property). The system classloader
+ * is responsible for finding the application classes from the classpath,
+ * and delegates all requests for the standard library classes to its parent
+ * the bootstrap classloader. Most programs will load all their classes
+ * through the system classloaders.
+ *
+ * <p>The bootstrap classloader in GNU Classpath is implemented as a couple of
+ * static (native) methods on the package private class
+ * <code>java.lang.VMClassLoader</code>, the system classloader is an
+ * instance of <code>gnu.java.lang.SystemClassLoader</code>
+ * (which is a subclass of <code>java.net.URLClassLoader</code>).
+ *
+ * <p>Users of a <code>ClassLoader</code> will normally just use the methods
+ * <ul>
+ * <li> <code>loadClass()</code> to load a class.</li>
+ * <li> <code>getResource()</code> or <code>getResourceAsStream()</code>
+ * to access a resource.</li>
+ * <li> <code>getResources()</code> to get an Enumeration of URLs to all
+ * the resources provided by the classloader and its parents with the
+ * same name.</li>
+ * </ul>
+ *
+ * <p>Subclasses should implement the methods
+ * <ul>
+ * <li> <code>findClass()</code> which is called by <code>loadClass()</code>
+ * when the parent classloader cannot provide a named class.</li>
+ * <li> <code>findResource()</code> which is called by
+ * <code>getResource()</code> when the parent classloader cannot provide
+ * a named resource.</li>
+ * <li> <code>findResources()</code> which is called by
+ * <code>getResource()</code> to combine all the resources with the
+ * same name from the classloader and its parents.</li>
+ * <li> <code>findLibrary()</code> which is called by
+ * <code>Runtime.loadLibrary()</code> when a class defined by the
+ * classloader wants to load a native library.</li>
+ * </ul>
+ *
+ * @author John Keiser
+ * @author Mark Wielaard
+ * @author Eric Blake
+ * @author Kresten Krab Thorup
+ * @see Class
+ * @since 1.0
+ * @status still missing 1.4 functionality
*/
-
public abstract class ClassLoader
{
/**
@@ -73,12 +127,40 @@ public abstract class ClassLoader
// Package visible for use by Class.
Map classAssertionStatus;
- private ClassLoader parent;
+ /**
+ * The classloader that is consulted before this classloader.
+ * If null then the parent is the bootstrap classloader.
+ */
+ private final ClassLoader parent;
+
+ /**
+ * All packages defined by this classloader. It is not private in order to
+ * allow native code (and trusted subclasses) access to this field.
+ */
private HashMap definedPackages = new HashMap();
+ /**
+ * Returns the parent of this classloader. If the parent of this
+ * classloader is the bootstrap classloader then this method returns
+ * <code>null</code>. A security check may be performed on
+ * <code>RuntimePermission("getClassLoader")</code>.
+ *
+ * @throws SecurityException if the security check fails
+ * @since 1.2
+ */
public final ClassLoader getParent ()
{
- /* FIXME: security */
+ // Check if we may return the parent classloader
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ /* FIXME: security, getClassContext() not implemented.
+ Class c = VMSecurityManager.getClassContext()[1];
+ ClassLoader cl = c.getClassLoader();
+ if (cl != null && cl != this)
+ sm.checkPermission(new RuntimePermission("getClassLoader"));
+ */
+ }
return parent;
}
@@ -449,7 +531,8 @@ public abstract class ClassLoader
else
{
InternalError e
- = new InternalError ("unexpected exception during linking");
+ = new InternalError ("unexpected exception during linking: "
+ + clazz.getName());
e.initCause (x);
throw e;
}
@@ -521,6 +604,8 @@ public abstract class ClassLoader
* null when the package is not defined by this classloader or one of its
* parents.
*
+ * @param name the package name to find
+ * @return the package, if defined
* @since 1.2
*/
protected Package getPackage(String name)
@@ -546,6 +631,7 @@ public abstract class ClassLoader
/**
* Returns all Package objects defined by this classloader and its parents.
*
+ * @return an array of all defined packages
* @since 1.2
*/
protected Package[] getPackages()
@@ -577,6 +663,26 @@ public abstract class ClassLoader
return allPackages;
}
+ /**
+ * Called by <code>Runtime.loadLibrary()</code> to get an absolute path
+ * to a (system specific) library that was requested by a class loaded
+ * by this classloader. The default implementation returns
+ * <code>null</code>. It should be implemented by subclasses when they
+ * have a way to find the absolute path to a library. If this method
+ * returns null the library is searched for in the default locations
+ * (the directories listed in the <code>java.library.path</code> system
+ * property).
+ *
+ * @param name the (system specific) name of the requested library
+ * @return the full pathname to the requested library, or null
+ * @see Runtime#loadLibrary()
+ * @since 1.2
+ */
+ protected String findLibrary(String name)
+ {
+ return null;
+ }
+
/**
* Returns a class found in a system-specific way, typically
* via the <code>java.class.path</code> system property. Loads the
diff --git a/libjava/java/net/JarURLConnection.java b/libjava/java/net/JarURLConnection.java
index a90c7f33037..6f4cd1fe7ea 100644
--- a/libjava/java/net/JarURLConnection.java
+++ b/libjava/java/net/JarURLConnection.java
@@ -134,7 +134,11 @@ public abstract class JarURLConnection extends URLConnection
if (jarfile != null)
{
// this is the easy way...
- return jarfile.getInputStream (jarfile.getEntry (element));
+ ZipEntry entry = jarfile.getEntry(element);
+ if (entry != null)
+ return jarfile.getInputStream (entry);
+ else
+ return null;
}
else
{
@@ -320,12 +324,17 @@ public abstract class JarURLConnection extends URLConnection
// to add others later and for consistency, we'll implement it this way.
// Add the only header we know about right now: Content-length.
- long len;
+ long len = -1;
if (element == null)
- len = jarFileURLConnection.getContentLength ();
+ if (jarFileURLConnection != null)
+ len = jarFileURLConnection.getContentLength ();
else
- len = getJarEntry ().getSize ();
+ {
+ JarEntry entry = getJarEntry();
+ if (entry != null)
+ len = entry.getSize ();
+ }
String line = "Content-length: " + len;
hdrVec.addElement(line);
@@ -381,7 +390,6 @@ public abstract class JarURLConnection extends URLConnection
{
JarFile file = getJarFile ();
- // FIXME: implement this
- return null;
+ return (file != null) ? file.getManifest() : null;
}
}
diff --git a/libjava/java/net/URLClassLoader.java b/libjava/java/net/URLClassLoader.java
index 5e059cf9cb6..e37a81a5021 100644
--- a/libjava/java/net/URLClassLoader.java
+++ b/libjava/java/net/URLClassLoader.java
@@ -1,332 +1,701 @@
-/* Copyright (C) 1999, 2000, 2002 Free Software Foundation
+/* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs
+ Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
- This file is part of libgcj.
+This file is part of GNU Classpath.
-This software is copyrighted work licensed under the terms of the
-Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
-details. */
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
package java.net;
-import java.io.*;
-import java.util.jar.*;
-import java.util.Enumeration;
-import java.util.Vector;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.FilePermission;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.AccessControlContext;
import java.security.CodeSource;
import java.security.SecureClassLoader;
+import java.security.PrivilegedAction;
import java.security.PermissionCollection;
import java.security.cert.Certificate;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.HashMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipException;
/**
+ * A secure class loader that can load classes and resources from
+ * multiple locations. Given an array of <code>URL</code>s this class
+ * loader will retrieve classes and resources by fetching them from
+ * possible remote locations. Each <code>URL</code> is searched in
+ * order in which it was added. If the file portion of the
+ * <code>URL</code> ends with a '/' character then it is interpreted
+ * as a base directory, otherwise it is interpreted as a jar file from
+ * which the classes/resources are resolved.
+ *
+ * <p>New instances can be created by two static
+ * <code>newInstance()</code> methods or by three public
+ * contructors. Both ways give the option to supply an initial array
+ * of <code>URL</code>s and (optionally) a parent classloader (that is
+ * different from the standard system class loader).</p>
+ *
+ * <p>Normally creating a <code>URLClassLoader</code> throws a
+ * <code>SecurityException</code> if a <code>SecurityManager</code> is
+ * installed and the <code>checkCreateClassLoader()</code> method does
+ * not return true. But the <code>newInstance()</code> methods may be
+ * used by any code as long as it has permission to acces the given
+ * <code>URL</code>s. <code>URLClassLoaders</code> created by the
+ * <code>newInstance()</code> methods also explicitly call the
+ * <code>checkPackageAccess()</code> method of
+ * <code>SecurityManager</code> if one is installed before trying to
+ * load a class. Note that only subclasses of
+ * <code>URLClassLoader</code> can add new URLs after the
+ * URLClassLoader had been created. But it is always possible to get
+ * an array of all URLs that the class loader uses to resolve classes
+ * and resources by way of the <code>getURLs()</code> method.</p>
+ *
+ * <p>Open issues:
+ * <ul>
+ *
+ * <li>Should the URLClassLoader actually add the locations found in
+ * the manifest or is this the responsibility of some other
+ * loader/(sub)class? (see <a
+ * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
+ * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
+ *
+ * <li>How does <code>definePackage()</code> and sealing work
+ * precisely?</li>
+ *
+ * <li>We save and use the security context (when a created by
+ * <code>newInstance()</code> but do we have to use it in more
+ * places?</li>
+ *
+ * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
+ *
+ * </ul>
+ * </p>
+ *
* @since 1.2
+ *
+ * @author Mark Wielaard (mark@klomp.org)
+ * @author Wu Gansha (gansha.wu@intel.com)
*/
+
public class URLClassLoader extends SecureClassLoader
{
- // The URLStreamHandlerFactory
- URLStreamHandlerFactory factory = null;
+ // Class Variables
- // `path' contains simply the URL's we're using for the searching.
- private Vector path;
+ /**
+ * 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
+ * create handlers each time the same protocol comes.
+ */
+ private static HashMap factoryCache = new HashMap(5);
- // If path[n] is a zip/jar, then this holds a JarURLConnection for
- // that thing, otherwise, path[n] is null.
- private Vector info;
+ // Instance variables
- private URLStreamHandler getHandler0 (String protocol)
- {
- if (factory != null)
- return factory.createURLStreamHandler(protocol);
- else
- return null;
- }
+ /** Locations to load classes from */
+ private final Vector urls = new Vector();
/**
- * Createa a new URL class loader object
- *
- * @exception SecurityException If a security manager exists and its
- * checkCreateClassLoader method doesn't allow creation of a class loader
+ * Store pre-parsed information for each url into this vector
+ * each element is a URL loader, corresponding to the URL of
+ * the same index in "urls"
*/
- public URLClassLoader (URL[] urls)
- {
- this (urls, null, null);
- }
-
+ private final Vector urlinfos = new Vector();
+
+ /** Factory used to get the protocol handlers of the URLs */
+ private final URLStreamHandlerFactory factory;
+
/**
- * Createa a new URL class loader object
- *
- * @exception SecurityException If a security manager exists and its
- * checkCreateClassLoader method doesn't allow creation of a class loader
+ * The security context when created from <code>newInstance()</code>
+ * or null when created through a normal constructor or when no
+ * <code>SecurityManager</code> was installed.
*/
- public URLClassLoader (URL[] urls, ClassLoader parent)
- {
- this (urls, parent, null);
- }
+ private final AccessControlContext securityContext;
- // A File URL may actually be a Jar URL. Convert if possible.
- private URL jarFileize (URL url)
+ // Helper classes
+
+ /**
+ * A <code>URLLoader</code> contains all logic to load resources from a
+ * given base <code>URL</code>.
+ */
+ static abstract class URLLoader
{
- if (! url.getProtocol ().equals ("jar"))
- {
- String f = url.getFile ();
+ /**
+ * Our classloader to get info from if needed.
+ */
+ final URLClassLoader classloader;
- // If it ends with '/' we'll take it for a directory,
- // otherwise it's a jar file. This is how JDK 1.2 defines
- // it, so we will not try to be smart here.
- if (f.charAt (f.length ()-1) != '/')
- {
- try
- {
- url = new URL ("jar", "", -1, (url.toExternalForm ())+"!/",
- getHandler0 ("jar"));
- }
- catch (MalformedURLException x)
- {
- /* ignore */
- }
- }
- }
- return url;
- }
+ /**
+ * The base URL from which all resources are loaded.
+ */
+ final URL baseURL;
- protected void addURL (URL url)
- {
- JarURLConnection conn = null;
+ /**
+ * A <code>CodeSource</code> without any associated certificates.
+ * It is common for classes to not have certificates associated
+ * with them. If they come from the same <code>URLLoader</code>
+ * then it is safe to share the associated <code>CodeSource</code>
+ * between them since <code>CodeSource</code> is immutable.
+ */
+ final CodeSource noCertCodeSource;
+
+ URLLoader(URLClassLoader classloader, URL baseURL)
+ {
+ this.classloader = classloader;
+ this.baseURL = baseURL;
+ this.noCertCodeSource = new CodeSource(baseURL, null);
+ }
+
+ /**
+ * Returns a <code>Resource</code> loaded by this
+ * <code>URLLoader</code>, or <code>null</code> when no
+ * <code>Resource</code> with the given name exists.
+ */
+ abstract Resource getResource(String s);
+
+ /**
+ * Returns the <code>Manifest</code> associated with the
+ * <code>Resource</code>s loaded by this <code>URLLoader</code> or
+ * <code>null</code> there is no such <code>Manifest</code>.
+ */
+ Manifest getManifest()
+ {
+ return null;
+ }
+ }
- // Convert a Jar File URL into Jar URL if possible.
- url = jarFileize (url);
+ /**
+ * A <code>Resource</code> represents a resource in some
+ * <code>URLLoader</code>. It also contains all information (e.g.,
+ * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and
+ * <code>InputStream</code>) that is necessary for loading resources
+ * and creating classes from a <code>URL</code>.
+ */
+ static abstract class Resource
+ {
+ final URLLoader loader;
+ final String name;
- path.addElement (url);
+ Resource(URLLoader loader, String name)
+ {
+ this.loader = loader;
+ this.name = name;
+ }
- if (url.getProtocol ().equals ("jar"))
- {
- try
- {
- conn = (JarURLConnection) url.openConnection ();
- }
- catch (java.io.IOException x)
- {
- /* ignore */
- }
- }
+ /**
+ * Returns the non-null <code>CodeSource</code> associated with
+ * this resource.
+ */
+ CodeSource getCodeSource()
+ {
+ Certificate[] certs = getCertificates();
+ if (certs != null)
+ return loader.noCertCodeSource;
+ else
+ return new CodeSource(loader.baseURL, certs);
+ }
+
+ /**
+ * Returns <code>Certificates</code> associated with this
+ * resource, or null when there are none.
+ */
+ Certificate[] getCertificates()
+ {
+ return null;
+ }
+
+ /**
+ * Return a <code>URL</code> that can be used to access this resource.
+ */
+ abstract URL getURL();
- info.addElement (conn);
+ /**
+ * Returns the size of this <code>Resource</code> in bytes or
+ * <code>-1</code> when unknown.
+ */
+ abstract int getLength();
+
+ /**
+ * Returns the non-null <code>InputStream</code> through which
+ * this resource can be loaded.
+ */
+ abstract InputStream getInputStream() throws IOException;
}
/**
- * Createa a new URL class loader object
- *
- * @exception SecurityException If a security manager exists and its
- * checkCreateClassLoader method doesn't allow creation of a class loader
+ * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
+ * only loading from jar url.
*/
- public URLClassLoader (URL[] urls, ClassLoader parent,
- URLStreamHandlerFactory fac)
- {
- super (parent);
-
- SecurityManager s = System.getSecurityManager();
- if (s != null)
- s.checkCreateClassLoader();
-
- factory = fac;
+ final static class JarURLLoader extends URLLoader
+ {
+ final JarFile jarfile; // The canonical jar file for this url
+ final URL baseJarURL; // Base jar: url for all resources loaded from jar
- if (urls == null || urls.length == 0)
- {
- path = new Vector (1);
- info = new Vector (1);
- return;
- }
+ public JarURLLoader(URLClassLoader classloader, URL baseURL)
+ {
+ super(classloader, baseURL);
+
+ // cache url prefix for all resources in this jar url
+ String external = baseURL.toExternalForm();
+ StringBuffer sb = new StringBuffer(external.length() + 6);
+ sb.append("jar:");
+ sb.append(external);
+ sb.append("!/");
+ String jarURL = sb.toString();
- path = new Vector (urls.length);
- info = new Vector (urls.length);
+ URL baseJarURL = null;
+ JarFile jarfile = null;
+ try
+ {
+ baseJarURL
+ = new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
+ jarfile
+ = ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
+ }
+ catch (IOException ioe) { /* ignored */ }
- for (int i = 0; i < urls.length; i++)
- {
- // Convert a Jar File URL into a Jar URL if possible.
- URL u = jarFileize(urls[i]);
+ this.baseJarURL = baseJarURL;
+ this.jarfile = jarfile;
+ }
+
+ /** get resource with the name "name" in the jar url */
+ Resource getResource(String name)
+ {
+ if (jarfile == null)
+ return null;
- path.addElement (u);
+ JarEntry je = jarfile.getJarEntry(name);
+ if(je != null)
+ return new JarURLResource(this, name, je);
+ else
+ return null;
+ }
- if (u.getProtocol ().equals ("jar"))
- {
- JarURLConnection conn = null;
- try
- {
- conn = (JarURLConnection) u.openConnection ();
- }
- catch (java.io.IOException x)
- {
- /* ignore */
- }
- info.addElement (conn);
- }
- else
- {
- info.addElement (null);
- }
- }
+ Manifest getManifest()
+ {
+ try
+ {
+ return (jarfile == null) ? null : jarfile.getManifest();
+ }
+ catch (IOException ioe)
+ {
+ return null;
+ }
+ }
+
}
- public URL[] getURLs ()
+ final static class JarURLResource extends Resource
{
- URL[] urls = new URL[path.size()];
- path.copyInto (urls);
- return urls;
+ private final JarEntry entry;
+
+ JarURLResource(JarURLLoader loader, String name, JarEntry entry)
+ {
+ super(loader, name);
+ this.entry = entry;
+ }
+
+ InputStream getInputStream() throws IOException
+ {
+ return ((JarURLLoader)loader).jarfile.getInputStream(entry);
+ }
+
+ int getLength()
+ {
+ return (int)entry.getSize();
+ }
+
+ Certificate[] getCertificates()
+ {
+ return entry.getCertificates();
+ }
+
+ URL getURL()
+ {
+ try
+ {
+ return new URL(((JarURLLoader)loader).baseJarURL, name,
+ loader.classloader.getURLStreamHandler("jar"));
+ }
+ catch(MalformedURLException e)
+ {
+ throw new InternalError(e.toString());
+ }
+ }
}
-
+
/**
- * Returns an Enumeration of URLs representing all of the resources on the
- * URL search path having the specified name
- *
- * @exception IOException If an error occurs
+ * Loader for remote directories.
*/
- public Enumeration findResources (String name)
+ final static class RemoteURLLoader extends URLLoader
{
- Vector results = new Vector ();
+ final private String protocol;
- for (int i = 0; i < path.size(); i++)
- {
- URL u = (URL)path.elementAt (i);
-
- try {
- JarURLConnection conn = (JarURLConnection) info.elementAt (i);
-
- if (conn != null)
+ RemoteURLLoader(URLClassLoader classloader, URL url)
+ {
+ super(classloader, url);
+ protocol = url.getProtocol();
+ }
+
+ /**
+ * Get a remote resource.
+ * Returns null if no such resource exists.
+ */
+ Resource getResource(String name)
+ {
+ try
+ {
+ URL url = new URL(baseURL, name,
+ classloader.getURLStreamHandler(protocol));
+ URLConnection connection = url.openConnection();
+
+ // Open the connection and check the stream
+ // just to be sure it exists.
+ int length = connection.getContentLength();
+ InputStream stream = connection.getInputStream();
+
+ // We can do some extra checking if it is a http request
+ if (connection instanceof HttpURLConnection)
{
- if (conn.getJarFile().getJarEntry (name) != null)
- results.addElement (new URL(u, name,
- getHandler0 (u.getProtocol())));
+ int response
+ = ((HttpURLConnection)connection).getResponseCode();
+ if (response/100 != 2)
+ return null;
}
+
+ if (stream != null)
+ return new RemoteResource(this, name, url, stream, length);
else
- {
- URL p = new URL (u, name, getHandler0 (u.getProtocol()));
-
- InputStream is = p.openStream();
- if (is != null)
- {
- is.close();
- results.addElement (p);
- }
- }
-
- // if we get an exception ... try the next path element
- } catch (IOException x) {
- continue;
+ return null;
}
- }
-
- return results.elements ();
+ catch (IOException ioe)
+ {
+ return null;
+ }
+ }
}
- public URL findResource (String name)
+ /**
+ * A resource from some remote location.
+ */
+ final static class RemoteResource extends Resource
{
- for (int i = 0; i < path.size(); i++)
- {
- URL u = (URL)path.elementAt (i);
-
- try {
- JarURLConnection conn = (JarURLConnection) info.elementAt (i);
-
- if (conn != null)
- {
- if (conn.getJarFile().getJarEntry (name) != null)
- return new URL(u, name, getHandler0 (u.getProtocol()));
- }
- else
- {
- URL p = new URL (u, name, getHandler0 (u.getProtocol()));
+ final private URL url;
+ final private InputStream stream;
+ final private int length;
- InputStream is = p.openStream();
- if (is != null)
- {
- is.close();
- return p;
- }
- }
-
- // if we get an exception ... try the next path element
- } catch (IOException x) {
- continue;
+ RemoteResource(RemoteURLLoader loader, String name, URL url,
+ InputStream stream, int length)
+ {
+ super(loader, name);
+ this.url = url;
+ this.stream = stream;
+ this.length = length;
+ }
+
+ InputStream getInputStream() throws IOException
+ {
+ return stream;
+ }
+
+ public int getLength()
+ {
+ return length;
+ }
+
+ public URL getURL()
+ {
+ return url;
+ }
+ }
+
+ /**
+ * A <code>FileURLLoader</code> is a type of <code>URLLoader</code>
+ * only loading from file url.
+ */
+ final static class FileURLLoader extends URLLoader
+ {
+ File dir; //the canonical file for this file url
+
+ FileURLLoader(URLClassLoader classloader, URL url)
+ {
+ super(classloader, url);
+ // Note that this must be a "file" protocol URL.
+ dir = new File(url.getFile());
+ }
+
+ /** get resource with the name "name" in the file url */
+ Resource getResource(String name)
+ {
+ File file = new File(dir, name);
+ if (file.exists() && !file.isDirectory())
+ return new FileResource(this, name, file);
+ else
+ return null;
+ }
+ }
+
+ final static class FileResource extends Resource
+ {
+ final File file;
+
+ FileResource(FileURLLoader loader, String name, File file)
+ {
+ super(loader, name);
+ this.file = file;
+ }
+
+ InputStream getInputStream() throws IOException
+ {
+ return new FileInputStream(file);
+ }
+
+ public int getLength()
+ {
+ return (int)file.length();
+ }
+
+ public URL getURL()
+ {
+ try
+ {
+ return new URL(loader.baseURL, name,
+ loader.classloader.getURLStreamHandler("file"));
}
- }
+ catch(MalformedURLException e)
+ {
+ throw new InternalError(e.toString());
+ }
+ }
+ }
+
+ // Constructors
- return null;
+ /**
+ * Creates a URLClassLoader that gets classes from the supplied URLs.
+ * To determine if this classloader may be created the constructor of
+ * the super class (<code>SecureClassLoader</code>) is called first, which
+ * can throw a SecurityException. Then the supplied URLs are added
+ * in the order given to the URLClassLoader which uses these URLs to
+ * load classes and resources (after using the default parent ClassLoader).
+ *
+ * @exception SecurityException if the SecurityManager disallows the
+ * creation of a ClassLoader.
+ * @param urls Locations that should be searched by this ClassLoader when
+ * resolving Classes or Resources.
+ * @see SecureClassLoader
+ */
+ public URLClassLoader(URL[] urls) throws SecurityException
+ {
+ super();
+ this.factory = null;
+ this.securityContext = null;
+ addURLs(urls);
}
/**
- * Finds and loads the class with the specified name from the
- * URL search path
+ * Private constructor used by the static
+ * <code>newInstance(URL[])</code> method. Creates an
+ * <code>URLClassLoader</code> without any <code>URL</code>s
+ * yet. This is used to bypass the normal security check for
+ * creating classloaders, but remembers the security context which
+ * will be used when defining classes. The <code>URL</code>s to
+ * load from must be added by the <code>newInstance()</code> method
+ * in the security context of the caller.
*
- * @exception ClassNotFoundException If the class could not be found
+ * @param securityContext the security context of the unprivileged code.
*/
- protected Class findClass (String name)
- throws ClassNotFoundException
+ private URLClassLoader(AccessControlContext securityContext)
{
- if (name == null)
- throw new ClassNotFoundException ("null");
+ super();
+ this.factory = null;
+ this.securityContext = securityContext;
+ }
- try
- {
- URL url = getResource (name.replace ('.', '/') + ".class");
+ /**
+ * Creates a <code>URLClassLoader</code> that gets classes from the supplied
+ * <code>URL</code>s.
+ * To determine if this classloader may be created the constructor of
+ * the super class (<code>SecureClassLoader</code>) is called first, which
+ * can throw a SecurityException. Then the supplied URLs are added
+ * in the order given to the URLClassLoader which uses these URLs to
+ * load classes and resources (after using the supplied parent ClassLoader).
+ * @exception SecurityException if the SecurityManager disallows the
+ * creation of a ClassLoader.
+ * @exception SecurityException
+ * @param urls Locations that should be searched by this ClassLoader when
+ * resolving Classes or Resources.
+ * @param parent The parent class loader used before trying this class
+ * loader.
+ * @see SecureClassLoader
+ */
+ public URLClassLoader(URL[] urls, ClassLoader parent)
+ throws SecurityException
+ {
+ super(parent);
+ this.factory = null;
+ this.securityContext = null;
+ addURLs(urls);
+ }
- if (url == null)
- throw new ClassNotFoundException (name);
+ /**
+ * Private constructor used by the static
+ * <code>newInstance(URL[])</code> method. Creates an
+ * <code>URLClassLoader</code> with the given parent but without any
+ * <code>URL</code>s yet. This is used to bypass the normal security
+ * check for creating classloaders, but remembers the security
+ * context which will be used when defining classes. The
+ * <code>URL</code>s to load from must be added by the
+ * <code>newInstance()</code> method in the security context of the
+ * caller.
+ *
+ * @param securityContext the security context of the unprivileged code.
+ */
+ private URLClassLoader(ClassLoader parent,
+ AccessControlContext securityContext)
+ {
+ super(parent);
+ this.factory = null;
+ this.securityContext = securityContext;
+ }
- URLConnection connection = url.openConnection ();
- InputStream is = connection.getInputStream ();
+ /**
+ * Creates a URLClassLoader that gets classes from the supplied URLs.
+ * To determine if this classloader may be created the constructor of
+ * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
+ * can throw a SecurityException. Then the supplied URLs are added
+ * in the order given to the URLClassLoader which uses these URLs to
+ * load classes and resources (after using the supplied parent ClassLoader).
+ * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
+ * protocol handlers of the supplied URLs.
+ * @exception SecurityException if the SecurityManager disallows the
+ * creation of a ClassLoader.
+ * @exception SecurityException
+ * @param urls Locations that should be searched by this ClassLoader when
+ * resolving Classes or Resources.
+ * @param parent The parent class loader used before trying this class
+ * loader.
+ * @param factory Used to get the protocol handler for the URLs.
+ * @see SecureClassLoader
+ */
+ public URLClassLoader(URL[] urls,
+ ClassLoader parent,
+ URLStreamHandlerFactory factory)
+ throws SecurityException
+ {
+ super(parent);
+ this.securityContext = null;
+ this.factory = factory;
+ addURLs(urls);
- int len = connection.getContentLength ();
- byte[] data = new byte[len];
+ // If this factory is still not in factoryCache, add it,
+ // since we only support three protocols so far, 5 is enough
+ // for cache initial size
+ synchronized(factoryCache)
+ {
+ if(factory != null && factoryCache.get(factory) == null)
+ factoryCache.put(factory, new HashMap(5));
+ }
+ }
- int left = len;
- int off = 0;
- while (left > 0)
- {
- int c = is.read (data, off, len-off);
- if (c == -1 || c == 0)
- throw new InternalError ("premature end of file");
- left -= c;
- off += c;
- }
+ // Methods
- // Now construct the CodeSource (if loaded from a jar file)
- CodeSource source = null;
- if (url.getProtocol().equals("jar"))
- {
- Certificate[] certificates =
- ((JarURLConnection) connection).getCertificates();
- String u = url.toExternalForm ();
- u = u.substring (4); //skip "jar:"
- int i = u.indexOf ('!');
- if (i >= 0)
- u = u.substring (0, i);
- url = new URL(u);
-
- source = new CodeSource(url, certificates);
- }
- else if (url.getProtocol().equals("file"))
+ /**
+ * Adds a new location to the end of the internal URL store.
+ * @param newUrl the location to add
+ */
+ protected void addURL(URL newUrl)
+ {
+ synchronized(urlloaders)
+ {
+ if (newUrl == null)
+ return; // Silently ignore...
+
+ // check global cache to see if there're already url loader
+ // for this url
+ URLLoader loader = (URLLoader)urlloaders.get(newUrl);
+ if (loader == null)
{
- try
- {
- String u = url.toExternalForm();
- // Skip "file:" and then get canonical directory name.
- File f = new File(u.substring(5));
- f = f.getCanonicalFile();
- url = new URL("file", "", f.getParent());
- source = new CodeSource (url, null);
- }
- catch (IOException ignore)
- {
- }
+ String file = newUrl.getFile();
+ // Check that it is not a directory
+ if (!(file.endsWith("/") || file.endsWith(File.separator)))
+ loader = new JarURLLoader(this, newUrl);
+ else // it's a url that point to a jar file
+ if ("file".equals(newUrl.getProtocol()))
+ loader = new FileURLLoader(this, newUrl);
+ else
+ loader = new RemoteURLLoader(this, newUrl);
+
+ // cache it
+ urlloaders.put(newUrl, loader);
}
- return defineClass (name, data, 0, len, source);
- }
- catch (java.io.IOException x)
- {
- throw new ClassNotFoundException(name);
+ urls.add(newUrl);
+ urlinfos.add(loader);
}
}
+ /**
+ * Adds an array of new locations to the end of the internal URL store.
+ * @param newUrls the locations to add
+ */
+ private void addURLs(URL[] newUrls)
+ {
+ for (int i = 0; i < newUrls.length; i++)
+ {
+ addURL(newUrls[i]);
+ }
+ }
+
/**
* Defines a Package based on the given name and the supplied manifest
* information. The manifest indicates the tile, version and
@@ -362,36 +731,231 @@ public class URLClassLoader extends SecureClassLoader
// Look if the Manifest indicates that this package is sealed
// XXX - most likely not completely correct!
// Shouldn't we also check the sealed attribute of the complete jar?
- // http://java.sun.com/products/jdk/1.3/docs/guide/extensions/spec.html#bundled
+ // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
// But how do we get that jar manifest here?
String sealed = attr.getValue(Attributes.Name.SEALED);
if ("false".equals(sealed))
- {
- // Make sure that the URL is null so the package is not
- // sealed.
- url = null;
- }
+ {
+ // make sure that the URL is null so the package is not sealed
+ url = null;
+ }
return definePackage(name, specTitle, specVersion, specVendor,
implTitle, implVersion, implVendor, url);
}
/**
- * Returns the permissions needed to access a particular code source.
- * These permissions includes those returned by
- * <CODE>SecureClassLoader.getPermissions</CODE> and the actual permissions
- * to access the objects referenced by the URL of the code source.
- * The extra permissions added depend on the protocol and file portion of
- * the URL in the code source. If the URL has the "file" protocol ends with
- * a / character then it must be a directory and a file Permission to read
- * everthing in that directory and all subdirectories is added. If the URL
- * had the "file" protocol and doesn't end with a / character then it must
- * be a normal file and a file permission to read that file is added. If the
- * URL has any other protocol then a socket permission to connect and accept
- * connections from the host portion of the URL is added.
+ * Finds (the first) class by name from one of the locations. The locations
+ * are searched in the order they were added to the URLClassLoader.
+ *
+ * @param className the classname to find
+ * @exception ClassNotFoundException when the class could not be found or
+ * loaded
+ * @return a Class object representing the found class
+ */
+ protected Class findClass(final String className)
+ throws ClassNotFoundException
+ {
+ // Just try to find the resource by the (almost) same name
+ String resourceName = className.replace('.', '/') + ".class";
+ Resource resource = findURLResource(resourceName);
+ if (resource == null)
+ throw new ClassNotFoundException(className + " not found in " + urls);
+
+ // Try to read the class data, create the CodeSource, Package and
+ // construct the class (and watch out for those nasty IOExceptions)
+ try
+ {
+ byte [] data;
+ InputStream in = resource.getInputStream();
+ int length = resource.getLength();
+ if (length != -1)
+ {
+ // We know the length of the data.
+ // Just try to read it in all at once
+ data = new byte[length];
+ int pos = 0;
+ while(length - pos > 0)
+ {
+ int len = in.read(data, pos, length - pos);
+ if (len == -1)
+ throw new EOFException("Not enough data reading from: "
+ + in);
+ pos += len;
+ }
+ }
+ else
+ {
+ // We don't know the data length.
+ // Have to read it in chunks.
+ ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
+ byte b[] = new byte[4096];
+ int l = 0;
+ while (l != -1)
+ {
+ l = in.read(b);
+ if (l != -1)
+ out.write(b, 0, l);
+ }
+ data = out.toByteArray();
+ }
+ final byte[] classData = data;
+
+ // Now get the CodeSource
+ final CodeSource source = resource.getCodeSource();
+
+ // Find out package name
+ String packageName = null;
+ int lastDot = className.lastIndexOf('.');
+ if (lastDot != -1)
+ packageName = className.substring(0, lastDot);
+
+ if (packageName != null && getPackage(packageName) == null)
+ {
+ // define the package
+ Manifest manifest = resource.loader.getManifest();
+ if (manifest == null)
+ definePackage(packageName,
+ null, null, null, null, null, null, null);
+ else
+ definePackage(packageName, manifest, resource.loader.baseURL);
+ }
+
+ // And finally construct the class!
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null && securityContext != null)
+ {
+ return (Class)AccessController.doPrivileged
+ (new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return defineClass(className, classData,
+ 0, classData.length,
+ source);
+ }
+ }, securityContext);
+ }
+ else
+ return defineClass(className, classData,
+ 0, classData.length,
+ source);
+ }
+ catch (IOException ioe)
+ {
+ throw new ClassNotFoundException(className, ioe);
+ }
+ }
+
+ /**
+ * Finds the first occurrence of a resource that can be found. The locations
+ * are searched in the order they were added to the URLClassLoader.
+ *
+ * @param resourceName the resource name to look for
+ * @return the URLResource for the resource if found, null otherwise
+ */
+ private Resource findURLResource(String resourceName)
+ {
+ int max = urls.size();
+ for (int i = 0; i < max; i++)
+ {
+ URLLoader loader = (URLLoader)urlinfos.elementAt(i);
+ if (loader == null)
+ continue;
+
+ Resource resource = loader.getResource(resourceName);
+ if (resource != null)
+ return resource;
+ }
+ return null;
+ }
+
+ /**
+ * Finds the first occurrence of a resource that can be found.
+ *
+ * @param resourceName the resource name to look for
+ * @return the URL if found, null otherwise
+ */
+ public URL findResource(String resourceName)
+ {
+ Resource resource = findURLResource(resourceName);
+ if (resource != null)
+ return resource.getURL();
+
+ // Resource not found
+ return null;
+ }
+
+ /**
+ * If the URLStreamHandlerFactory has been set this return the appropriate
+ * URLStreamHandler for the given protocol, if not set returns null.
+ *
+ * @param protocol the protocol for which we need a URLStreamHandler
+ * @return the appropriate URLStreamHandler or null
+ */
+ URLStreamHandler getURLStreamHandler(String protocol)
+ {
+ if (factory == null)
+ return null;
+
+ URLStreamHandler handler;
+ synchronized (factoryCache)
+ {
+ // check if there're handler for the same protocol in cache
+ HashMap cache = (HashMap)factoryCache.get(factory);
+ handler = (URLStreamHandler)cache.get(protocol);
+ if(handler == null)
+ {
+ // add it to cache
+ handler = factory.createURLStreamHandler(protocol);
+ cache.put(protocol, handler);
+ }
+ }
+ return handler;
+ }
+
+ /**
+ * Finds all the resources with a particular name from all the locations.
+ *
+ * @exception IOException when an error occurs accessing one of the
+ * locations
+ * @param resourceName the name of the resource to lookup
+ * @return a (possible empty) enumeration of URLs where the resource can be
+ * found
+ */
+ public Enumeration findResources(String resourceName) throws IOException
+ {
+ Vector resources = new Vector();
+ int max = urls.size();
+ for (int i = 0; i < max; i++)
+ {
+ URLLoader loader = (URLLoader)urlinfos.elementAt(i);
+ Resource resource = loader.getResource(resourceName);
+ if (resource != null)
+ resources.add(resource.getURL());
+ }
+ return resources.elements();
+ }
+
+ /**
+ * Returns the permissions needed to access a particular code
+ * source. These permissions includes those returned by
+ * <code>SecureClassLoader.getPermissions()</code> and the actual
+ * permissions to access the objects referenced by the URL of the
+ * code source. The extra permissions added depend on the protocol
+ * and file portion of the URL in the code source. If the URL has
+ * the "file" protocol ends with a '/' character then it must be a
+ * directory and a file Permission to read everything in that
+ * directory and all subdirectories is added. If the URL had the
+ * "file" protocol and doesn't end with a '/' character then it must
+ * be a normal file and a file permission to read that file is
+ * added. If the <code>URL</code> has any other protocol then a
+ * socket permission to connect and accept connections from the host
+ * portion of the URL is added.
+ *
* @param source The codesource that needs the permissions to be accessed
* @return the collection of permissions needed to access the code resource
- * @see SecureClassLoader.getPermissions()
+ * @see java.security.SecureClassLoader#getPermissions()
*/
protected PermissionCollection getPermissions(CodeSource source)
{
@@ -408,7 +972,7 @@ public class URLClassLoader extends SecureClassLoader
{
String file = url.getFile();
// If the file end in / it must be an directory
- if (file.endsWith("/"))
+ if (file.endsWith("/") || file.endsWith(File.separator))
{
// Grant permission to read everything in that directory and
// all subdirectories
@@ -424,35 +988,81 @@ public class URLClassLoader extends SecureClassLoader
else
{
// Grant permission to connect to and accept connections from host
- String host = url.getHost();
+ String host = url.getHost();
+ if (host != null)
permissions.add(new SocketPermission(host, "connect,accept"));
}
return permissions;
}
+
+ /**
+ * Returns all the locations that this class loader currently uses the
+ * resolve classes and resource. This includes both the initially supplied
+ * URLs as any URLs added later by the loader.
+ * @return All the currently used URLs
+ */
+ public URL[] getURLs()
+ {
+ return (URL[]) urls.toArray(new URL[urls.size()]);
+ }
/**
- * Creates a new instance of a URLClassLoader that gets classes from the
- * supplied URLs. This class loader will have as parent the standard
- * system class loader.
- * @param urls the initial URLs used to resolve classes and resources
+ * Creates a new instance of a <code>URLClassLoader</code> that gets
+ * classes from the supplied <code>URL</code>s. This class loader
+ * will have as parent the standard system class loader.
+ *
+ * @param urls the initial URLs used to resolve classes and
+ * resources
+ *
+ * @exception SecurityException when the calling code does not have
+ * permission to access the given <code>URL</code>s
*/
- public static URLClassLoader newInstance(URL[] urls) throws
- SecurityException
+ public static URLClassLoader newInstance(URL urls[])
+ throws SecurityException
{
- return new URLClassLoader(urls);
+ return newInstance(urls, null);
}
/**
- * Creates a new instance of a URLClassLoader that gets classes from the
- * supplied URLs and with the supplied loader as parent class loader.
- * @param urls the initial URLs used to resolve classes and resources
+ * Creates a new instance of a <code>URLClassLoader</code> that gets
+ * classes from the supplied <code>URL</code>s and with the supplied
+ * loader as parent class loader.
+ *
+ * @param urls the initial URLs used to resolve classes and
+ * resources
* @param parent the parent class loader
+ *
+ * @exception SecurityException when the calling code does not have
+ * permission to access the given <code>URL</code>s
*/
- public static URLClassLoader newInstance(URL[] urls,
- ClassLoader parent)
+ public static URLClassLoader newInstance(URL urls[],
+ final ClassLoader parent)
throws SecurityException
{
- return new URLClassLoader(urls, parent);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null)
+ return new URLClassLoader(urls, parent);
+ else
+ {
+ final Object securityContext = sm.getSecurityContext();
+ // XXX - What to do with anything else then an AccessControlContext?
+ if (!(securityContext instanceof AccessControlContext))
+ throw new SecurityException
+ ("securityContext must be AccessControlContext: "
+ + securityContext);
+
+ URLClassLoader loader =
+ (URLClassLoader)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return new URLClassLoader
+ (parent, (AccessControlContext)securityContext);
+ }
+ });
+ loader.addURLs(urls);
+ return loader;
+ }
}
}