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 26 package sun.rmi.server; 27 28 import java.io.IOException; 29 import java.io.ObjectInput; 30 import java.io.ObjectOutput; 31 import java.io.PrintStream; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 import java.rmi.MarshalException; 35 import java.rmi.Remote; 36 import java.rmi.RemoteException; 37 import java.rmi.ServerError; 38 import java.rmi.ServerException; 39 import java.rmi.UnmarshalException; 40 import java.rmi.server.ExportException; 41 import java.rmi.server.RemoteCall; 42 import java.rmi.server.RemoteRef; 43 import java.rmi.server.RemoteStub; 44 import java.rmi.server.ServerNotActiveException; 45 import java.rmi.server.ServerRef; 46 import java.rmi.server.Skeleton; 47 import java.rmi.server.SkeletonNotFoundException; 48 import java.security.AccessController; 49 import java.security.PrivilegedAction; 50 import java.util.Collections; 51 import java.util.Date; 52 import java.util.HashMap; 53 import java.util.Map; 54 import java.util.WeakHashMap; 55 import sun.rmi.runtime.Log; 56 import sun.rmi.transport.LiveRef; 57 import sun.rmi.transport.Target; 58 import sun.rmi.transport.tcp.TCPTransport; 59 import sun.security.action.GetBooleanAction; 60 61 /** 62 * UnicastServerRef implements the remote reference layer server-side 63 * behavior for remote objects exported with the "UnicastRef" reference 64 * type. 65 * 66 * @author Ann Wollrath 67 * @author Roger Riggs 68 * @author Peter Jones 69 */ 70 @SuppressWarnings("deprecation") 71 public class UnicastServerRef extends UnicastRef 72 implements ServerRef, Dispatcher 73 { 74 /** value of server call log property */ 75 public static final boolean logCalls = AccessController.doPrivileged( 76 new GetBooleanAction("java.rmi.server.logCalls")); 77 78 /** server call log */ 79 public static final Log callLog = 80 Log.getLog("sun.rmi.server.call", "RMI", logCalls); 81 82 // use serialVersionUID from JDK 1.2.2 for interoperability 83 private static final long serialVersionUID = -7384275867073752268L; 84 85 /** flag to enable writing exceptions to System.err */ 86 private static final boolean wantExceptionLog = 87 AccessController.doPrivileged( 88 new GetBooleanAction("sun.rmi.server.exceptionTrace")); 89 90 private boolean forceStubUse = false; 91 92 /** 93 * flag to remove server-side stack traces before marshalling 94 * exceptions thrown by remote invocations to this VM 95 */ 96 private static final boolean suppressStackTraces = 97 AccessController.doPrivileged( 98 new GetBooleanAction( 99 "sun.rmi.server.suppressStackTraces")); 100 101 /** 102 * skeleton to dispatch remote calls through, for 1.1 stub protocol 103 * (may be null if stub class only uses 1.2 stub protocol) 104 */ 105 private transient Skeleton skel; 106 107 /** maps method hash to Method object for each remote method */ 108 private transient Map<Long,Method> hashToMethod_Map = null; 109 110 /** 111 * A weak hash map, mapping classes to hash maps that map method 112 * hashes to method objects. 113 **/ 114 private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps = 115 new HashToMethod_Maps(); 116 117 /** cache of impl classes that have no corresponding skeleton class */ 118 private static final Map<Class<?>,?> withoutSkeletons = 119 Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>()); 120 121 /** 122 * Create a new (empty) Unicast server remote reference. 123 */ 124 public UnicastServerRef() { 125 } 126 127 /** 128 * Construct a Unicast server remote reference for a specified 129 * liveRef. 130 */ 131 public UnicastServerRef(LiveRef ref) { 132 super(ref); 133 } 134 135 /** 136 * Construct a Unicast server remote reference to be exported 137 * on the specified port. 138 */ 139 public UnicastServerRef(int port) { 140 super(new LiveRef(port)); 141 } 142 143 /** 144 * Constructs a UnicastServerRef to be exported on an 145 * anonymous port (i.e., 0) and that uses a pregenerated stub class 146 * (NOT a dynamic proxy instance) if 'forceStubUse' is 'true'. 147 * 148 * This constructor is only called by the method 149 * UnicastRemoteObject.exportObject(Remote) passing 'true' for 150 * 'forceStubUse'. The UnicastRemoteObject.exportObject(Remote) method 151 * returns RemoteStub, so it must ensure that the stub for the 152 * exported object is an instance of a pregenerated stub class that 153 * extends RemoteStub (instead of an instance of a dynamic proxy class 154 * which is not an instance of RemoteStub). 155 **/ 156 public UnicastServerRef(boolean forceStubUse) { 157 this(0); 158 this.forceStubUse = forceStubUse; 159 } 160 161 /** 162 * With the addition of support for dynamic proxies as stubs, this 163 * method is obsolete because it returns RemoteStub instead of the more 164 * general Remote. It should not be called. It sets the 165 * 'forceStubUse' flag to true so that the stub for the exported object 166 * is forced to be an instance of the pregenerated stub class, which 167 * extends RemoteStub. 168 * 169 * Export this object, create the skeleton and stubs for this 170 * dispatcher. Create a stub based on the type of the impl, 171 * initialize it with the appropriate remote reference. Create the 172 * target defined by the impl, dispatcher (this) and stub. 173 * Export that target via the Ref. 174 **/ 175 public RemoteStub exportObject(Remote impl, Object data) 176 throws RemoteException 177 { 178 forceStubUse = true; 179 return (RemoteStub) exportObject(impl, data, false); 180 } 181 182 /** 183 * Export this object, create the skeleton and stubs for this 184 * dispatcher. Create a stub based on the type of the impl, 185 * initialize it with the appropriate remote reference. Create the 186 * target defined by the impl, dispatcher (this) and stub. 187 * Export that target via the Ref. 188 */ 189 public Remote exportObject(Remote impl, Object data, 190 boolean permanent) 191 throws RemoteException 192 { 193 Class<?> implClass = impl.getClass(); 194 Remote stub; 195 196 try { 197 stub = Util.createProxy(implClass, getClientRef(), forceStubUse); 198 } catch (IllegalArgumentException e) { 199 throw new ExportException( 200 "remote object implements illegal remote interface", e); 201 } 202 if (stub instanceof RemoteStub) { 203 setSkeleton(impl); 204 } 205 206 Target target = 207 new Target(impl, this, stub, ref.getObjID(), permanent); 208 ref.exportObject(target); 209 hashToMethod_Map = hashToMethod_Maps.get(implClass); 210 return stub; 211 } 212 213 /** 214 * Return the hostname of the current client. When called from a 215 * thread actively handling a remote method invocation the 216 * hostname of the client is returned. 217 * @exception ServerNotActiveException If called outside of servicing 218 * a remote method invocation. 219 */ 220 public String getClientHost() throws ServerNotActiveException { 221 return TCPTransport.getClientHost(); 222 } 223 224 /** 225 * Discovers and sets the appropriate skeleton for the impl. 226 */ 227 public void setSkeleton(Remote impl) throws RemoteException { 228 if (!withoutSkeletons.containsKey(impl.getClass())) { 229 try { 230 skel = Util.createSkeleton(impl); 231 } catch (SkeletonNotFoundException e) { 232 /* 233 * Ignore exception for skeleton class not found, because a 234 * skeleton class is not necessary with the 1.2 stub protocol. 235 * Remember that this impl's class does not have a skeleton 236 * class so we don't waste time searching for it again. 237 */ 238 withoutSkeletons.put(impl.getClass(), null); 239 } 240 } 241 } 242 243 /** 244 * Call to dispatch to the remote object (on the server side). 245 * The up-call to the server and the marshalling of return result 246 * (or exception) should be handled before returning from this 247 * method. 248 * @param obj the target remote object for the call 249 * @param call the "remote call" from which operation and 250 * method arguments can be obtained. 251 * @exception IOException If unable to marshal return result or 252 * release input or output streams 253 */ 254 public void dispatch(Remote obj, RemoteCall call) throws IOException { 255 // positive operation number in 1.1 stubs; 256 // negative version number in 1.2 stubs and beyond... 257 int num; 258 long op; 259 260 try { 261 // read remote call header 262 ObjectInput in; 263 try { 264 in = call.getInputStream(); 265 num = in.readInt(); 266 if (num >= 0) { 267 if (skel != null) { 268 oldDispatch(obj, call, num); 269 return; 270 } else { 271 throw new UnmarshalException( 272 "skeleton class not found but required " + 273 "for client version"); 274 } 275 } 276 op = in.readLong(); 277 } catch (Exception readEx) { 278 throw new UnmarshalException("error unmarshalling call header", 279 readEx); 280 } 281 282 /* 283 * Since only system classes (with null class loaders) will be on 284 * the execution stack during parameter unmarshalling for the 1.2 285 * stub protocol, tell the MarshalInputStream not to bother trying 286 * to resolve classes using its superclasses's default method of 287 * consulting the first non-null class loader on the stack. 288 */ 289 MarshalInputStream marshalStream = (MarshalInputStream) in; 290 marshalStream.skipDefaultResolveClass(); 291 292 Method method = hashToMethod_Map.get(op); 293 if (method == null) { 294 throw new UnmarshalException("unrecognized method hash: " + 295 "method not supported by remote object"); 296 } 297 298 // if calls are being logged, write out object id and operation 299 logCall(obj, method); 300 301 // unmarshal parameters 302 Class<?>[] types = method.getParameterTypes(); 303 Object[] params = new Object[types.length]; 304 305 try { 306 unmarshalCustomCallData(in); 307 for (int i = 0; i < types.length; i++) { 308 params[i] = unmarshalValue(types[i], in); 309 } 310 } catch (java.io.IOException e) { 311 throw new UnmarshalException( 312 "error unmarshalling arguments", e); 313 } catch (ClassNotFoundException e) { 314 throw new UnmarshalException( 315 "error unmarshalling arguments", e); 316 } finally { 317 call.releaseInputStream(); 318 } 319 320 // make upcall on remote object 321 Object result; 322 try { 323 result = method.invoke(obj, params); 324 } catch (InvocationTargetException e) { 325 throw e.getTargetException(); 326 } 327 328 // marshal return value 329 try { 330 ObjectOutput out = call.getResultStream(true); 331 Class<?> rtype = method.getReturnType(); 332 if (rtype != void.class) { 333 marshalValue(rtype, result, out); 334 } 335 } catch (IOException ex) { 336 throw new MarshalException("error marshalling return", ex); 337 /* 338 * This throw is problematic because when it is caught below, 339 * we attempt to marshal it back to the client, but at this 340 * point, a "normal return" has already been indicated, 341 * so marshalling an exception will corrupt the stream. 342 * This was the case with skeletons as well; there is no 343 * immediately obvious solution without a protocol change. 344 */ 345 } 346 } catch (Throwable e) { 347 logCallException(e); 348 349 ObjectOutput out = call.getResultStream(false); 350 if (e instanceof Error) { 351 e = new ServerError( 352 "Error occurred in server thread", (Error) e); 353 } else if (e instanceof RemoteException) { 354 e = new ServerException( 355 "RemoteException occurred in server thread", 356 (Exception) e); 357 } 358 if (suppressStackTraces) { 359 clearStackTraces(e); 360 } 361 out.writeObject(e); 362 } finally { 363 call.releaseInputStream(); // in case skeleton doesn't 364 call.releaseOutputStream(); 365 } 366 } 367 368 protected void unmarshalCustomCallData(ObjectInput in) 369 throws IOException, ClassNotFoundException 370 {} 371 372 /** 373 * Handle server-side dispatch using the RMI 1.1 stub/skeleton 374 * protocol, given a non-negative operation number that has 375 * already been read from the call stream. 376 * 377 * @param obj the target remote object for the call 378 * @param call the "remote call" from which operation and 379 * method arguments can be obtained. 380 * @param op the operation number 381 * @exception IOException if unable to marshal return result or 382 * release input or output streams 383 */ 384 public void oldDispatch(Remote obj, RemoteCall call, int op) 385 throws IOException 386 { 387 long hash; // hash for matching stub with skeleton 388 389 try { 390 // read remote call header 391 ObjectInput in; 392 try { 393 in = call.getInputStream(); 394 try { 395 Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel"); 396 if (clazz.isAssignableFrom(skel.getClass())) { 397 ((MarshalInputStream)in).useCodebaseOnly(); 398 } 399 } catch (ClassNotFoundException ignore) { } 400 hash = in.readLong(); 401 } catch (Exception readEx) { 402 throw new UnmarshalException("error unmarshalling call header", 403 readEx); 404 } 405 406 // if calls are being logged, write out object id and operation 407 logCall(obj, skel.getOperations()[op]); 408 unmarshalCustomCallData(in); 409 // dispatch to skeleton for remote object 410 skel.dispatch(obj, call, op, hash); 411 412 } catch (Throwable e) { 413 logCallException(e); 414 415 ObjectOutput out = call.getResultStream(false); 416 if (e instanceof Error) { 417 e = new ServerError( 418 "Error occurred in server thread", (Error) e); 419 } else if (e instanceof RemoteException) { 420 e = new ServerException( 421 "RemoteException occurred in server thread", 422 (Exception) e); 423 } 424 if (suppressStackTraces) { 425 clearStackTraces(e); 426 } 427 out.writeObject(e); 428 } finally { 429 call.releaseInputStream(); // in case skeleton doesn't 430 call.releaseOutputStream(); 431 } 432 } 433 434 /** 435 * Clear the stack trace of the given Throwable by replacing it with 436 * an empty StackTraceElement array, and do the same for all of its 437 * chained causative exceptions. 438 */ 439 public static void clearStackTraces(Throwable t) { 440 StackTraceElement[] empty = new StackTraceElement[0]; 441 while (t != null) { 442 t.setStackTrace(empty); 443 t = t.getCause(); 444 } 445 } 446 447 /** 448 * Log the details of an incoming call. The method parameter is either of 449 * type java.lang.reflect.Method or java.rmi.server.Operation. 450 */ 451 private void logCall(Remote obj, Object method) { 452 if (callLog.isLoggable(Log.VERBOSE)) { 453 String clientHost; 454 try { 455 clientHost = getClientHost(); 456 } catch (ServerNotActiveException snae) { 457 clientHost = "(local)"; // shouldn't happen 458 } 459 callLog.log(Log.VERBOSE, "[" + clientHost + ": " + 460 obj.getClass().getName() + 461 ref.getObjID().toString() + ": " + 462 method + "]"); 463 } 464 } 465 466 /** 467 * Log the exception detail of an incoming call. 468 */ 469 private void logCallException(Throwable e) { 470 // if calls are being logged, log them 471 if (callLog.isLoggable(Log.BRIEF)) { 472 String clientHost = ""; 473 try { 474 clientHost = "[" + getClientHost() + "] "; 475 } catch (ServerNotActiveException snae) { 476 } 477 callLog.log(Log.BRIEF, clientHost + "exception: ", e); 478 } 479 480 // write exceptions (only) to System.err if desired 481 if (wantExceptionLog) { 482 java.io.PrintStream log = System.err; 483 synchronized (log) { 484 log.println(); 485 log.println("Exception dispatching call to " + 486 ref.getObjID() + " in thread \"" + 487 Thread.currentThread().getName() + 488 "\" at " + (new Date()) + ":"); 489 e.printStackTrace(log); 490 } 491 } 492 } 493 494 /** 495 * Returns the class of the ref type to be serialized. 496 */ 497 public String getRefClass(ObjectOutput out) { 498 return "UnicastServerRef"; 499 } 500 501 /** 502 * Return the client remote reference for this remoteRef. 503 * In the case of a client RemoteRef "this" is the answer. 504 * For a server remote reference, a client side one will have to 505 * found or created. 506 */ 507 protected RemoteRef getClientRef() { 508 return new UnicastRef(ref); 509 } 510 511 /** 512 * Write out external representation for remote ref. 513 */ 514 public void writeExternal(ObjectOutput out) throws IOException { 515 } 516 517 /** 518 * Read in external representation for remote ref. 519 * @exception ClassNotFoundException If the class for an object 520 * being restored cannot be found. 521 */ 522 public void readExternal(ObjectInput in) 523 throws IOException, ClassNotFoundException 524 { 525 // object is re-exported elsewhere (e.g., by UnicastRemoteObject) 526 ref = null; 527 skel = null; 528 } 529 530 531 /** 532 * A weak hash map, mapping classes to hash maps that map method 533 * hashes to method objects. 534 **/ 535 private static class HashToMethod_Maps 536 extends WeakClassHashMap<Map<Long,Method>> 537 { 538 HashToMethod_Maps() {} 539 540 protected Map<Long,Method> computeValue(Class<?> remoteClass) { 541 Map<Long,Method> map = new HashMap<>(); 542 for (Class<?> cl = remoteClass; 543 cl != null; 544 cl = cl.getSuperclass()) 545 { 546 for (Class<?> intf : cl.getInterfaces()) { 547 if (Remote.class.isAssignableFrom(intf)) { 548 for (Method method : intf.getMethods()) { 549 final Method m = method; 550 /* 551 * Set this Method object to override language 552 * access checks so that the dispatcher can invoke 553 * methods from non-public remote interfaces. 554 */ 555 AccessController.doPrivileged( 556 new PrivilegedAction<Void>() { 557 public Void run() { 558 m.setAccessible(true); 559 return null; 560 } 561 }); 562 map.put(Util.computeMethodHash(m), m); 563 } 564 } 565 } 566 } 567 return map; 568 } 569 } 570 }