Source for java.rmi.server.RMIClassLoader

   1: /* RMIClassLoader.java --
   2:    Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2004
   3:    Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: package java.rmi.server;
  40: 
  41: import java.net.MalformedURLException;
  42: import java.net.URL;
  43: import java.net.URLClassLoader;
  44: import java.util.ArrayList;
  45: import java.util.Hashtable;
  46: import java.util.Map;
  47: import java.util.StringTokenizer;
  48: 
  49: 
  50: /**
  51:  * This class provides a set of public static utility methods for supporting
  52:  * network-based class loading in RMI. These methods are called by RMI's
  53:  * internal marshal streams to implement the dynamic class loading of types for
  54:  * RMI parameters and return values.
  55:  */
  56: public class RMIClassLoader
  57: {
  58:   /**
  59:    * This class isn't intended to be instantiated.
  60:    */
  61:   private RMIClassLoader() {}
  62: 
  63:   private static class MyClassLoader extends URLClassLoader
  64:   {
  65:     // Package-private to avoid a trampoline constructor.
  66:     MyClassLoader (URL[] urls, ClassLoader parent, String annotation)
  67:     {
  68:       super (urls, parent);
  69:       this.annotation = annotation;
  70:     }
  71: 
  72:     private MyClassLoader (URL[] urls, ClassLoader parent)
  73:     {
  74:       super (urls, parent);
  75:       this.annotation = urlToAnnotation (urls);
  76:     }
  77: 
  78:     public static String urlToAnnotation (URL[] urls)
  79:     {
  80:       if (urls.length == 0)
  81:         return null;
  82: 
  83:       StringBuffer annotation = new StringBuffer (64 * urls.length);
  84: 
  85:       for (int i = 0; i < urls.length; i++)
  86:       {
  87:         annotation.append (urls [i].toExternalForm());
  88:         annotation.append (' ');
  89:       }
  90: 
  91:       return annotation.toString();
  92:     }
  93: 
  94:     public final String getClassAnnotation()
  95:     {
  96:       return annotation;
  97:     }
  98: 
  99:     private final String annotation;
 100:   }
 101:   
 102:   /** 
 103:    * This class is used to identify a cached classloader by its codebase and 
 104:    * the context classloader that is its parent.
 105:    */  
 106:   private static class CacheKey
 107:   {
 108:      private String mCodeBase;
 109:      private ClassLoader mContextClassLoader;
 110:       
 111:      public CacheKey (String theCodebase, ClassLoader theContextClassLoader)
 112:      {
 113:        mCodeBase = theCodebase;
 114:        mContextClassLoader = theContextClassLoader;
 115:      }
 116:       
 117:     /**
 118:      * @return true if the codebase and the context classloader are equal
 119:      */
 120:     public boolean equals (Object theOther)
 121:     {
 122:       if (theOther instanceof CacheKey)
 123:       {
 124:           CacheKey key = (CacheKey) theOther;
 125:     
 126:           return (equals (this.mCodeBase,key.mCodeBase)
 127:                 && equals (this.mContextClassLoader, key.mContextClassLoader));
 128:         }
 129:       return false;
 130:     }
 131:     
 132:     /**
 133:      * Test if the two objects are equal or both null.
 134:      * @param theOne
 135:      * @param theOther
 136:      * @return
 137:      */
 138:     private boolean equals (Object theOne, Object theOther)
 139:     {
 140:       return theOne != null ? theOne.equals (theOther) : theOther == null;
 141:     }
 142: 
 143:     /**
 144:      * @return hashCode  
 145:      */
 146:     public int hashCode()
 147:     {
 148:       return ((mCodeBase != null           ? mCodeBase.hashCode()           :  0) 
 149:               ^(mContextClassLoader != null ? mContextClassLoader.hashCode() : -1));
 150:     }
 151: 
 152:     public String toString()
 153:     {
 154:       return "[" + mCodeBase + "," + mContextClassLoader + "]"; 
 155:     }
 156: 
 157:   }
 158: 
 159:   private static Map cacheLoaders; //map annotations to loaders
 160:   private static Map cacheAnnotations; //map loaders to annotations
 161: 
 162:   //defaultAnnotation is got from system property
 163:   // "java.rmi.server.defaultAnnotation"
 164:   private static String defaultAnnotation;
 165: 
 166:   //URL object for defaultAnnotation
 167:   private static URL defaultCodebase;
 168: 
 169:   //class loader for defaultAnnotation
 170:   private static MyClassLoader defaultLoader;
 171: 
 172:   static
 173:   {
 174:     // 89 is a nice prime number for Hashtable initial capacity
 175:     cacheLoaders = new Hashtable (89);
 176:     cacheAnnotations = new Hashtable (89);
 177: 
 178:     defaultAnnotation = System.getProperty ("java.rmi.server.defaultAnnotation");
 179: 
 180:     try
 181:       {
 182:         if (defaultAnnotation != null)
 183:           defaultCodebase = new URL (defaultAnnotation);
 184:       }
 185:     catch (Exception _)
 186:       {
 187:         defaultCodebase = null;
 188:       }
 189: 
 190:     if (defaultCodebase != null)
 191:       {
 192:         defaultLoader = new MyClassLoader (new URL[] { defaultCodebase }, null,
 193:                                            defaultAnnotation);
 194:         cacheLoaders.put (new CacheKey (defaultAnnotation,
 195:                                         Thread.currentThread().getContextClassLoader()),
 196:                                         defaultLoader);
 197:       }
 198:     }
 199: 
 200:   /**
 201:    * @deprecated
 202:    */
 203:   public static Class loadClass (String name)
 204:     throws MalformedURLException, ClassNotFoundException
 205:   {
 206:     return loadClass ("", name);
 207:   }
 208: 
 209:   public static Class loadClass (String codebases, String name)
 210:     throws MalformedURLException, ClassNotFoundException
 211:   {
 212:     ClassLoader loader = Thread.currentThread().getContextClassLoader();
 213: 
 214:     //try context class loader first
 215:     try 
 216:       {
 217:         return Class.forName(name, false, loader);
 218:       }
 219:     catch (ClassNotFoundException e)
 220:       {
 221:         // class not found in the local classpath
 222:       }
 223: 
 224:     if (codebases.length() == 0) //==""
 225:       {
 226:         loader = defaultLoader;
 227:       }
 228:     else
 229:       {
 230:         loader = getClassLoader(codebases);
 231:       }
 232: 
 233:     if (loader == null)
 234:       {
 235:         //do not throw NullPointerException
 236:         throw new ClassNotFoundException ("Could not find class (" + name +
 237:                                           ") at codebase (" + codebases + ")");
 238:       }
 239:       
 240:     return Class.forName(name, false, loader);
 241:   }
 242: 
 243:   /**
 244:    * Gets a classloader for the given codebase and with the current
 245:    * context classloader as parent.
 246:    * 
 247:    * @param codebases
 248:    * 
 249:    * @return a classloader for the given codebase
 250:    * 
 251:    * @throws MalformedURLException if the codebase contains a malformed URL
 252:    */
 253:   public static ClassLoader getClassLoader (String codebases) 
 254:     throws MalformedURLException
 255:   {
 256:     ClassLoader loader;
 257:     CacheKey loaderKey = new CacheKey
 258:       (codebases, Thread.currentThread().getContextClassLoader());
 259:     loader = (ClassLoader) cacheLoaders.get (loaderKey);
 260:       
 261:     if (loader == null)
 262:       {
 263:         //create an entry in cacheLoaders mapping a loader to codebases.
 264:         // codebases are separated by " "
 265:         StringTokenizer tok = new StringTokenizer (codebases, " ");
 266:         ArrayList urls = new ArrayList();
 267:       
 268:         while (tok.hasMoreTokens())
 269:           urls.add (new URL (tok.nextToken()));
 270:       
 271:         loader = new MyClassLoader ((URL[]) urls.toArray (new URL [urls.size()]),
 272:                                     Thread.currentThread().getContextClassLoader(),
 273:                                     codebases);
 274:         cacheLoaders.put (loaderKey, loader);
 275:       }
 276:            
 277:     return loader;
 278:   }
 279:  
 280:   /**
 281:    * Returns a string representation of the network location where a remote
 282:    * endpoint can get the class-definition of the given class.
 283:    *
 284:    * @param cl
 285:    *
 286:    * @return a space seperated list of URLs where the class-definition
 287:    * of cl may be found
 288:    */
 289:   public static String getClassAnnotation (Class cl)
 290:   {
 291:     ClassLoader loader = cl.getClassLoader();
 292:     
 293:     if (loader == null
 294:         || loader == ClassLoader.getSystemClassLoader())
 295:       {
 296:         return System.getProperty ("java.rmi.server.codebase");
 297:       }
 298: 
 299:     if (loader instanceof MyClassLoader)
 300:       {
 301:         return ((MyClassLoader) loader).getClassAnnotation();
 302:       }
 303: 
 304:     String s = (String) cacheAnnotations.get (loader);
 305: 
 306:     if (s != null)
 307:       return s;
 308: 
 309:     if (loader instanceof URLClassLoader)
 310:       {
 311:         URL[] urls = ((URLClassLoader) loader).getURLs();
 312: 
 313:         if (urls.length == 0)
 314:           return null;
 315: 
 316:         StringBuffer annotation = new StringBuffer (64 * urls.length);
 317: 
 318:         for (int i = 0; i < urls.length; i++)
 319:           {
 320:             annotation.append (urls [i].toExternalForm());
 321:             annotation.append (' ');
 322:           }
 323: 
 324:         s = annotation.toString();
 325:         cacheAnnotations.put (loader, s);
 326:         return s;
 327:       }
 328: 
 329:     return System.getProperty ("java.rmi.server.codebase");
 330:   }
 331: 
 332:   /**
 333:    * @deprecated
 334:    */
 335:   public static Object getSecurityContext (ClassLoader loader)
 336:   {
 337:     throw new Error ("Not implemented");
 338:   }
 339: }