Source for gnu.java.rmi.server.RMIClassLoaderImpl

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