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 }