563 lines · 20.4 KB · Java
RawDownload
/* * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.rmi.server; import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectOutput;import java.io.PrintStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.rmi.MarshalException;import java.rmi.Remote;import java.rmi.RemoteException;import java.rmi.ServerError;import java.rmi.ServerException;import java.rmi.UnmarshalException;import java.rmi.server.ExportException;import java.rmi.server.RemoteCall;import java.rmi.server.RemoteRef;import java.rmi.server.RemoteStub;import java.rmi.server.ServerNotActiveException;import java.rmi.server.ServerRef;import java.rmi.server.Skeleton;import java.rmi.server.SkeletonNotFoundException;import java.security.AccessController;import java.security.PrivilegedAction;import java.util.Collections;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.WeakHashMap;import sun.rmi.runtime.Log;import sun.rmi.transport.LiveRef;import sun.rmi.transport.Target;import sun.rmi.transport.tcp.TCPTransport;import sun.security.action.GetBooleanAction; /** * UnicastServerRef implements the remote reference layer server-side * behavior for remote objects exported with the "UnicastRef" reference * type. * * @author  Ann Wollrath * @author  Roger Riggs * @author  Peter Jones */public class UnicastServerRef extends UnicastRef    implements ServerRef, Dispatcher{    /** value of server call log property */    public static final boolean logCalls = AccessController.doPrivileged(        new GetBooleanAction("java.rmi.server.logCalls"));     /** server call log */    public static final Log callLog =        Log.getLog("sun.rmi.server.call", "RMI", logCalls);     // use serialVersionUID from JDK 1.2.2 for interoperability    private static final long serialVersionUID = -7384275867073752268L;     /** flag to enable writing exceptions to System.err */    private static final boolean wantExceptionLog =        AccessController.doPrivileged(            new GetBooleanAction("sun.rmi.server.exceptionTrace"));     private boolean forceStubUse = false;     /**     * flag to remove server-side stack traces before marshalling     * exceptions thrown by remote invocations to this VM     */    private static final boolean suppressStackTraces =        AccessController.doPrivileged(            new GetBooleanAction(                "sun.rmi.server.suppressStackTraces"));     /**     * skeleton to dispatch remote calls through, for 1.1 stub protocol     * (may be null if stub class only uses 1.2 stub protocol)     */    private transient Skeleton skel;     /** maps method hash to Method object for each remote method */    private transient Map<Long,Method> hashToMethod_Map = null;     /**     * A weak hash map, mapping classes to hash maps that map method     * hashes to method objects.     **/    private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps =        new HashToMethod_Maps();     /** cache of impl classes that have no corresponding skeleton class */    private static final Map<Class<?>,?> withoutSkeletons =        Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>());     /**     * Create a new (empty) Unicast server remote reference.     */    public UnicastServerRef() {    }     /**     * Construct a Unicast server remote reference for a specified     * liveRef.     */    public UnicastServerRef(LiveRef ref) {        super(ref);    }     /**     * Construct a Unicast server remote reference to be exported     * on the specified port.     */    public UnicastServerRef(int port) {        super(new LiveRef(port));    }     /**     * Constructs a UnicastServerRef to be exported on an     * anonymous port (i.e., 0) and that uses a pregenerated stub class     * (NOT a dynamic proxy instance) if 'forceStubUse' is 'true'.     *     * This constructor is only called by the method     * UnicastRemoteObject.exportObject(Remote) passing 'true' for     * 'forceStubUse'.  The UnicastRemoteObject.exportObject(Remote) method     * returns RemoteStub, so it must ensure that the stub for the     * exported object is an instance of a pregenerated stub class that     * extends RemoteStub (instead of an instance of a dynamic proxy class     * which is not an instance of RemoteStub).     **/    public UnicastServerRef(boolean forceStubUse) {        this(0);        this.forceStubUse = forceStubUse;    }     /**     * With the addition of support for dynamic proxies as stubs, this     * method is obsolete because it returns RemoteStub instead of the more     * general Remote.  It should not be called.  It sets the     * 'forceStubUse' flag to true so that the stub for the exported object     * is forced to be an instance of the pregenerated stub class, which     * extends RemoteStub.     *     * Export this object, create the skeleton and stubs for this     * dispatcher.  Create a stub based on the type of the impl,     * initialize it with the appropriate remote reference. Create the     * target defined by the impl, dispatcher (this) and stub.     * Export that target via the Ref.     **/    public RemoteStub exportObject(Remote impl, Object data)        throws RemoteException    {        forceStubUse = true;        return (RemoteStub) exportObject(impl, data, false);    }     /**     * Export this object, create the skeleton and stubs for this     * dispatcher.  Create a stub based on the type of the impl,     * initialize it with the appropriate remote reference. Create the     * target defined by the impl, dispatcher (this) and stub.     * Export that target via the Ref.     */    public Remote exportObject(Remote impl, Object data,                               boolean permanent)        throws RemoteException    {        Class implClass = impl.getClass();        Remote stub;         try {            stub = Util.createProxy(implClass, getClientRef(), forceStubUse);        } catch (IllegalArgumentException e) {            throw new ExportException(                "remote object implements illegal remote interface", e);        }        if (stub instanceof RemoteStub) {            setSkeleton(impl);        }         Target target =            new Target(impl, this, stub, ref.getObjID(), permanent);        ref.exportObject(target);        hashToMethod_Map = hashToMethod_Maps.get(implClass);        return stub;    }     /**     * Return the hostname of the current client.  When called from a     * thread actively handling a remote method invocation the     * hostname of the client is returned.     * @exception ServerNotActiveException If called outside of servicing     * a remote method invocation.     */    public String getClientHost() throws ServerNotActiveException {        return TCPTransport.getClientHost();    }     /**     * Discovers and sets the appropriate skeleton for the impl.     */    public void setSkeleton(Remote impl) throws RemoteException {        if (!withoutSkeletons.containsKey(impl.getClass())) {            try {                skel = Util.createSkeleton(impl);            } catch (SkeletonNotFoundException e) {                /*                 * Ignore exception for skeleton class not found, because a                 * skeleton class is not necessary with the 1.2 stub protocol.                 * Remember that this impl's class does not have a skeleton                 * class so we don't waste time searching for it again.                 */                withoutSkeletons.put(impl.getClass(), null);            }        }    }     /**     * Call to dispatch to the remote object (on the server side).     * The up-call to the server and the marshalling of return result     * (or exception) should be handled before returning from this     * method.     * @param obj the target remote object for the call     * @param call the "remote call" from which operation and     * method arguments can be obtained.     * @exception IOException If unable to marshal return result or     * release input or output streams     */    public void dispatch(Remote obj, RemoteCall call) throws IOException {        // positive operation number in 1.1 stubs;        // negative version number in 1.2 stubs and beyond...        int num;        long op;         try {            // read remote call header            ObjectInput in;            try {                in = call.getInputStream();                num = in.readInt();                if (num >= 0) {                    if (skel != null) {                        oldDispatch(obj, call, num);                        return;                    } else {                        throw new UnmarshalException(                            "skeleton class not found but required " +                            "for client version");                    }                }                op = in.readLong();            } catch (Exception readEx) {                throw new UnmarshalException("error unmarshalling call header",                                             readEx);            }             /*             * Since only system classes (with null class loaders) will be on             * the execution stack during parameter unmarshalling for the 1.2             * stub protocol, tell the MarshalInputStream not to bother trying             * to resolve classes using its superclasses's default method of             * consulting the first non-null class loader on the stack.             */            MarshalInputStream marshalStream = (MarshalInputStream) in;            marshalStream.skipDefaultResolveClass();             Method method = hashToMethod_Map.get(op);            if (method == null) {                throw new UnmarshalException("unrecognized method hash: " +                    "method not supported by remote object");            }             // if calls are being logged, write out object id and operation            logCall(obj, method);             // unmarshal parameters            Class[] types = method.getParameterTypes();            Object[] params = new Object[types.length];             try {                unmarshalCustomCallData(in);                for (int i = 0; i < types.length; i++) {                    params[i] = unmarshalValue(types[i], in);                }            } catch (java.io.IOException e) {                throw new UnmarshalException(                    "error unmarshalling arguments", e);            } catch (ClassNotFoundException e) {                throw new UnmarshalException(                    "error unmarshalling arguments", e);            } finally {                call.releaseInputStream();            }             // make upcall on remote object            Object result;            try {                result = method.invoke(obj, params);            } catch (InvocationTargetException e) {                throw e.getTargetException();            }             // marshal return value            try {                ObjectOutput out = call.getResultStream(true);                Class rtype = method.getReturnType();                if (rtype != void.class) {                    marshalValue(rtype, result, out);                }            } catch (IOException ex) {                throw new MarshalException("error marshalling return", ex);                /*                 * This throw is problematic because when it is caught below,                 * we attempt to marshal it back to the client, but at this                 * point, a "normal return" has already been indicated,                 * so marshalling an exception will corrupt the stream.                 * This was the case with skeletons as well; there is no                 * immediately obvious solution without a protocol change.                 */            }        } catch (Throwable e) {            logCallException(e);             ObjectOutput out = call.getResultStream(false);            if (e instanceof Error) {                e = new ServerError(                    "Error occurred in server thread", (Error) e);            } else if (e instanceof RemoteException) {                e = new ServerException(                    "RemoteException occurred in server thread",                    (Exception) e);            }            if (suppressStackTraces) {                clearStackTraces(e);            }            out.writeObject(e);        } finally {            call.releaseInputStream(); // in case skeleton doesn't            call.releaseOutputStream();        }    }     protected void unmarshalCustomCallData(ObjectInput in)        throws IOException, ClassNotFoundException    {}     /**     * Handle server-side dispatch using the RMI 1.1 stub/skeleton     * protocol, given a non-negative operation number that has     * already been read from the call stream.     *     * @param obj the target remote object for the call     * @param call the "remote call" from which operation and     * method arguments can be obtained.     * @param op the operation number     * @exception IOException if unable to marshal return result or     * release input or output streams     */    public void oldDispatch(Remote obj, RemoteCall call, int op)        throws IOException    {        long hash;              // hash for matching stub with skeleton         try {            // read remote call header            ObjectInput in;            try {                in = call.getInputStream();                hash = in.readLong();            } catch (Exception readEx) {                throw new UnmarshalException("error unmarshalling call header",                                             readEx);            }             // if calls are being logged, write out object id and operation            logCall(obj, skel.getOperations()[op]);            unmarshalCustomCallData(in);            // dispatch to skeleton for remote object            skel.dispatch(obj, call, op, hash);         } catch (Throwable e) {            logCallException(e);             ObjectOutput out = call.getResultStream(false);            if (e instanceof Error) {                e = new ServerError(                    "Error occurred in server thread", (Error) e);            } else if (e instanceof RemoteException) {                e = new ServerException(                    "RemoteException occurred in server thread",                    (Exception) e);            }            if (suppressStackTraces) {                clearStackTraces(e);            }            out.writeObject(e);        } finally {            call.releaseInputStream(); // in case skeleton doesn't            call.releaseOutputStream();        }    }     /**     * Clear the stack trace of the given Throwable by replacing it with     * an empty StackTraceElement array, and do the same for all of its     * chained causative exceptions.     */    public static void clearStackTraces(Throwable t) {        StackTraceElement[] empty = new StackTraceElement[0];        while (t != null) {            t.setStackTrace(empty);            t = t.getCause();        }    }     /**     * Log the details of an incoming call.  The method parameter is either of     * type java.lang.reflect.Method or java.rmi.server.Operation.     */    private void logCall(Remote obj, Object method) {        if (callLog.isLoggable(Log.VERBOSE)) {            String clientHost;            try {                clientHost = getClientHost();            } catch (ServerNotActiveException snae) {                clientHost = "(local)"; // shouldn't happen            }            callLog.log(Log.VERBOSE, "[" + clientHost + ": " +                              obj.getClass().getName() +                              ref.getObjID().toString() + ": " +                              method + "]");        }    }     /**     * Log the exception detail of an incoming call.     */    private void logCallException(Throwable e) {        // if calls are being logged, log them        if (callLog.isLoggable(Log.BRIEF)) {            String clientHost = "";            try {                clientHost = "[" + getClientHost() + "] ";            } catch (ServerNotActiveException snae) {            }            callLog.log(Log.BRIEF, clientHost + "exception: ", e);        }         // write exceptions (only) to System.err if desired        if (wantExceptionLog) {            java.io.PrintStream log = System.err;            synchronized (log) {                log.println();                log.println("Exception dispatching call to " +                            ref.getObjID() + " in thread \"" +                            Thread.currentThread().getName() +                            "\" at " + (new Date()) + ":");                e.printStackTrace(log);            }        }    }     /**     * Returns the class of the ref type to be serialized.     */    public String getRefClass(ObjectOutput out) {        return "UnicastServerRef";    }     /**     * Return the client remote reference for this remoteRef.     * In the case of a client RemoteRef "this" is the answer.     * For a server remote reference, a client side one will have to     * found or created.     */    protected RemoteRef getClientRef() {

Lines 1–500 of 563.