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