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