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 }