1 /*
   2  * Copyright (c) 1996, 2006, 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.lang.reflect.Method;
  32 import java.rmi.MarshalException;
  33 import java.rmi.Remote;
  34 import java.rmi.RemoteException;
  35 import java.rmi.ServerException;
  36 import java.rmi.UnmarshalException;
  37 import java.rmi.server.Operation;
  38 import java.rmi.server.RemoteCall;
  39 import java.rmi.server.RemoteObject;
  40 import java.rmi.server.RemoteRef;
  41 import java.security.AccessController;
  42 import sun.rmi.runtime.Log;
  43 import sun.rmi.transport.Connection;
  44 import sun.rmi.transport.LiveRef;
  45 import sun.rmi.transport.StreamRemoteCall;
  46 import sun.security.action.GetBooleanAction;
  47 
  48 /**
  49  * NOTE: There is a JDK-internal dependency on the existence of this
  50  * class's getLiveRef method (as it is inherited by UnicastRef2) in
  51  * the implementation of javax.management.remote.rmi.RMIConnector.
  52  **/
  53 public class UnicastRef implements RemoteRef {
  54 
  55     /**
  56      * Client-side transport log.
  57      */
  58     public static final Log clientRefLog =
  59         Log.getLog("sun.rmi.client.ref", "transport",  Util.logLevel);
  60 
  61     /**
  62      * Client-side call log.
  63      */
  64     public static final Log clientCallLog =
  65         Log.getLog("sun.rmi.client.call", "RMI",
  66                    AccessController.doPrivileged(
  67                        new GetBooleanAction("sun.rmi.client.logCalls")));
  68     private static final long serialVersionUID = 8258372400816541186L;
  69 
  70     protected LiveRef ref;
  71 
  72     /**
  73      * Create a new (empty) Unicast remote reference.
  74      */
  75     public UnicastRef() {
  76     }
  77 
  78     /**
  79      * Create a new Unicast RemoteRef.
  80      */
  81     public UnicastRef(LiveRef liveRef) {
  82         ref = liveRef;
  83     }
  84 
  85     /**
  86      * Returns the current value of this UnicastRef's underlying
  87      * LiveRef.
  88      *
  89      * NOTE: There is a JDK-internal dependency on the existence of
  90      * this method (as it is inherited by UnicastRef) in the
  91      * implementation of javax.management.remote.rmi.RMIConnector.
  92      **/
  93     public LiveRef getLiveRef() {
  94         return ref;
  95     }
  96 
  97     /**
  98      * Invoke a method. This form of delegating method invocation
  99      * to the reference allows the reference to take care of
 100      * setting up the connection to the remote host, marshalling
 101      * some representation for the method and parameters, then
 102      * communicating the method invocation to the remote host.
 103      * This method either returns the result of a method invocation
 104      * on the remote object which resides on the remote host or
 105      * throws a RemoteException if the call failed or an
 106      * application-level exception if the remote invocation throws
 107      * an exception.
 108      *
 109      * @param obj the proxy for the remote object
 110      * @param method the method to be invoked
 111      * @param params the parameter list
 112      * @param opnum  a hash that may be used to represent the method
 113      * @since 1.2
 114      */
 115     public Object invoke(Remote obj,
 116                          Method method,
 117                          Object[] params,
 118                          long opnum)
 119         throws Exception
 120     {
 121         if (clientRefLog.isLoggable(Log.VERBOSE)) {
 122             clientRefLog.log(Log.VERBOSE, "method: " + method);
 123         }
 124 
 125         if (clientCallLog.isLoggable(Log.VERBOSE)) {
 126             logClientCall(obj, method);
 127         }
 128 
 129         Connection conn = ref.getChannel().newConnection();
 130         RemoteCall call = null;
 131         boolean reuse = true;
 132 
 133         /* If the call connection is "reused" early, remember not to
 134          * reuse again.
 135          */
 136         boolean alreadyFreed = false;
 137 
 138         try {
 139             if (clientRefLog.isLoggable(Log.VERBOSE)) {
 140                 clientRefLog.log(Log.VERBOSE, "opnum = " + opnum);
 141             }
 142 
 143             // create call context
 144             call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);
 145 
 146             // marshal parameters
 147             try {
 148                 ObjectOutput out = call.getOutputStream();
 149                 marshalCustomCallData(out);
 150                 Class<?>[] types = method.getParameterTypes();
 151                 for (int i = 0; i < types.length; i++) {
 152                     marshalValue(types[i], params[i], out);
 153                 }
 154             } catch (IOException e) {
 155                 clientRefLog.log(Log.BRIEF,
 156                     "IOException marshalling arguments: ", e);
 157                 throw new MarshalException("error marshalling arguments", e);
 158             }
 159 
 160             // unmarshal return
 161             call.executeCall();
 162 
 163             try {
 164                 Class<?> rtype = method.getReturnType();
 165                 if (rtype == void.class)
 166                     return null;
 167                 ObjectInput in = call.getInputStream();
 168 
 169                 /* StreamRemoteCall.done() does not actually make use
 170                  * of conn, therefore it is safe to reuse this
 171                  * connection before the dirty call is sent for
 172                  * registered refs.
 173                  */
 174                 Object returnValue = unmarshalValue(rtype, in);
 175 
 176                 /* we are freeing the connection now, do not free
 177                  * again or reuse.
 178                  */
 179                 alreadyFreed = true;
 180 
 181                 /* if we got to this point, reuse must have been true. */
 182                 clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
 183 
 184                 /* Free the call's connection early. */
 185                 ref.getChannel().free(conn, true);
 186 
 187                 return returnValue;
 188 
 189             } catch (IOException e) {
 190                 clientRefLog.log(Log.BRIEF,
 191                                  "IOException unmarshalling return: ", e);
 192                 throw new UnmarshalException("error unmarshalling return", e);
 193             } catch (ClassNotFoundException e) {
 194                 clientRefLog.log(Log.BRIEF,
 195                     "ClassNotFoundException unmarshalling return: ", e);
 196 
 197                 throw new UnmarshalException("error unmarshalling return", e);
 198             } finally {
 199                 try {
 200                     call.done();
 201                 } catch (IOException e) {
 202                     /* WARNING: If the conn has been reused early,
 203                      * then it is too late to recover from thrown
 204                      * IOExceptions caught here. This code is relying
 205                      * on StreamRemoteCall.done() not actually
 206                      * throwing IOExceptions.
 207                      */
 208                     reuse = false;
 209                 }
 210             }
 211 
 212         } catch (RuntimeException e) {
 213             /*
 214              * Need to distinguish between client (generated by the
 215              * invoke method itself) and server RuntimeExceptions.
 216              * Client side RuntimeExceptions are likely to have
 217              * corrupted the call connection and those from the server
 218              * are not likely to have done so.  If the exception came
 219              * from the server the call connection should be reused.
 220              */
 221             if ((call == null) ||
 222                 (((StreamRemoteCall) call).getServerException() != e))
 223             {
 224                 reuse = false;
 225             }
 226             throw e;
 227 
 228         } catch (RemoteException e) {
 229             /*
 230              * Some failure during call; assume connection cannot
 231              * be reused.  Must assume failure even if ServerException
 232              * or ServerError occurs since these failures can happen
 233              * during parameter deserialization which would leave
 234              * the connection in a corrupted state.
 235              */
 236             reuse = false;
 237             throw e;
 238 
 239         } catch (Error e) {
 240             /* If errors occurred, the connection is most likely not
 241              *  reusable.
 242              */
 243             reuse = false;
 244             throw e;
 245 
 246         } finally {
 247 
 248             /* alreadyFreed ensures that we do not log a reuse that
 249              * may have already happened.
 250              */
 251             if (!alreadyFreed) {
 252                 if (clientRefLog.isLoggable(Log.BRIEF)) {
 253                     clientRefLog.log(Log.BRIEF, "free connection (reuse = " +
 254                                            reuse + ")");
 255                 }
 256                 ref.getChannel().free(conn, reuse);
 257             }
 258         }
 259     }
 260 
 261     protected void marshalCustomCallData(ObjectOutput out) throws IOException
 262     {}
 263 
 264     /**
 265      * Marshal value to an ObjectOutput sink using RMI's serialization
 266      * format for parameters or return values.
 267      */
 268     protected static void marshalValue(Class<?> type, Object value,
 269                                        ObjectOutput out)
 270         throws IOException
 271     {
 272         if (type.isPrimitive()) {
 273             if (type == int.class) {
 274                 out.writeInt(((Integer) value).intValue());
 275             } else if (type == boolean.class) {
 276                 out.writeBoolean(((Boolean) value).booleanValue());
 277             } else if (type == byte.class) {
 278                 out.writeByte(((Byte) value).byteValue());
 279             } else if (type == char.class) {
 280                 out.writeChar(((Character) value).charValue());
 281             } else if (type == short.class) {
 282                 out.writeShort(((Short) value).shortValue());
 283             } else if (type == long.class) {
 284                 out.writeLong(((Long) value).longValue());
 285             } else if (type == float.class) {
 286                 out.writeFloat(((Float) value).floatValue());
 287             } else if (type == double.class) {
 288                 out.writeDouble(((Double) value).doubleValue());
 289             } else {
 290                 throw new Error("Unrecognized primitive type: " + type);
 291             }
 292         } else {
 293             out.writeObject(value);
 294         }
 295     }
 296 
 297     /**
 298      * Unmarshal value from an ObjectInput source using RMI's serialization
 299      * format for parameters or return values.
 300      */
 301     protected static Object unmarshalValue(Class<?> type, ObjectInput in)
 302         throws IOException, ClassNotFoundException
 303     {
 304         if (type.isPrimitive()) {
 305             if (type == int.class) {
 306                 return Integer.valueOf(in.readInt());
 307             } else if (type == boolean.class) {
 308                 return Boolean.valueOf(in.readBoolean());
 309             } else if (type == byte.class) {
 310                 return Byte.valueOf(in.readByte());
 311             } else if (type == char.class) {
 312                 return Character.valueOf(in.readChar());
 313             } else if (type == short.class) {
 314                 return Short.valueOf(in.readShort());
 315             } else if (type == long.class) {
 316                 return Long.valueOf(in.readLong());
 317             } else if (type == float.class) {
 318                 return Float.valueOf(in.readFloat());
 319             } else if (type == double.class) {
 320                 return Double.valueOf(in.readDouble());
 321             } else {
 322                 throw new Error("Unrecognized primitive type: " + type);
 323             }
 324         } else {
 325             return in.readObject();
 326         }
 327     }
 328 
 329     /**
 330      * Create an appropriate call object for a new call on this object.
 331      * Passing operation array and index, allows the stubs generator to
 332      * assign the operation indexes and interpret them. The RemoteRef
 333      * may need the operation to encode in for the call.
 334      */
 335     public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum,
 336                               long hash)
 337         throws RemoteException
 338     {
 339         clientRefLog.log(Log.BRIEF, "get connection");
 340 
 341         Connection conn = ref.getChannel().newConnection();
 342         try {
 343             clientRefLog.log(Log.VERBOSE, "create call context");
 344 
 345             /* log information about the outgoing call */
 346             if (clientCallLog.isLoggable(Log.VERBOSE)) {
 347                 logClientCall(obj, ops[opnum]);
 348             }
 349 
 350             RemoteCall call =
 351                 new StreamRemoteCall(conn, ref.getObjID(), opnum, hash);
 352             try {
 353                 marshalCustomCallData(call.getOutputStream());
 354             } catch (IOException e) {
 355                 throw new MarshalException("error marshaling " +
 356                                            "custom call data");
 357             }
 358             return call;
 359         } catch (RemoteException e) {
 360             ref.getChannel().free(conn, false);
 361             throw e;
 362         }
 363     }
 364 
 365     /**
 366      * Invoke makes the remote call present in the RemoteCall object.
 367      *
 368      * Invoke will raise any "user" exceptions which
 369      * should pass through and not be caught by the stub.  If any
 370      * exception is raised during the remote invocation, invoke should
 371      * take care of cleaning up the connection before raising the
 372      * "user" or remote exception.
 373      */
 374     public void invoke(RemoteCall call) throws Exception {
 375         try {
 376             clientRefLog.log(Log.VERBOSE, "execute call");
 377 
 378             call.executeCall();
 379 
 380         } catch (RemoteException e) {
 381             /*
 382              * Call did not complete; connection can't be reused.
 383              */
 384             clientRefLog.log(Log.BRIEF, "exception: ", e);
 385             free(call, false);
 386             throw e;
 387 
 388         } catch (Error e) {
 389             /* If errors occurred, the connection is most likely not
 390              *  reusable.
 391              */
 392             clientRefLog.log(Log.BRIEF, "error: ", e);
 393             free(call, false);
 394             throw e;
 395 
 396         } catch (RuntimeException e) {
 397             /*
 398              * REMIND: Since runtime exceptions are no longer wrapped,
 399              * we can't assue that the connection was left in
 400              * a reusable state. Is this okay?
 401              */
 402             clientRefLog.log(Log.BRIEF, "exception: ", e);
 403             free(call, false);
 404             throw e;
 405 
 406         } catch (Exception e) {
 407             /*
 408              * Assume that these other exceptions are user exceptions
 409              * and leave the connection in a reusable state.
 410              */
 411             clientRefLog.log(Log.BRIEF, "exception: ", e);
 412             free(call, true);
 413             /* reraise user (and unknown) exceptions. */
 414             throw e;
 415         }
 416 
 417         /*
 418          * Don't free the connection if an exception did not
 419          * occur because the stub needs to unmarshal the
 420          * return value. The connection will be freed
 421          * by a call to the "done" method.
 422          */
 423     }
 424 
 425     /**
 426      * Private method to free a connection.
 427      */
 428     private void free(RemoteCall call, boolean reuse) throws RemoteException {
 429         Connection conn = ((StreamRemoteCall)call).getConnection();
 430         ref.getChannel().free(conn, reuse);
 431     }
 432 
 433     /**
 434      * Done should only be called if the invoke returns successfully
 435      * (non-exceptionally) to the stub. It allows the remote reference to
 436      * clean up (or reuse) the connection.
 437      */
 438     public void done(RemoteCall call) throws RemoteException {
 439 
 440         /* Done only uses the connection inside the call to obtain the
 441          * channel the connection uses.  Once all information is read
 442          * from the connection, the connection may be freed.
 443          */
 444         clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
 445 
 446         /* Free the call connection early. */
 447         free(call, true);
 448 
 449         try {
 450             call.done();
 451         } catch (IOException e) {
 452             /* WARNING: If the conn has been reused early, then it is
 453              * too late to recover from thrown IOExceptions caught
 454              * here. This code is relying on StreamRemoteCall.done()
 455              * not actually throwing IOExceptions.
 456              */
 457         }
 458     }
 459 
 460     /**
 461      * Log the details of an outgoing call.  The method parameter is either of
 462      * type java.lang.reflect.Method or java.rmi.server.Operation.
 463      */
 464     void logClientCall(Object obj, Object method) {
 465         clientCallLog.log(Log.VERBOSE, "outbound call: " +
 466             ref + " : " + obj.getClass().getName() +
 467             ref.getObjID().toString() + ": " + method);
 468     }
 469 
 470     /**
 471      * Returns the class of the ref type to be serialized
 472      */
 473     public String getRefClass(ObjectOutput out) {
 474         return "UnicastRef";
 475     }
 476 
 477     /**
 478      * Write out external representation for remote ref.
 479      */
 480     public void writeExternal(ObjectOutput out) throws IOException {
 481         ref.write(out, false);
 482     }
 483 
 484     /**
 485      * Read in external representation for remote ref.
 486      * @exception ClassNotFoundException If the class for an object
 487      * being restored cannot be found.
 488      */
 489     public void readExternal(ObjectInput in)
 490         throws IOException, ClassNotFoundException
 491     {
 492         ref = LiveRef.read(in, false);
 493     }
 494 
 495     //----------------------------------------------------------------------;
 496     /**
 497      * Method from object, forward from RemoteObject
 498      */
 499     public String remoteToString() {
 500         return Util.getUnqualifiedName(getClass()) + " [liveRef: " + ref + "]";
 501     }
 502 
 503     /**
 504      * default implementation of hashCode for remote objects
 505      */
 506     public int remoteHashCode() {
 507         return ref.hashCode();
 508     }
 509 
 510     /** default implementation of equals for remote objects
 511      */
 512     public boolean remoteEquals(RemoteRef sub) {
 513         if (sub instanceof UnicastRef)
 514             return ref.remoteEquals(((UnicastRef)sub).ref);
 515         return false;
 516     }
 517 }