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