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

Save This Page
Home » openjdk-7 » sun.rmi » server » [javadoc | source]