001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov, Vasily Zakharov
021: * @version $Revision: 1.1.2.3 $
022: */package org.apache.harmony.rmi.common;
023:
024: import java.io.IOException;
025:
026: import java.lang.reflect.Method;
027:
028: import java.net.InetAddress;
029: import java.net.ServerSocket;
030: import java.net.UnknownHostException;
031:
032: import java.rmi.Remote;
033: import java.rmi.RemoteException;
034:
035: import java.security.AccessController;
036: import java.security.PrivilegedActionException;
037: import java.security.PrivilegedExceptionAction;
038:
039: import java.util.Arrays;
040: import java.util.HashMap;
041: import java.util.Iterator;
042: import java.util.LinkedList;
043: import java.util.List;
044: import java.util.Map;
045:
046: import org.apache.harmony.rmi.internal.nls.Messages;
047:
048: /**
049: * Utility class for RMI implementation.
050: *
051: * This class cannot be instantiated.
052: *
053: * @author Mikhail A. Markov, Vasily Zakharov
054: * @version $Revision: 1.1.2.3 $
055: */
056: public final class RMIUtil {
057:
058: /**
059: * This class cannot be instantiated.
060: */
061: private RMIUtil() {
062: }
063:
064: /**
065: * Returns wrapping Object class for specified primitive class.
066: *
067: * @param cls
068: * Class to wrap.
069: *
070: * @return Wrapping Object class for <code>cls</code>, if <code>cls</code>
071: * is Object class itself (e. g. <code>Vector</code>),
072: * <code>cls</code> itself is returned, for primitive types
073: * (e. g. <code>int</code>) the respective wrapping Object class
074: * is returned (in case of <code>int</code>, {@link Integer}).
075: */
076: public static Class getWrappingClass(Class cls) {
077: if (cls == boolean.class) {
078: return Boolean.class;
079: } else if (cls == char.class) {
080: return Character.class;
081: } else if (cls == byte.class) {
082: return Byte.class;
083: } else if (cls == short.class) {
084: return Short.class;
085: } else if (cls == int.class) {
086: return Integer.class;
087: } else if (cls == long.class) {
088: return Long.class;
089: } else if (cls == float.class) {
090: return Float.class;
091: } else if (cls == double.class) {
092: return Double.class;
093: } else if (cls == void.class) {
094: return Void.class;
095: } else { // Object type.
096: return cls;
097: }
098: }
099:
100: /**
101: * Returns package name for the class.
102: *
103: * @param cls
104: * Class to get package name for.
105: *
106: * @return Package name of the class,
107: * or <code>null</code> if class does not belong to a package.
108: */
109: public static String getPackageName(Class cls) {
110: if (cls.isArray() || cls.isPrimitive()) {
111: return null;
112: }
113: String name = cls.getName();
114: int index = name.lastIndexOf('.');
115:
116: return ((index > 0) ? name.substring(0, index) : null);
117: }
118:
119: /**
120: * Returns canonical name for the class, e. g. full class name with
121: * package name, with <code>[]</code> appended to the end for array types.
122: * Handles local classes correctly.
123: *
124: * @param cls
125: * Class to get canonical name for.
126: *
127: * @return Canonical name of the class.
128: *
129: * @todo Remove completely for Java 5.0 in favor of
130: * <code>Class.getCanonicalName()</code>.
131: */
132: public static String getCanonicalName(Class cls) {
133: if (cls.isArray()) {
134: // Use recursion to create name for array class.
135: return (getCanonicalName(cls.getComponentType()) + "[]"); //$NON-NLS-1$
136: }
137: Class declaring = cls.getDeclaringClass();
138:
139: if (declaring != null) {
140: // Use recursion to create name for local classes.
141: return (getCanonicalName(declaring) + '.' + getSimpleName(cls));
142: }
143: return cls.getName();
144: }
145:
146: /**
147: * Returns short canonical name for the class, e. g. short class name
148: * without package name (but with '.' symbols for local classes),
149: * with <code>[]</code> appended to the end for array types.
150: *
151: * @param cls
152: * Class to get short canonical name for.
153: *
154: * @return Short canonical name of the class.
155: */
156: public static String getShortCanonicalName(Class cls) {
157: if (cls.isArray()) {
158: // Use recursion to create name for array class.
159: return (getShortCanonicalName(cls.getComponentType()) + "[]"); //$NON-NLS-1$
160: }
161:
162: // The last dot in full name separates class name from package name.
163: int index = cls.getName().lastIndexOf('.');
164:
165: // Canonical name uses dots to separate local class names.
166: String name = getCanonicalName(cls);
167:
168: return ((index > 0) ? name.substring(index + 1) : name);
169: }
170:
171: /**
172: * Returns short name for the class, e. g. short class name without
173: * package name (but with '$' symbols for local classes),
174: * with <code>[]</code> appended to the end for array types.
175: *
176: * @param cls
177: * Class to get short name for.
178: *
179: * @return Short name of the class.
180: */
181: public static String getShortName(Class cls) {
182: if (cls.isArray()) {
183: // Use recursion to create name for array class.
184: return (getShortName(cls.getComponentType()) + "[]"); //$NON-NLS-1$
185: }
186: String name = cls.getName();
187: int index = name.lastIndexOf('.');
188:
189: return ((index > 0) ? name.substring(index + 1) : name);
190: }
191:
192: /**
193: * Returns simple name for the class, e. g. short class name without
194: * package name or declaring class name, with <code>[]</code> appended
195: * to the end for array types.
196: *
197: * @param cls
198: * Class to get simple name for.
199: *
200: * @return Simple name of the class.
201: *
202: * @todo Remove completely for Java 5.0 in favor of
203: * <code>Class.getSimpleName()</code>.
204: */
205: public static String getSimpleName(Class cls) {
206: if (cls.isArray()) {
207: // Use recursion to create name for array class.
208: return (getSimpleName(cls.getComponentType()) + "[]"); //$NON-NLS-1$
209: }
210: String name = cls.getName();
211: Class declaring = cls.getDeclaringClass();
212:
213: if (declaring != null) {
214: // Use substringing to extract simple name of a local class.
215: return (name.substring(declaring.getName().length() + 1));
216: }
217: int index = name.lastIndexOf('.');
218:
219: return ((index > 0) ? name.substring(index + 1) : name);
220: }
221:
222: /**
223: * Returns system name for the class, e. g.
224: * <code>I</code> for <code>int</code>,
225: * <code>[[B</code> for <code>boolean[][]</code>,
226: * <code>[Ljava/lang/String;</code> for <code>String[]</code>.
227: *
228: * @param cls
229: * Class to get system name for.
230: *
231: * @return System name of the class.
232: */
233: public static String getSystemName(Class cls) {
234: if (cls == boolean.class) {
235: return "Z"; //$NON-NLS-1$
236: } else if (cls == char.class) {
237: return "C"; //$NON-NLS-1$
238: } else if (cls == byte.class) {
239: return "B"; //$NON-NLS-1$
240: } else if (cls == short.class) {
241: return "S"; //$NON-NLS-1$
242: } else if (cls == int.class) {
243: return "I"; //$NON-NLS-1$
244: } else if (cls == long.class) {
245: return "J"; //$NON-NLS-1$
246: } else if (cls == float.class) {
247: return "F"; //$NON-NLS-1$
248: } else if (cls == double.class) {
249: return "D"; //$NON-NLS-1$
250: } else if (cls == void.class) {
251: return "V"; //$NON-NLS-1$
252: } else { // Object type.
253: String className = cls.getName().replace('.', '/');
254:
255: // Add reference to non-array reference types.
256: return (cls.isArray() ? className : ('L' + className + ';'));
257: }
258: }
259:
260: /**
261: * Returns method descriptor as specified in section 4.3.3
262: * of Virtual Machine Specification.
263: *
264: * @param method
265: * Method to return descriptor for.
266: *
267: * @return Method descriptor.
268: */
269: public static String getMethodDescriptor(Method method) {
270: StringBuffer buffer = new StringBuffer().append('(');
271: Class[] parameters = method.getParameterTypes();
272:
273: for (int i = 0; i < parameters.length; i++) {
274: buffer.append(getSystemName(parameters[i]));
275: }
276: buffer.append(')')
277: .append(getSystemName(method.getReturnType()));
278:
279: return buffer.toString();
280: }
281:
282: /**
283: * Returns extended method descriptor,
284: * i. e. method name appended with method descriptor
285: * as specified in section 4.3.3
286: * of Virtual Machine Specification.
287: *
288: * @param method
289: * Method to return extended descriptor for.
290: *
291: * @return Extended method descriptor.
292: */
293: public static String getExtendedMethodDescriptor(Method method) {
294: return (method.getName() + getMethodDescriptor(method));
295: }
296:
297: /**
298: * Returns basic method signature (method name and full parameters
299: * class names) for the method.
300: *
301: * @param method
302: * Method to get basic signature for.
303: *
304: * @return Basic method signature (method name and full parameters
305: * class names) for the method. For example, for this particular
306: * method the long signature will be: <code>
307: * "getBasicMethodSignature(java.lang.reflect.Method)"</code>.
308: */
309: public static String getBasicMethodSignature(Method method) {
310: // Start with method name.
311: StringBuffer buffer = new StringBuffer().append(
312: method.getName()).append('(');
313: Class[] parameters = method.getParameterTypes();
314:
315: // Append names of parameter types.
316: for (int i = 0; i < parameters.length; i++) {
317: if (i > 0) {
318: buffer.append(", "); //$NON-NLS-1$
319: }
320: buffer.append(getCanonicalName(parameters[i]));
321: }
322: return buffer.append(')').toString();
323: }
324:
325: /**
326: * Returns long method signature (return type, method name and full
327: * parameters class names) for the method.
328: *
329: * @param method
330: * Method to get long signature for.
331: *
332: * @return Long method signature (return type, method name and full
333: * parameters class names) for the method. For example, for this
334: * particular method the long signature will be: <code>
335: * "java.lang.String
336: * getLongMethodSignature(java.lang.reflect.Method)"</code>.
337: */
338: public static String getLongMethodSignature(Method method) {
339: StringBuffer suffix = new StringBuffer();
340: Class cls = method.getReturnType();
341:
342: // Create signature suffix for array types.
343: while (cls.isArray()) {
344: suffix.append("[]"); //$NON-NLS-1$
345: cls = cls.getComponentType();
346: }
347: return (getCanonicalName(cls) + ' '
348: + getBasicMethodSignature(method) + suffix);
349: }
350:
351: /**
352: * Returns short method signature (without return type,
353: * declaring class name or parameters package names) for the method.
354: *
355: * @param method
356: * Method to get short signature for.
357: *
358: * @return Short method signature (without return type,
359: * declaring class name or parameters package names)
360: * for the method. For example, for this particular method
361: * the short signature will be:
362: * <code>"getShortMethodSignature(Method)"</code>.
363: */
364: public static String getShortMethodSignature(Method method) {
365: // Start with method name.
366: StringBuffer buffer = new StringBuffer(method.getName() + '(');
367: Class[] parameters = method.getParameterTypes();
368:
369: // Append short names of parameter types.
370: for (int i = 0; i < parameters.length; i++) {
371: buffer.append(((i > 0) ? ", " : "") //$NON-NLS-1$ //$NON-NLS-2$
372: + getShortCanonicalName(parameters[i]));
373: }
374: return buffer.append(')').toString();
375: }
376:
377: /**
378: * Validates remote interface.
379: * Particularly, checks that all methods throw {@link RemoteException}.
380: *
381: * @param iface
382: * Interface to validate.
383: *
384: * @return <code>true</code> if the specified class is a valid remote
385: * interface, <code>false</code> if the specified class is not
386: * a remote interface.
387: *
388: * @throws IllegalArgumentException
389: * If specified class is not an interface or if it implements
390: * {@link java.rmi.Remote} but is not a valid remote interface.
391: */
392: public static boolean checkRemoteInterface(Class iface)
393: throws IllegalArgumentException {
394: if (!iface.isInterface()) {
395: // This is not an interface.
396: // rmi.45={0} is not an interface
397: throw new IllegalArgumentException(Messages.getString(
398: "rmi.45", //$NON-NLS-1$
399: iface.getName()));
400: }
401:
402: if (!Remote.class.isAssignableFrom(iface)) {
403: // This is not a remote interface.
404: return false;
405: }
406:
407: // Extract all methods from the specified interface.
408: Method methods[] = iface.getMethods();
409:
410: methods: for (int i = 0; i < methods.length; i++) {
411: Method method = methods[i];
412:
413: // Extract thrown exceptions list from a particular method.
414: Iterator j = Arrays.asList(method.getExceptionTypes())
415: .iterator();
416:
417: while (j.hasNext()) {
418: // Search for exception that extends RemoteException.
419: if (((Class) j.next())
420: .isAssignableFrom(RemoteException.class))
421: continue methods;
422: }
423: // rmi.46={0} is not a valid remote interface: method {1} must throw java.rmi.RemoteException
424: throw new IllegalArgumentException(
425: Messages
426: .getString(
427: "rmi.46", iface.getName(), getBasicMethodSignature(method))); //$NON-NLS-1$
428: }
429: return true;
430: }
431:
432: /**
433: * Returns the list of implemented remote interfaces for the specified
434: * class.
435: *
436: * @param cls
437: * Class to return list of remote interfaces for.
438: *
439: * @return Array of remote interfaces implemented by the specified class.
440: * May be empty if the specified class is not a remote class
441: * or if <code>cls</code> is <code>null</code>.
442: *
443: * @throws IllegalArgumentException
444: * If class implements any invalid remote interfaces.
445: */
446: public static Class[] getRemoteInterfaces(Class cls)
447: throws IllegalArgumentException {
448: List interfaces = new LinkedList();
449:
450: for (; cls != null; cls = cls.getSuperclass()) {
451: // Get the list of interfaces the class implements.
452: Class[] interfacesArray = cls.getInterfaces();
453:
454: // Walk through all interfaces the class implements.
455: for (int i = 0; i < interfacesArray.length; i++) {
456: Class iface = interfacesArray[i];
457:
458: // Ignore duplicates and non-Remote interfaces.
459: if (!interfaces.contains(iface)
460: && checkRemoteInterface(iface)) {
461: // Add this interface to the interfaces table.
462: interfaces.add(iface);
463: }
464: }
465: }
466: return (Class[]) interfaces
467: .toArray(new Class[interfaces.size()]);
468: }
469:
470: /**
471: * Returns the string representation of the list of remote interfaces
472: * implemented by the specified class.
473: *
474: * @param cls
475: * Class to find remote interfaces for.
476: *
477: * @return List of remote interfaces for the specified class.
478: *
479: * @throws IllegalArgumentException
480: * If some error occurred while creating the list.
481: */
482: public static String[] getRemoteInterfacesNames(Class cls)
483: throws IllegalArgumentException {
484: Class[] interfaces = getRemoteInterfaces(cls);
485:
486: if ((interfaces == null) || (interfaces.length == 0)) {
487: return new String[0];
488: }
489: String[] interfStr = new String[interfaces.length];
490:
491: for (int i = 0; i < interfaces.length; ++i) {
492: interfStr[i] = interfaces[i].getName();
493: }
494: return interfStr;
495: }
496:
497: /**
498: * Returns a map containing all remote methods of the specified class
499: * (i. e. all methods contained in {@link Remote} interfaces implemented
500: * by the class). Hashes of methods are keys in this map.
501: *
502: * @param cls
503: * Class to list remote methods for.
504: *
505: * @return Map containing all the remote methods of the specified class
506: * and having method hashes as keys.
507: *
508: * @throws RMIHashException
509: * If error occurred while calculating method hash.
510: */
511: public static Map getRemoteMethods(Class cls)
512: throws RMIHashException {
513: Map map = new HashMap();
514:
515: for (; cls != null; cls = cls.getSuperclass()) {
516: Class[] interf = cls.getInterfaces();
517:
518: for (int i = 0; i < interf.length; ++i) {
519: if (!Remote.class.isAssignableFrom(interf[i])) {
520: continue;
521: }
522: Method[] m = interf[i].getMethods();
523:
524: for (int j = 0; j < m.length; ++j) {
525: // Calculate the hash for the method.
526: long hash = RMIHash.getMethodHash(m[j]);
527: map.put(new Long(hash), m[j]);
528: }
529: }
530: }
531: return map;
532: }
533:
534: /**
535: * Finds the superclass of the specified class that directly implements
536: * remote interface(s).
537: *
538: * @param cls
539: * Class to check for remote superclass.
540: *
541: * @return The class found.
542: *
543: * @throws IllegalArgumentException
544: * If the specified class is not remote.
545: */
546: public static Class getRemoteClass(Class cls)
547: throws IllegalArgumentException {
548: for (; cls != null; cls = cls.getSuperclass()) {
549: Class[] interfaces = cls.getInterfaces();
550:
551: for (int i = 0; i < interfaces.length; ++i) {
552: if (Remote.class.isAssignableFrom(interfaces[i])) {
553: return cls;
554: }
555: }
556: }
557: // rmi.47=The specified class is not remote
558: throw new IllegalArgumentException(Messages.getString("rmi.47")); //$NON-NLS-1$
559: }
560:
561: /**
562: * Returns <code>true</code> if the specified hostName is a local host
563: * and <code>false</code> otherwise.
564: *
565: * @param hostName
566: * The name of the host to check.
567: *
568: * @return <code>true</code> if the specified hostName is a local host
569: * and <code>false</code> otherwise.
570: *
571: * @throws UnknownHostException
572: * If the specified host name could not be resolved.
573: */
574: public static boolean isLocalHost(final String hostName)
575: throws UnknownHostException {
576: if (hostName == null) {
577: return true;
578: }
579:
580: try {
581: AccessController
582: .doPrivileged(new PrivilegedExceptionAction() {
583: public Object run()
584: throws UnknownHostException,
585: IOException {
586: // Resolve the host name.
587: InetAddress hostAddr = InetAddress
588: .getByName(hostName);
589:
590: // Check if this address is really local.
591: ServerSocket ss = new ServerSocket(0, 1,
592: hostAddr);
593:
594: try {
595: ss.close();
596: } catch (IOException ioe) {
597: // Ignoring.
598: }
599: return null;
600: }
601: });
602: } catch (PrivilegedActionException pae) {
603: Exception ex = pae.getException();
604:
605: if (ex instanceof UnknownHostException) {
606: throw (UnknownHostException) ex;
607: } else {
608: return false;
609: }
610: }
611: return true;
612: }
613:
614: /**
615: * Returns <code>true</code> if the first specified class loader
616: * is a parent class loader (or equal) to the second specified class loader
617: * and <code>false</code> otherwise.
618: *
619: * @param cl1
620: * First class loader.
621: *
622: * @param cl2
623: * Second class loader.
624: *
625: * @return <code>true</code> if the first class loader is a parent
626: * (or equal) to the second class loader.
627: */
628: public static boolean isParentLoader(ClassLoader cl1,
629: ClassLoader cl2) {
630: if (cl1 == null) {
631: // cl1 is a bootstrap or system class loader.
632: return true;
633: }
634:
635: for (; cl2 != null; cl2 = cl2.getParent()) {
636: if (cl1 == cl2) {
637: return true;
638: }
639: }
640: return false;
641: }
642: }
|