1 /* RMIClassLoaderImpl.java -- FIXME: briefly describe file purpose 2 Copyright (C) 2005, 2006 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 gnu.java.lang.CPStringBuilder; 42 43 import java.lang.reflect.Proxy; 44 import java.net.MalformedURLException; 45 import java.net.URL; 46 import java.net.URLClassLoader; 47 import java.rmi.server.RMIClassLoaderSpi; 48 import java.util.ArrayList; 49 import java.util.Hashtable; 50 import java.util.Map; 51 import java.util.StringTokenizer; 52 53 /** 54 * The default implementation of {@link java.rmi.server.RMIClassLoaderSpi}. 55 * 56 * @author Roman Kennke (kennke@aicas.com) 57 */ 58 public class RMIClassLoaderImpl extends RMIClassLoaderSpi 59 { 60 private static class MyClassLoader extends URLClassLoader 61 { 62 // Package-private to avoid a trampoline constructor. MyClassLoader(URL[] urls, ClassLoader parent, String annotation)63 MyClassLoader (URL[] urls, ClassLoader parent, String annotation) 64 { 65 super (urls, parent); 66 this.annotation = annotation; 67 } 68 urlToAnnotation(URL[] urls)69 public static String urlToAnnotation (URL[] urls) 70 { 71 if (urls.length == 0) 72 return null; 73 74 CPStringBuilder annotation = new CPStringBuilder (64 * urls.length); 75 76 for (int i = 0; i < urls.length; i++) 77 { 78 annotation.append (urls [i].toExternalForm()); 79 annotation.append (' '); 80 } 81 82 return annotation.toString(); 83 } 84 getClassAnnotation()85 public final String getClassAnnotation() 86 { 87 return annotation; 88 } 89 90 private final String annotation; 91 } 92 93 /** 94 * This class is used to identify a cached classloader by its codebase and 95 * the context classloader that is its parent. 96 */ 97 private static class CacheKey 98 { 99 private String mCodeBase; 100 private ClassLoader mContextClassLoader; 101 CacheKey(String theCodebase, ClassLoader theContextClassLoader)102 public CacheKey (String theCodebase, ClassLoader theContextClassLoader) 103 { 104 mCodeBase = theCodebase; 105 mContextClassLoader = theContextClassLoader; 106 } 107 108 /** 109 * @return true if the codebase and the context classloader are equal 110 */ equals(Object theOther)111 public boolean equals (Object theOther) 112 { 113 if (theOther instanceof CacheKey) 114 { 115 CacheKey key = (CacheKey) theOther; 116 117 return (equals (this.mCodeBase,key.mCodeBase) 118 && equals (this.mContextClassLoader, key.mContextClassLoader)); 119 } 120 return false; 121 } 122 123 /** 124 * Test if the two objects are equal or both null. 125 * @param theOne 126 * @param theOther 127 * @return 128 */ equals(Object theOne, Object theOther)129 private boolean equals (Object theOne, Object theOther) 130 { 131 return theOne != null ? theOne.equals (theOther) : theOther == null; 132 } 133 134 /** 135 * @return hashCode 136 */ hashCode()137 public int hashCode() 138 { 139 return ((mCodeBase != null ? mCodeBase.hashCode() : 0) 140 ^(mContextClassLoader != null ? mContextClassLoader.hashCode() : -1)); 141 } 142 toString()143 public String toString() 144 { 145 return "[" + mCodeBase + "," + mContextClassLoader + "]"; 146 } 147 148 } 149 150 private static RMIClassLoaderImpl instance = null; 151 152 private static Map cacheLoaders; //map annotations to loaders 153 private static Map cacheAnnotations; //map loaders to annotations 154 //class loader for defaultAnnotation 155 private static MyClassLoader defaultClassLoader; 156 157 //defaultAnnotation is got from system property 158 // "java.rmi.server.defaultAnnotation" 159 private static String defaultAnnotation; 160 161 //URL object for defaultAnnotation 162 private static URL defaultCodebase; 163 164 static 165 { 166 // 89 is a nice prime number for Hashtable initial capacity 167 cacheLoaders = new Hashtable (89); 168 cacheAnnotations = new Hashtable (89); 169 170 defaultAnnotation = System.getProperty ("java.rmi.server.defaultAnnotation"); 171 172 try 173 { 174 if (defaultAnnotation != null) 175 defaultCodebase = new URL (defaultAnnotation); 176 } 177 catch (Exception _) 178 { 179 defaultCodebase = null; 180 } 181 182 if (defaultCodebase != null) 183 { 184 defaultClassLoader = new MyClassLoader (new URL[] { defaultCodebase }, null, 185 defaultAnnotation); 186 // XXX using getContextClassLoader here *cannot* be right cacheLoaders.put(new CacheKey (defaultAnnotation, Thread.currentThread().getContextClassLoader()), defaultClassLoader)187 cacheLoaders.put (new CacheKey (defaultAnnotation, 188 Thread.currentThread().getContextClassLoader()), 189 defaultClassLoader); 190 } 191 } 192 193 /** 194 * This is a singleton class and may only be instantiated once from within 195 * the {@link #getInstance} method. 196 */ RMIClassLoaderImpl()197 private RMIClassLoaderImpl() 198 { 199 } 200 201 /** 202 * Returns an instance of RMIClassLoaderImpl. 203 * 204 * @return an instance of RMIClassLoaderImpl 205 */ getInstance()206 public static RMIClassLoaderSpi getInstance() 207 { 208 if (instance == null) 209 instance = new RMIClassLoaderImpl(); 210 return instance; 211 } 212 loadClass(String codeBase, String name, ClassLoader defaultLoader)213 public Class loadClass(String codeBase, String name, 214 ClassLoader defaultLoader) 215 throws MalformedURLException, ClassNotFoundException 216 { 217 try 218 { 219 if (defaultLoader != null) 220 return Class.forName(name, false, defaultLoader); 221 } 222 catch (ClassNotFoundException e) 223 { 224 } 225 226 return Class.forName(name, false, getClassLoader(codeBase)); 227 } 228 loadProxyClass(String codeBase, String[] interfaces, ClassLoader defaultLoader)229 public Class loadProxyClass(String codeBase, String[] interfaces, 230 ClassLoader defaultLoader) 231 throws MalformedURLException, ClassNotFoundException 232 { 233 Class clss[] = new Class[interfaces.length]; 234 235 for (int i = 0; i < interfaces.length; i++) 236 { 237 clss[i] = loadClass(codeBase, interfaces[i], defaultLoader); 238 } 239 240 // Chain all class loaders (they may differ). 241 ArrayList loaders = new ArrayList(clss.length); 242 ClassLoader loader = null; 243 for (int i = 0; i < clss.length; i++) 244 { 245 loader = clss[i].getClassLoader(); 246 if (! loaders.contains(loader)) 247 { 248 loaders.add(0, loader); 249 } 250 } 251 if (loaders.size() > 1) 252 { 253 loader = new CombinedClassLoader(loaders); 254 } 255 256 try 257 { 258 return Proxy.getProxyClass(loader, clss); 259 } 260 catch (IllegalArgumentException e) 261 { 262 throw new ClassNotFoundException(null, e); 263 } 264 } 265 266 /** 267 * Gets a classloader for the given codebase and with the current 268 * context classloader as parent. 269 * 270 * @param codebase 271 * 272 * @return a classloader for the given codebase 273 * 274 * @throws MalformedURLException if the codebase contains a malformed URL 275 */ getClassLoader(String codebase)276 public ClassLoader getClassLoader(String codebase) 277 throws MalformedURLException 278 { 279 if (codebase == null || codebase.length() == 0) 280 return Thread.currentThread().getContextClassLoader(); 281 282 ClassLoader loader; 283 CacheKey loaderKey = new CacheKey 284 (codebase, Thread.currentThread().getContextClassLoader()); 285 loader = (ClassLoader) cacheLoaders.get (loaderKey); 286 287 if (loader == null) 288 { 289 //create an entry in cacheLoaders mapping a loader to codebases. 290 // codebases are separated by " " 291 StringTokenizer tok = new StringTokenizer (codebase, " "); 292 ArrayList urls = new ArrayList(); 293 294 while (tok.hasMoreTokens()) 295 urls.add (new URL(tok.nextToken())); 296 297 loader = new MyClassLoader((URL[]) urls.toArray(new URL [urls.size()]), 298 Thread.currentThread().getContextClassLoader(), 299 codebase); 300 cacheLoaders.put (loaderKey, loader); 301 } 302 303 return loader; 304 } 305 306 /** 307 * Returns a string representation of the network location where a remote 308 * endpoint can get the class-definition of the given class. 309 * 310 * @param cl 311 * 312 * @return a space seperated list of URLs where the class-definition 313 * of cl may be found 314 */ getClassAnnotation(Class cl)315 public String getClassAnnotation(Class cl) 316 { 317 ClassLoader loader = cl.getClassLoader(); 318 319 if (loader == null 320 || loader == ClassLoader.getSystemClassLoader()) 321 { 322 return System.getProperty ("java.rmi.server.codebase"); 323 } 324 325 if (loader instanceof MyClassLoader) 326 { 327 return ((MyClassLoader) loader).getClassAnnotation(); 328 } 329 330 String s = (String) cacheAnnotations.get (loader); 331 332 if (s != null) 333 return s; 334 335 if (loader instanceof URLClassLoader) 336 { 337 URL[] urls = ((URLClassLoader) loader).getURLs(); 338 339 if (urls.length == 0) 340 return null; 341 342 CPStringBuilder annotation = new CPStringBuilder (64 * urls.length); 343 344 for (int i = 0; i < urls.length; i++) 345 { 346 annotation.append (urls [i].toExternalForm()); 347 annotation.append (' '); 348 } 349 350 s = annotation.toString(); 351 cacheAnnotations.put (loader, s); 352 return s; 353 } 354 355 return System.getProperty ("java.rmi.server.codebase"); 356 } 357 } 358