KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > kernel > ClassLoading


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.geronimo.kernel;
19
20 import java.lang.reflect.Array JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.util.LinkedHashSet JavaDoc;
24 import java.util.LinkedList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.ArrayList JavaDoc;
28
29 import org.apache.geronimo.kernel.config.MultiParentClassLoader;
30
31 /**
32  * Utility class for loading classes by a variety of name variations.
33  * <p/>
34  * Supported names types are:
35  * <p/>
36  * 1) Fully qualified class name (e.g., "java.lang.String", "org.apache.geronimo.kernel.ClassLoading"
37  * 2) Method signature encoding ("Ljava.lang.String;", "J", "I", etc.)
38  * 3) Primitive type names ("int", "boolean", etc.)
39  * 4) Method array signature strings ("[I", "[Ljava.lang.String")
40  * 5) Arrays using Java code format ("int[]", "java.lang.String[][]")
41  * <p/>
42  * The classes are loaded using the provided class loader. For the basic types, the primitive
43  * reflection types are returned.
44  *
45  * @version $Rev: 476049 $
46  */

47 public class ClassLoading {
48
49     /**
50      * Table for mapping primitive class names/signatures to the implementing
51      * class object
52      */

53     private static final HashMap JavaDoc PRIMITIVE_CLASS_MAP = new HashMap JavaDoc();
54
55     /**
56      * Table for mapping primitive classes back to their name signature type, which
57      * allows a reverse mapping to be performed from a class object into a resolvable
58      * signature.
59      */

60     private static final HashMap JavaDoc CLASS_TO_SIGNATURE_MAP = new HashMap JavaDoc();
61
62
63     /**
64      * Setup the primitives map. We make any entry for each primitive class using both the
65      * human readable name and the method signature shorthand type.
66      */

67     static {
68         PRIMITIVE_CLASS_MAP.put("boolean", boolean.class);
69         PRIMITIVE_CLASS_MAP.put("Z", boolean.class);
70         PRIMITIVE_CLASS_MAP.put("byte", byte.class);
71         PRIMITIVE_CLASS_MAP.put("B", byte.class);
72         PRIMITIVE_CLASS_MAP.put("char", char.class);
73         PRIMITIVE_CLASS_MAP.put("C", char.class);
74         PRIMITIVE_CLASS_MAP.put("short", short.class);
75         PRIMITIVE_CLASS_MAP.put("S", short.class);
76         PRIMITIVE_CLASS_MAP.put("int", int.class);
77         PRIMITIVE_CLASS_MAP.put("I", int.class);
78         PRIMITIVE_CLASS_MAP.put("long", long.class);
79         PRIMITIVE_CLASS_MAP.put("J", long.class);
80         PRIMITIVE_CLASS_MAP.put("float", float.class);
81         PRIMITIVE_CLASS_MAP.put("F", float.class);
82         PRIMITIVE_CLASS_MAP.put("double", double.class);
83         PRIMITIVE_CLASS_MAP.put("D", double.class);
84         PRIMITIVE_CLASS_MAP.put("void", void.class);
85         PRIMITIVE_CLASS_MAP.put("V", void.class);
86
87         // Now build a reverse mapping table. The table above has a many-to-one mapping for
88
// class names. To do the reverse, we need to pick just one. As long as the
89
// returned name supports "round tripping" of the requests, this will work fine.
90

91         CLASS_TO_SIGNATURE_MAP.put(boolean.class, "Z");
92         CLASS_TO_SIGNATURE_MAP.put(byte.class, "B");
93         CLASS_TO_SIGNATURE_MAP.put(char.class, "C");
94         CLASS_TO_SIGNATURE_MAP.put(short.class, "S");
95         CLASS_TO_SIGNATURE_MAP.put(int.class, "I");
96         CLASS_TO_SIGNATURE_MAP.put(long.class, "J");
97         CLASS_TO_SIGNATURE_MAP.put(float.class, "F");
98         CLASS_TO_SIGNATURE_MAP.put(double.class, "D");
99         CLASS_TO_SIGNATURE_MAP.put(void.class, "V");
100     }
101
102
103     /**
104      * Load a class that matches the requested name, using the provided class loader context.
105      * <p/>
106      * The class name may be a standard class name, the name of a primitive type Java
107      * reflection class (e.g., "boolean" or "int"), or a type in method type signature
108      * encoding. Array classes in either encoding form are also processed.
109      *
110      * @param className The name of the required class.
111      * @param classLoader The class loader used to resolve the class object.
112      * @return The Class object resolved from "className".
113      * @throws ClassNotFoundException When unable to resolve the class object.
114      * @throws IllegalArgumentException If either argument is null.
115      */

116     public static Class JavaDoc loadClass(String JavaDoc className, ClassLoader JavaDoc classLoader) throws ClassNotFoundException JavaDoc {
117
118         // the tests require IllegalArgumentExceptions for null values on either of these.
119
if (className == null) {
120             throw new IllegalArgumentException JavaDoc("className is null");
121         }
122
123         if (classLoader == null) {
124             throw new IllegalArgumentException JavaDoc("classLoader is null");
125         }
126         // The easiest case is a proper class name. We just have the class loader resolve this.
127
// If the class loader throws a ClassNotFoundException, then we need to check each of the
128
// special name encodings we support.
129
try {
130             return classLoader.loadClass(className);
131         } catch (ClassNotFoundException JavaDoc ignore) {
132             // if not found, continue on to the other name forms.
133
}
134
135
136         // The second easiest version to resolve is a direct map to a primitive type name
137
// or method signature. Check our name-to-class map for one of those.
138
Class JavaDoc resolvedClass = (Class JavaDoc) PRIMITIVE_CLASS_MAP.get(className);
139         if (resolvedClass != null) {
140             return resolvedClass;
141         }
142
143         // Class names in method signature have the format "Lfully.resolved.name;",
144
// so if it ends in a semicolon and begins with an "L", this must be in
145
// this format. Have the class loader try to load this. There are no other
146
// options if this fails, so just allow the class loader to throw the
147
// ClassNotFoundException.
148
if (className.endsWith(";") && className.startsWith("L")) {
149             // pick out the name portion
150
String JavaDoc typeName = className.substring(1, className.length() - 1);
151             // and delegate the loading to the class loader.
152
return classLoader.loadClass(typeName);
153         }
154
155         // All we have left now are the array types. Method signature array types
156
// have a series of leading "[" characters to specify the number of dimensions.
157
// The other array type we handle uses trailing "[]" for the dimensions, just
158
// like the Java language syntax.
159

160         // first check for the signature form ([[[[type).
161
if (className.charAt(0) == '[') {
162             // we have at least one array marker, now count how many leading '['s we have
163
// to get the dimension count.
164
int count = 0;
165             int nameLen = className.length();
166
167             while (count < nameLen && className.charAt(count) == '[') {
168                 count++;
169             }
170
171             // pull of the name subtype, which is everything after the last '['
172
String JavaDoc arrayTypeName = className.substring(count, className.length());
173             // resolve the type using a recursive call, which will load any of the primitive signature
174
// types as well as class names.
175
Class JavaDoc arrayType = loadClass(arrayTypeName, classLoader);
176
177             // Resolving array types require a little more work. The array classes are
178
// created dynamically when the first instance of a given dimension and type is
179
// created. We need to create one using reflection to do this.
180
return getArrayClass(arrayType, count);
181         }
182
183
184         // ok, last chance. Now check for an array specification in Java language
185
// syntax. This will be a type name followed by pairs of "[]" to indicate
186
// the number of dimensions.
187
if (className.endsWith("[]")) {
188             // get the base component class name and the arrayDimensions
189
int count = 0;
190             int position = className.length();
191
192             while (position > 1 && className.substring(position - 2, position).equals("[]")) {
193                 // count this dimension
194
count++;
195                 // and step back the probe position.
196
position -= 2;
197             }
198
199             // position now points at the location of the last successful test. This makes it
200
// easy to pick off the class name.
201

202             String JavaDoc typeName = className.substring(0, position);
203
204             // load the base type, again, doing this recursively
205
Class JavaDoc arrayType = loadClass(typeName, classLoader);
206             // and turn this into the class object
207
return getArrayClass(arrayType, count);
208         }
209
210         // We're out of options, just toss an exception over the wall.
211
if (classLoader instanceof MultiParentClassLoader) {
212             MultiParentClassLoader cl = (MultiParentClassLoader) classLoader;
213             throw new ClassNotFoundException JavaDoc("Could not load class " + className + " from classloader: " + cl.getId() + ", destroyed state: " + cl.isDestroyed());
214         }
215         throw new ClassNotFoundException JavaDoc("Could not load class " + className + " from unknown classloader; " + classLoader);
216     }
217
218
219     /**
220      * Map a class object back to a class name. The returned class object
221      * must be "round trippable", which means
222      * <p/>
223      * type == ClassLoading.loadClass(ClassLoading.getClassName(type), classLoader)
224      * <p/>
225      * must be true. To ensure this, the class name is always returned in
226      * method signature format.
227      *
228      * @param type The class object we convert into name form.
229      * @return A string representation of the class name, in method signature
230      * format.
231      */

232     public static String JavaDoc getClassName(Class JavaDoc type) {
233         StringBuffer JavaDoc name = new StringBuffer JavaDoc();
234
235         // we test these in reverse order from the resolution steps,
236
// first handling arrays, then primitive types, and finally
237
// "normal" class objects.
238

239         // First handle arrays. If a class is an array, the type is
240
// element stored at that level. So, for a 2-dimensional array
241
// of ints, the top-level type will be "[I". We need to loop
242
// down the hierarchy until we hit a non-array type.
243
while (type.isArray()) {
244             // add another array indicator at the front of the name,
245
// and continue with the next type.
246
name.append('[');
247             type = type.getComponentType();
248         }
249
250         // we're down to the base type. If this is a primitive, then
251
// we poke in the single-character type specifier.
252
if (type.isPrimitive()) {
253             name.append((String JavaDoc) CLASS_TO_SIGNATURE_MAP.get(type));
254         }
255         // a "normal" class. This gets expressing using the "Lmy.class.name;" syntax.
256
else {
257             name.append('L');
258             name.append(type.getName());
259             name.append(';');
260         }
261         return name.toString();
262     }
263
264     private static Class JavaDoc getArrayClass(Class JavaDoc type, int dimension) {
265         // Array.newInstance() requires an array of the requested number of dimensions
266
// that gives the size for each dimension. We just request 0 in each of the
267
// dimentions, which is not unlike a black hole sigularity.
268
int dimensions[] = new int[dimension];
269         // create an instance and return the associated class object.
270
return Array.newInstance(type, dimensions).getClass();
271     }
272
273     public static Set JavaDoc getAllTypes(Class JavaDoc type) {
274         Set JavaDoc allTypes = new LinkedHashSet JavaDoc();
275         allTypes.add(type);
276         allTypes.addAll(getAllSuperClasses(type));
277         allTypes.addAll(getAllInterfaces(type));
278         return allTypes;
279     }
280
281     private static Set JavaDoc getAllSuperClasses(Class JavaDoc clazz) {
282         Set JavaDoc allSuperClasses = new LinkedHashSet JavaDoc();
283         for (Class JavaDoc superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) {
284             allSuperClasses.add(superClass);
285         }
286         return allSuperClasses;
287     }
288
289     private static Set JavaDoc getAllInterfaces(Class JavaDoc clazz) {
290         Set JavaDoc allInterfaces = new LinkedHashSet JavaDoc();
291         LinkedList JavaDoc stack = new LinkedList JavaDoc();
292         stack.addAll(Arrays.asList(clazz.getInterfaces()));
293         while (!stack.isEmpty()) {
294             Class JavaDoc intf = (Class JavaDoc) stack.removeFirst();
295             if (!allInterfaces.contains(intf)) {
296                 allInterfaces.add(intf);
297                 stack.addAll(Arrays.asList(intf.getInterfaces()));
298             }
299         }
300         return allInterfaces;
301     }
302
303     public static Set JavaDoc reduceInterfaces(Set JavaDoc source) {
304         Class JavaDoc[] classes = (Class JavaDoc[]) source.toArray(new Class JavaDoc[source.size()]);
305         classes = reduceInterfaces(classes);
306         return new LinkedHashSet JavaDoc(Arrays.asList(classes));
307     }
308
309     /**
310      * If there are multiple interfaces, and some of them extend each other,
311      * eliminate the superclass in favor of the subclasses that extend them.
312      *
313      * If one of the entries is a class (not an interface), make sure it's
314      * the first one in the array. If more than one of the entries is a
315      * class, throws an IllegalArgumentException
316      *
317      * @param source the original list of interfaces
318      * @return the equal or smaller list of interfaces
319      */

320     public static Class JavaDoc[] reduceInterfaces(Class JavaDoc[] source) {
321         // use a copy of the sorce array
322
source = (Class JavaDoc[]) source.clone();
323
324         for (int leftIndex = 0; leftIndex < source.length-1; leftIndex++) {
325             Class JavaDoc left = source[leftIndex];
326             if(left == null) {
327                 continue;
328             }
329
330             for (int rightIndex = leftIndex +1; rightIndex < source.length; rightIndex++) {
331                 Class JavaDoc right = source[rightIndex];
332                 if(right == null) {
333                     continue;
334                 }
335
336                 if(left == right || right.isAssignableFrom(left)) {
337                     // right is the same as class or a sub class of left
338
source[rightIndex] = null;
339                 } else if(left.isAssignableFrom(right)) {
340                     // left is the same as class or a sub class of right
341
source[leftIndex] = null;
342
343                     // the left has been eliminated; move on to the next left
344
break;
345                 }
346             }
347         }
348
349         Class JavaDoc clazz = null;
350         for (int i = 0; i < source.length; i++) {
351             if (source[i] != null && !source[i].isInterface()) {
352                 if (clazz != null) {
353                     throw new IllegalArgumentException JavaDoc("Source contains two classes which are not subclasses of each other: " + clazz.getName() + ", " + source[i].getName());
354                 }
355                 clazz = source[i];
356                 source[i] = null;
357             }
358         }
359
360         List JavaDoc list = new ArrayList JavaDoc(source.length);
361         if (clazz != null) list.add(clazz);
362         for (int i = 0; i < source.length; i++) {
363             if(source[i] != null) {
364                 list.add(source[i]);
365             }
366         }
367         return (Class JavaDoc[]) list.toArray(new Class JavaDoc[list.size()]);
368     }
369 }
370
371
Popular Tags