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