1 /**
2 *
3 * Copyright 2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * 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;
21 import java.util.HashMap;
22 import java.util.Set;
23 import java.util.LinkedHashSet;
24 import java.util.LinkedList;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.ArrayList;
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: 394430 $
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 PRIMITIVE_CLASS_MAP = new HashMap();
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 CLASS_TO_SIGNATURE_MAP = new HashMap();
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
88
89
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 loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {
117
118
119 if (className == null) {
120 throw new IllegalArgumentException("className is null");
121 }
122
123 if (classLoader == null) {
124 throw new IllegalArgumentException("classLoader is null");
125 }
126
127
128
129 try {
130 return classLoader.loadClass(className);
131 } catch (ClassNotFoundException ignore) {
132
133 }
134
135
136
137
138 Class resolvedClass = (Class) PRIMITIVE_CLASS_MAP.get(className);
139 if (resolvedClass != null) {
140 return resolvedClass;
141 }
142
143
144
145
146
147
148 if (className.endsWith(";") && className.startsWith("L")) {
149
150 String typeName = className.substring(1, className.length() - 1);
151
152 return classLoader.loadClass(typeName);
153 }
154
155
156
157
158
159
160
161 if (className.charAt(0) == '[') {
162
163
164 int count = 0;
165 int nameLen = className.length();
166
167 while (count < nameLen && className.charAt(count) == '[') {
168 count++;
169 }
170
171
172 String arrayTypeName = className.substring(count, className.length());
173
174
175 Class arrayType = loadClass(arrayTypeName, classLoader);
176
177
178
179
180 return getArrayClass(arrayType, count);
181 }
182
183
184
185
186
187 if (className.endsWith("[]")) {
188
189 int count = 0;
190 int position = className.length();
191
192 while (position > 1 && className.substring(position - 2, position).equals("[]")) {
193
194 count++;
195
196 position -= 2;
197 }
198
199
200
201
202 String typeName = className.substring(0, position);
203
204
205 Class arrayType = loadClass(typeName, classLoader);
206
207 return getArrayClass(arrayType, count);
208 }
209
210
211 if (classLoader instanceof MultiParentClassLoader) {
212 MultiParentClassLoader cl = (MultiParentClassLoader) classLoader;
213 throw new ClassNotFoundException("Could not load class " + className + " from classloader: " + cl.getId() + ", destroyed state: " + cl.isDestroyed());
214 }
215 throw new ClassNotFoundException("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 getClassName(Class type) {
233 StringBuffer name = new StringBuffer();
234
235
236
237
238
239
240
241
242
243 while (type.isArray()) {
244
245
246 name.append('[');
247 type = type.getComponentType();
248 }
249
250
251
252 if (type.isPrimitive()) {
253 name.append((String) CLASS_TO_SIGNATURE_MAP.get(type));
254 }
255
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 getArrayClass(Class type, int dimension) {
265
266
267
268 int dimensions[] = new int[dimension];
269
270 return Array.newInstance(type, dimensions).getClass();
271 }
272
273 public static Set getAllTypes(Class type) {
274 Set allTypes = new LinkedHashSet();
275 allTypes.add(type);
276 allTypes.addAll(getAllSuperClasses(type));
277 allTypes.addAll(getAllInterfaces(type));
278 return allTypes;
279 }
280
281 private static Set getAllSuperClasses(Class clazz) {
282 Set allSuperClasses = new LinkedHashSet();
283 for (Class superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) {
284 allSuperClasses.add(superClass);
285 }
286 return allSuperClasses;
287 }
288
289 private static Set getAllInterfaces(Class clazz) {
290 Set allInterfaces = new LinkedHashSet();
291 LinkedList stack = new LinkedList();
292 stack.addAll(Arrays.asList(clazz.getInterfaces()));
293 while (!stack.isEmpty()) {
294 Class intf = (Class) 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 reduceInterfaces(Set source) {
304 Class[] classes = (Class[]) source.toArray(new Class[source.size()]);
305 classes = reduceInterfaces(classes);
306 return new LinkedHashSet(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[] reduceInterfaces(Class[] source) {
321
322 source = (Class[]) source.clone();
323
324 for (int leftIndex = 0; leftIndex < source.length-1; leftIndex++) {
325 Class left = source[leftIndex];
326 if(left == null) {
327 continue;
328 }
329
330 for (int rightIndex = leftIndex +1; rightIndex < source.length; rightIndex++) {
331 Class right = source[rightIndex];
332 if(right == null) {
333 continue;
334 }
335
336 if(left == right || right.isAssignableFrom(left)) {
337
338 source[rightIndex] = null;
339 } else if(left.isAssignableFrom(right)) {
340
341 source[leftIndex] = null;
342
343
344 break;
345 }
346 }
347 }
348
349 Class 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("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 list = new ArrayList(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[]) list.toArray(new Class[list.size()]);
368 }
369 }
370