1 /* 2 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.rmi.server; 26 27 import java.io.ByteArrayOutputStream; 28 import java.io.IOException; 29 import java.io.DataOutputStream; 30 import java.lang.reflect.Constructor; 31 import java.lang.reflect.InvocationHandler; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Proxy; 34 import java.lang.reflect.Method; 35 import java.rmi.Remote; 36 import java.rmi.RemoteException; 37 import java.rmi.StubNotFoundException; 38 import java.rmi.registry.Registry; 39 import java.rmi.server.LogStream; 40 import java.rmi.server.ObjID; 41 import java.rmi.server.RMIClientSocketFactory; 42 import java.rmi.server.RemoteObjectInvocationHandler; 43 import java.rmi.server.RemoteRef; 44 import java.rmi.server.RemoteStub; 45 import java.rmi.server.Skeleton; 46 import java.rmi.server.SkeletonNotFoundException; 47 import java.security.AccessController; 48 import java.security.MessageDigest; 49 import java.security.DigestOutputStream; 50 import java.security.NoSuchAlgorithmException; 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.Map; 54 import java.util.WeakHashMap; 55 import sun.rmi.registry.RegistryImpl; 56 import sun.rmi.runtime.Log; 57 import sun.rmi.transport.LiveRef; 58 import sun.rmi.transport.tcp.TCPEndpoint; 59 import sun.security.action.GetBooleanAction; 60 import sun.security.action.GetPropertyAction; 61 62 /** 63 * A utility class with static methods for creating stubs/proxies and 64 * skeletons for remote objects. 65 */ 66 public final class Util { 67 68 /** "server" package log level */ 69 static final int logLevel = LogStream.parseLevel( 70 AccessController.doPrivileged( 71 new GetPropertyAction("sun.rmi.server.logLevel"))); 72 73 /** server reference log */ 74 public static final Log serverRefLog = 75 Log.getLog("sun.rmi.server.ref", "transport", Util.logLevel); 76 77 /** cached value of property java.rmi.server.ignoreStubClasses */ 78 private static final boolean ignoreStubClasses = 79 AccessController.doPrivileged( 80 new GetBooleanAction("java.rmi.server.ignoreStubClasses")). 81 booleanValue(); 82 83 /** cache of impl classes that have no corresponding stub class */ 84 private static final Map<Class<?>, Void> withoutStubs = 85 Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>(11)); 86 87 /** parameter types for stub constructor */ 88 private static final Class[] stubConsParamTypes = { RemoteRef.class }; 89 90 private Util() { 91 } 92 93 /** 94 * Returns a proxy for the specified implClass. 95 * 96 * If both of the following criteria is satisfied, a dynamic proxy for 97 * the specified implClass is returned (otherwise a RemoteStub instance 98 * for the specified implClass is returned): 99 * 100 * a) either the property java.rmi.server.ignoreStubClasses is true or 101 * a pregenerated stub class does not exist for the impl class, and 102 * b) forceStubUse is false. 103 * 104 * If the above criteria are satisfied, this method constructs a 105 * dynamic proxy instance (that implements the remote interfaces of 106 * implClass) constructed with a RemoteObjectInvocationHandler instance 107 * constructed with the clientRef. 108 * 109 * Otherwise, this method loads the pregenerated stub class (which 110 * extends RemoteStub and implements the remote interfaces of 111 * implClass) and constructs an instance of the pregenerated stub 112 * class with the clientRef. 113 * 114 * @param implClass the class to obtain remote interfaces from 115 * @param clientRef the remote ref to use in the invocation handler 116 * @param forceStubUse if true, forces creation of a RemoteStub 117 * @throws IllegalArgumentException if implClass implements illegal 118 * remote interfaces 119 * @throws StubNotFoundException if problem locating/creating stub or 120 * creating the dynamic proxy instance 121 **/ 122 public static Remote createProxy(Class implClass, 123 RemoteRef clientRef, 124 boolean forceStubUse) 125 throws StubNotFoundException 126 { 127 Class remoteClass; 128 129 try { 130 remoteClass = getRemoteClass(implClass); 131 } catch (ClassNotFoundException ex ) { 132 throw new StubNotFoundException( 133 "object does not implement a remote interface: " + 134 implClass.getName()); 135 } 136 137 if (forceStubUse || 138 !(ignoreStubClasses || !stubClassExists(remoteClass))) 139 { 140 return createStub(remoteClass, clientRef); 141 } 142 143 ClassLoader loader = implClass.getClassLoader(); 144 Class[] interfaces = getRemoteInterfaces(implClass); 145 InvocationHandler handler = 146 new RemoteObjectInvocationHandler(clientRef); 147 148 /* REMIND: private remote interfaces? */ 149 150 try { 151 return (Remote) Proxy.newProxyInstance(loader, 152 interfaces, 153 handler); 154 } catch (IllegalArgumentException e) { 155 throw new StubNotFoundException("unable to create proxy", e); 156 } 157 } 158 159 /** 160 * Returns true if a stub class for the given impl class can be loaded, 161 * otherwise returns false. 162 * 163 * @param remoteClass the class to obtain remote interfaces from 164 */ 165 private static boolean stubClassExists(Class remoteClass) { 166 if (!withoutStubs.containsKey(remoteClass)) { 167 try { 168 Class.forName(remoteClass.getName() + "_Stub", 169 false, 170 remoteClass.getClassLoader()); 171 return true; 172 173 } catch (ClassNotFoundException cnfe) { 174 withoutStubs.put(remoteClass, null); 175 } 176 } 177 return false; 178 } 179 180 /* 181 * Returns the class/superclass that implements the remote interface. 182 * @throws ClassNotFoundException if no class is found to have a 183 * remote interface 184 */ 185 private static Class getRemoteClass(Class cl) 186 throws ClassNotFoundException 187 { 188 while (cl != null) { 189 Class[] interfaces = cl.getInterfaces(); 190 for (int i = interfaces.length -1; i >= 0; i--) { 191 if (Remote.class.isAssignableFrom(interfaces[i])) 192 return cl; // this class implements remote object 193 } 194 cl = cl.getSuperclass(); 195 } 196 throw new ClassNotFoundException( 197 "class does not implement java.rmi.Remote"); 198 } 199 200 /** 201 * Returns an array containing the remote interfaces implemented 202 * by the given class. 203 * 204 * @param remoteClass the class to obtain remote interfaces from 205 * @throws IllegalArgumentException if remoteClass implements 206 * any illegal remote interfaces 207 * @throws NullPointerException if remoteClass is null 208 */ 209 private static Class[] getRemoteInterfaces(Class remoteClass) { 210 ArrayList<Class<?>> list = new ArrayList<Class<?>>(); 211 getRemoteInterfaces(list, remoteClass); 212 return list.toArray(new Class<?>[list.size()]); 213 } 214 215 /** 216 * Fills the given array list with the remote interfaces implemented 217 * by the given class. 218 * 219 * @throws IllegalArgumentException if the specified class implements 220 * any illegal remote interfaces 221 * @throws NullPointerException if the specified class or list is null 222 */ 223 private static void getRemoteInterfaces(ArrayList<Class<?>> list, Class cl) { 224 Class superclass = cl.getSuperclass(); 225 if (superclass != null) { 226 getRemoteInterfaces(list, superclass); 227 } 228 229 Class[] interfaces = cl.getInterfaces(); 230 for (int i = 0; i < interfaces.length; i++) { 231 Class intf = interfaces[i]; 232 /* 233 * If it is a remote interface (if it extends from 234 * java.rmi.Remote) and is not already in the list, 235 * then add the interface to the list. 236 */ 237 if (Remote.class.isAssignableFrom(intf)) { 238 if (!(list.contains(intf))) { 239 Method[] methods = intf.getMethods(); 240 for (int j = 0; j < methods.length; j++) { 241 checkMethod(methods[j]); 242 } 243 list.add(intf); 244 } 245 } 246 } 247 } 248 249 /** 250 * Verifies that the supplied method has at least one declared exception 251 * type that is RemoteException or one of its superclasses. If not, 252 * then this method throws IllegalArgumentException. 253 * 254 * @throws IllegalArgumentException if m is an illegal remote method 255 */ 256 private static void checkMethod(Method m) { 257 Class<?>[] ex = m.getExceptionTypes(); 258 for (int i = 0; i < ex.length; i++) { 259 if (ex[i].isAssignableFrom(RemoteException.class)) 260 return; 261 } 262 throw new IllegalArgumentException( 263 "illegal remote method encountered: " + m); 264 } 265 266 /** 267 * Creates a RemoteStub instance for the specified class, constructed 268 * with the specified RemoteRef. The supplied class must be the most 269 * derived class in the remote object's superclass chain that 270 * implements a remote interface. The stub class name is the name of 271 * the specified remoteClass with the suffix "_Stub". The loading of 272 * the stub class is initiated from class loader of the specified class 273 * (which may be the bootstrap class loader). 274 **/ 275 private static RemoteStub createStub(Class remoteClass, RemoteRef ref) 276 throws StubNotFoundException 277 { 278 String stubname = remoteClass.getName() + "_Stub"; 279 280 /* Make sure to use the local stub loader for the stub classes. 281 * When loaded by the local loader the load path can be 282 * propagated to remote clients, by the MarshalOutputStream/InStream 283 * pickle methods 284 */ 285 try { 286 Class<?> stubcl = 287 Class.forName(stubname, false, remoteClass.getClassLoader()); 288 Constructor cons = stubcl.getConstructor(stubConsParamTypes); 289 return (RemoteStub) cons.newInstance(new Object[] { ref }); 290 291 } catch (ClassNotFoundException e) { 292 throw new StubNotFoundException( 293 "Stub class not found: " + stubname, e); 294 } catch (NoSuchMethodException e) { 295 throw new StubNotFoundException( 296 "Stub class missing constructor: " + stubname, e); 297 } catch (InstantiationException e) { 298 throw new StubNotFoundException( 299 "Can't create instance of stub class: " + stubname, e); 300 } catch (IllegalAccessException e) { 301 throw new StubNotFoundException( 302 "Stub class constructor not public: " + stubname, e); 303 } catch (InvocationTargetException e) { 304 throw new StubNotFoundException( 305 "Exception creating instance of stub class: " + stubname, e); 306 } catch (ClassCastException e) { 307 throw new StubNotFoundException( 308 "Stub class not instance of RemoteStub: " + stubname, e); 309 } 310 } 311 312 /** 313 * Locate and return the Skeleton for the specified remote object 314 */ 315 static Skeleton createSkeleton(Remote object) 316 throws SkeletonNotFoundException 317 { 318 Class cl; 319 try { 320 cl = getRemoteClass(object.getClass()); 321 } catch (ClassNotFoundException ex ) { 322 throw new SkeletonNotFoundException( 323 "object does not implement a remote interface: " + 324 object.getClass().getName()); 325 } 326 327 // now try to load the skeleton based ont he name of the class 328 String skelname = cl.getName() + "_Skel"; 329 try { 330 Class skelcl = Class.forName(skelname, false, cl.getClassLoader()); 331 332 return (Skeleton)skelcl.newInstance(); 333 } catch (ClassNotFoundException ex) { 334 throw new SkeletonNotFoundException("Skeleton class not found: " + 335 skelname, ex); 336 } catch (InstantiationException ex) { 337 throw new SkeletonNotFoundException("Can't create skeleton: " + 338 skelname, ex); 339 } catch (IllegalAccessException ex) { 340 throw new SkeletonNotFoundException("No public constructor: " + 341 skelname, ex); 342 } catch (ClassCastException ex) { 343 throw new SkeletonNotFoundException( 344 "Skeleton not of correct class: " + skelname, ex); 345 } 346 } 347 348 /** 349 * Compute the "method hash" of a remote method. The method hash 350 * is a long containing the first 64 bits of the SHA digest from 351 * the UTF encoded string of the method name and descriptor. 352 */ 353 public static long computeMethodHash(Method m) { 354 long hash = 0; 355 ByteArrayOutputStream sink = new ByteArrayOutputStream(127); 356 try { 357 MessageDigest md = MessageDigest.getInstance("SHA"); 358 DataOutputStream out = new DataOutputStream( 359 new DigestOutputStream(sink, md)); 360 361 String s = getMethodNameAndDescriptor(m); 362 if (serverRefLog.isLoggable(Log.VERBOSE)) { 363 serverRefLog.log(Log.VERBOSE, 364 "string used for method hash: \"" + s + "\""); 365 } 366 out.writeUTF(s); 367 368 // use only the first 64 bits of the digest for the hash 369 out.flush(); 370 byte hasharray[] = md.digest(); 371 for (int i = 0; i < Math.min(8, hasharray.length); i++) { 372 hash += ((long) (hasharray[i] & 0xFF)) << (i * 8); 373 } 374 } catch (IOException ignore) { 375 /* can't happen, but be deterministic anyway. */ 376 hash = -1; 377 } catch (NoSuchAlgorithmException complain) { 378 throw new SecurityException(complain.getMessage()); 379 } 380 return hash; 381 } 382 383 /** 384 * Return a string consisting of the given method's name followed by 385 * its "method descriptor", as appropriate for use in the computation 386 * of the "method hash". 387 * 388 * See section 4.3.3 of The Java Virtual Machine Specification for 389 * the definition of a "method descriptor". 390 */ 391 private static String getMethodNameAndDescriptor(Method m) { 392 StringBuffer desc = new StringBuffer(m.getName()); 393 desc.append('('); 394 Class[] paramTypes = m.getParameterTypes(); 395 for (int i = 0; i < paramTypes.length; i++) { 396 desc.append(getTypeDescriptor(paramTypes[i])); 397 } 398 desc.append(')'); 399 Class returnType = m.getReturnType(); 400 if (returnType == void.class) { // optimization: handle void here 401 desc.append('V'); 402 } else { 403 desc.append(getTypeDescriptor(returnType)); 404 } 405 return desc.toString(); 406 } 407 408 /** 409 * Get the descriptor of a particular type, as appropriate for either 410 * a parameter or return type in a method descriptor. 411 */ 412 private static String getTypeDescriptor(Class type) { 413 if (type.isPrimitive()) { 414 if (type == int.class) { 415 return "I"; 416 } else if (type == boolean.class) { 417 return "Z"; 418 } else if (type == byte.class) { 419 return "B"; 420 } else if (type == char.class) { 421 return "C"; 422 } else if (type == short.class) { 423 return "S"; 424 } else if (type == long.class) { 425 return "J"; 426 } else if (type == float.class) { 427 return "F"; 428 } else if (type == double.class) { 429 return "D"; 430 } else if (type == void.class) { 431 return "V"; 432 } else { 433 throw new Error("unrecognized primitive type: " + type); 434 } 435 } else if (type.isArray()) { 436 /* 437 * According to JLS 20.3.2, the getName() method on Class does 438 * return the VM type descriptor format for array classes (only); 439 * using that should be quicker than the otherwise obvious code: 440 * 441 * return "[" + getTypeDescriptor(type.getComponentType()); 442 */ 443 return type.getName().replace('.', '/'); 444 } else { 445 return "L" + type.getName().replace('.', '/') + ";"; 446 } 447 } 448 449 /** 450 * Returns the binary name of the given type without package 451 * qualification. Nested types are treated no differently from 452 * top-level types, so for a nested type, the returned name will 453 * still be qualified with the simple name of its enclosing 454 * top-level type (and perhaps other enclosing types), the 455 * separator will be '$', etc. 456 **/ 457 public static String getUnqualifiedName(Class c) { 458 String binaryName = c.getName(); 459 return binaryName.substring(binaryName.lastIndexOf('.') + 1); 460 } 461 }