Source code: java/rmi/server/RMIClassLoader.java
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 }