| /* RMIClassLoader.java -- |
| Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2004 |
| Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| 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.rmi.server; |
| |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| |
| /** |
| * This class provides a set of public static utility methods for supporting |
| * network-based class loading in RMI. These methods are called by RMI's |
| * internal marshal streams to implement the dynamic class loading of types for |
| * RMI parameters and return values. |
| */ |
| public class RMIClassLoader |
| { |
| /** |
| * This class isn't intended to be instantiated. |
| */ |
| private RMIClassLoader() {} |
| |
| private static class MyClassLoader extends URLClassLoader |
| { |
| // Package-private to avoid a trampoline constructor. |
| MyClassLoader (URL[] urls, ClassLoader parent, String annotation) |
| { |
| super (urls, parent); |
| this.annotation = annotation; |
| } |
| |
| private MyClassLoader (URL[] urls, ClassLoader parent) |
| { |
| super (urls, parent); |
| this.annotation = urlToAnnotation (urls); |
| } |
| |
| public static String urlToAnnotation (URL[] urls) |
| { |
| if (urls.length == 0) |
| return null; |
| |
| StringBuffer annotation = new StringBuffer (64 * urls.length); |
| |
| for (int i = 0; i < urls.length; i++) |
| { |
| annotation.append (urls [i].toExternalForm()); |
| annotation.append (' '); |
| } |
| |
| return annotation.toString(); |
| } |
| |
| public final String getClassAnnotation() |
| { |
| return annotation; |
| } |
| |
| private final String annotation; |
| } |
| |
| /** |
| * This class is used to identify a cached classloader by its codebase and |
| * the context classloader that is its parent. |
| */ |
| private static class CacheKey |
| { |
| private String mCodeBase; |
| private ClassLoader mContextClassLoader; |
| |
| public CacheKey (String theCodebase, ClassLoader theContextClassLoader) |
| { |
| mCodeBase = theCodebase; |
| mContextClassLoader = theContextClassLoader; |
| } |
| |
| /** |
| * @return true if the codebase and the context classloader are equal |
| */ |
| public boolean equals (Object theOther) |
| { |
| if (theOther instanceof CacheKey) |
| { |
| CacheKey key = (CacheKey) theOther; |
| |
| return (equals (this.mCodeBase,key.mCodeBase) |
| && equals (this.mContextClassLoader, key.mContextClassLoader)); |
| } |
| return false; |
| } |
| |
| /** |
| * Test if the two objects are equal or both null. |
| * @param theOne |
| * @param theOther |
| * @return |
| */ |
| private boolean equals (Object theOne, Object theOther) |
| { |
| return theOne != null ? theOne.equals (theOther) : theOther == null; |
| } |
| |
| /** |
| * @return hashCode |
| */ |
| public int hashCode() |
| { |
| return ((mCodeBase != null ? mCodeBase.hashCode() : 0) |
| ^(mContextClassLoader != null ? mContextClassLoader.hashCode() : -1)); |
| } |
| |
| public String toString() |
| { |
| return "[" + mCodeBase + "," + mContextClassLoader + "]"; |
| } |
| |
| } |
| |
| private static Map cacheLoaders; //map annotations to loaders |
| private static Map cacheAnnotations; //map loaders to annotations |
| |
| //defaultAnnotation is got from system property |
| // "java.rmi.server.defaultAnnotation" |
| private static String defaultAnnotation; |
| |
| //URL object for defaultAnnotation |
| private static URL defaultCodebase; |
| |
| //class loader for defaultAnnotation |
| private static MyClassLoader defaultLoader; |
| |
| static |
| { |
| // 89 is a nice prime number for Hashtable initial capacity |
| cacheLoaders = new Hashtable (89); |
| cacheAnnotations = new Hashtable (89); |
| |
| defaultAnnotation = System.getProperty ("java.rmi.server.defaultAnnotation"); |
| |
| try |
| { |
| if (defaultAnnotation != null) |
| defaultCodebase = new URL (defaultAnnotation); |
| } |
| catch (Exception _) |
| { |
| defaultCodebase = null; |
| } |
| |
| if (defaultCodebase != null) |
| { |
| defaultLoader = new MyClassLoader (new URL[] { defaultCodebase }, null, |
| defaultAnnotation); |
| cacheLoaders.put (new CacheKey (defaultAnnotation, |
| Thread.currentThread().getContextClassLoader()), |
| defaultLoader); |
| } |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public static Class loadClass (String name) |
| throws MalformedURLException, ClassNotFoundException |
| { |
| return loadClass ("", name); |
| } |
| |
| public static Class loadClass (String codebases, String name) |
| throws MalformedURLException, ClassNotFoundException |
| { |
| ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| |
| //try context class loader first |
| try |
| { |
| return loader.loadClass (name); |
| } |
| catch (ClassNotFoundException e) |
| { |
| // class not found in the local classpath |
| } |
| |
| if (codebases.length() == 0) //=="" |
| { |
| loader = defaultLoader; |
| } |
| else |
| { |
| loader = getClassLoader(codebases); |
| } |
| |
| if (loader == null) |
| { |
| //do not throw NullPointerException |
| throw new ClassNotFoundException ("Could not find class (" + name + |
| ") at codebase (" + codebases + ")"); |
| } |
| |
| return loader.loadClass (name); |
| } |
| |
| /** |
| * Gets a classloader for the given codebase and with the current |
| * context classloader as parent. |
| * |
| * @param codebases |
| * |
| * @return a classloader for the given codebase |
| * |
| * @throws MalformedURLException if the codebase contains a malformed URL |
| */ |
| private static ClassLoader getClassLoader (String codebases) |
| throws MalformedURLException |
| { |
| ClassLoader loader; |
| CacheKey loaderKey = new CacheKey |
| (codebases, Thread.currentThread().getContextClassLoader()); |
| loader = (ClassLoader) cacheLoaders.get (loaderKey); |
| |
| if (loader == null) |
| { |
| //create an entry in cacheLoaders mapping a loader to codebases. |
| // codebases are separated by " " |
| StringTokenizer tok = new StringTokenizer (codebases, " "); |
| ArrayList urls = new ArrayList(); |
| |
| while (tok.hasMoreTokens()) |
| urls.add (new URL (tok.nextToken())); |
| |
| loader = new MyClassLoader ((URL[]) urls.toArray (new URL [urls.size()]), |
| Thread.currentThread().getContextClassLoader(), |
| codebases); |
| cacheLoaders.put (loaderKey, loader); |
| } |
| |
| return loader; |
| } |
| |
| /** |
| * Returns a string representation of the network location where a remote |
| * endpoint can get the class-definition of the given class. |
| * |
| * @param cl |
| * |
| * @return a space seperated list of URLs where the class-definition |
| * of cl may be found |
| */ |
| public static String getClassAnnotation (Class cl) |
| { |
| ClassLoader loader = cl.getClassLoader(); |
| |
| if (loader == null |
| || loader == ClassLoader.getSystemClassLoader()) |
| { |
| return System.getProperty ("java.rmi.server.codebase"); |
| } |
| |
| if (loader instanceof MyClassLoader) |
| { |
| return ((MyClassLoader) loader).getClassAnnotation(); |
| } |
| |
| String s = (String) cacheAnnotations.get (loader); |
| |
| if (s != null) |
| return s; |
| |
| if (loader instanceof URLClassLoader) |
| { |
| URL[] urls = ((URLClassLoader) loader).getURLs(); |
| |
| if (urls.length == 0) |
| return null; |
| |
| StringBuffer annotation = new StringBuffer (64 * urls.length); |
| |
| for (int i = 0; i < urls.length; i++) |
| { |
| annotation.append (urls [i].toExternalForm()); |
| annotation.append (' '); |
| } |
| |
| s = annotation.toString(); |
| cacheAnnotations.put (loader, s); |
| return s; |
| } |
| |
| return System.getProperty ("java.rmi.server.codebase"); |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public static Object getSecurityContext (ClassLoader loader) |
| { |
| throw new Error ("Not implemented"); |
| } |
| } |