Save This Page
Home » openjdk-7 » sun.rmi » server » [javadoc | source]
    1   /*
    2    * Copyright 1996-2005 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any 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                   hash = in.readLong();
  394               } catch (Exception readEx) {
  395                   throw new UnmarshalException("error unmarshalling call header",
  396                                                readEx);
  397               }
  398   
  399               // if calls are being logged, write out object id and operation
  400               logCall(obj, skel.getOperations()[op]);
  401               unmarshalCustomCallData(in);
  402               // dispatch to skeleton for remote object
  403               skel.dispatch(obj, call, op, hash);
  404   
  405           } catch (Throwable e) {
  406               logCallException(e);
  407   
  408               ObjectOutput out = call.getResultStream(false);
  409               if (e instanceof Error) {
  410                   e = new ServerError(
  411                       "Error occurred in server thread", (Error) e);
  412               } else if (e instanceof RemoteException) {
  413                   e = new ServerException(
  414                       "RemoteException occurred in server thread",
  415                       (Exception) e);
  416               }
  417               if (suppressStackTraces) {
  418                   clearStackTraces(e);
  419               }
  420               out.writeObject(e);
  421           } finally {
  422               call.releaseInputStream(); // in case skeleton doesn't
  423               call.releaseOutputStream();
  424           }
  425       }
  426   
  427       /**
  428        * Clear the stack trace of the given Throwable by replacing it with
  429        * an empty StackTraceElement array, and do the same for all of its
  430        * chained causative exceptions.
  431        */
  432       public static void clearStackTraces(Throwable t) {
  433           StackTraceElement[] empty = new StackTraceElement[0];
  434           while (t != null) {
  435               t.setStackTrace(empty);
  436               t = t.getCause();
  437           }
  438       }
  439   
  440       /**
  441        * Log the details of an incoming call.  The method parameter is either of
  442        * type java.lang.reflect.Method or java.rmi.server.Operation.
  443        */
  444       private void logCall(Remote obj, Object method) {
  445           if (callLog.isLoggable(Log.VERBOSE)) {
  446               String clientHost;
  447               try {
  448                   clientHost = getClientHost();
  449               } catch (ServerNotActiveException snae) {
  450                   clientHost = "(local)"; // shouldn't happen
  451               }
  452               callLog.log(Log.VERBOSE, "[" + clientHost + ": " +
  453                                 obj.getClass().getName() +
  454                                 ref.getObjID().toString() + ": " +
  455                                 method + "]");
  456           }
  457       }
  458   
  459       /**
  460        * Log the exception detail of an incoming call.
  461        */
  462       private void logCallException(Throwable e) {
  463           // if calls are being logged, log them
  464           if (callLog.isLoggable(Log.BRIEF)) {
  465               String clientHost = "";
  466               try {
  467                   clientHost = "[" + getClientHost() + "] ";
  468               } catch (ServerNotActiveException snae) {
  469               }
  470               callLog.log(Log.BRIEF, clientHost + "exception: ", e);
  471           }
  472   
  473           // write exceptions (only) to System.err if desired
  474           if (wantExceptionLog) {
  475               java.io.PrintStream log = System.err;
  476               synchronized (log) {
  477                   log.println();
  478                   log.println("Exception dispatching call to " +
  479                               ref.getObjID() + " in thread \"" +
  480                               Thread.currentThread().getName() +
  481                               "\" at " + (new Date()) + ":");
  482                   e.printStackTrace(log);
  483               }
  484           }
  485       }
  486   
  487       /**
  488        * Returns the class of the ref type to be serialized.
  489        */
  490       public String getRefClass(ObjectOutput out) {
  491           return "UnicastServerRef";
  492       }
  493   
  494       /**
  495        * Return the client remote reference for this remoteRef.
  496        * In the case of a client RemoteRef "this" is the answer.
  497        * For a server remote reference, a client side one will have to
  498        * found or created.
  499        */
  500       protected RemoteRef getClientRef() {
  501           return new UnicastRef(ref);
  502       }
  503   
  504       /**
  505        * Write out external representation for remote ref.
  506        */
  507       public void writeExternal(ObjectOutput out) throws IOException {
  508       }
  509   
  510       /**
  511        * Read in external representation for remote ref.
  512        * @exception ClassNotFoundException If the class for an object
  513        * being restored cannot be found.
  514        */
  515       public void readExternal(ObjectInput in)
  516           throws IOException, ClassNotFoundException
  517       {
  518           // object is re-exported elsewhere (e.g., by UnicastRemoteObject)
  519           ref = null;
  520           skel = null;
  521       }
  522   
  523   
  524       /**
  525        * A weak hash map, mapping classes to hash maps that map method
  526        * hashes to method objects.
  527        **/
  528       private static class HashToMethod_Maps
  529           extends WeakClassHashMap<Map<Long,Method>>
  530       {
  531           HashToMethod_Maps() {}
  532   
  533           protected Map<Long,Method> computeValue(Class<?> remoteClass) {
  534               Map<Long,Method> map = new HashMap<Long,Method>();
  535               for (Class<?> cl = remoteClass;
  536                    cl != null;
  537                    cl = cl.getSuperclass())
  538               {
  539                   for (Class<?> intf : cl.getInterfaces()) {
  540                       if (Remote.class.isAssignableFrom(intf)) {
  541                           for (Method method : intf.getMethods()) {
  542                               final Method m = method;
  543                               /*
  544                                * Set this Method object to override language
  545                                * access checks so that the dispatcher can invoke
  546                                * methods from non-public remote interfaces.
  547                                */
  548                               AccessController.doPrivileged(
  549                                   new PrivilegedAction<Void>() {
  550                                   public Void run() {
  551                                       m.setAccessible(true);
  552                                       return null;
  553                                   }
  554                               });
  555                               map.put(Util.computeMethodHash(m), m);
  556                           }
  557                       }
  558                   }
  559               }
  560               return map;
  561           }
  562       }
  563   }

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