aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/rmi/server/UnicastServerRef.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/share/classes/sun/rmi/server/UnicastServerRef.java')
-rw-r--r--src/share/classes/sun/rmi/server/UnicastServerRef.java563
1 files changed, 563 insertions, 0 deletions
diff --git a/src/share/classes/sun/rmi/server/UnicastServerRef.java b/src/share/classes/sun/rmi/server/UnicastServerRef.java
new file mode 100644
index 000000000..63510db4f
--- /dev/null
+++ b/src/share/classes/sun/rmi/server/UnicastServerRef.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright 1996-2005 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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() {
+ return new UnicastRef(ref);
+ }
+
+ /**
+ * Write out external representation for remote ref.
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ }
+
+ /**
+ * Read in external representation for remote ref.
+ * @exception ClassNotFoundException If the class for an object
+ * being restored cannot be found.
+ */
+ public void readExternal(ObjectInput in)
+ throws IOException, ClassNotFoundException
+ {
+ // object is re-exported elsewhere (e.g., by UnicastRemoteObject)
+ ref = null;
+ skel = null;
+ }
+
+
+ /**
+ * A weak hash map, mapping classes to hash maps that map method
+ * hashes to method objects.
+ **/
+ private static class HashToMethod_Maps
+ extends WeakClassHashMap<Map<Long,Method>>
+ {
+ HashToMethod_Maps() {}
+
+ protected Map<Long,Method> computeValue(Class<?> remoteClass) {
+ Map<Long,Method> map = new HashMap<Long,Method>();
+ for (Class<?> cl = remoteClass;
+ cl != null;
+ cl = cl.getSuperclass())
+ {
+ for (Class<?> intf : cl.getInterfaces()) {
+ if (Remote.class.isAssignableFrom(intf)) {
+ for (Method method : intf.getMethods()) {
+ final Method m = method;
+ /*
+ * Set this Method object to override language
+ * access checks so that the dispatcher can invoke
+ * methods from non-public remote interfaces.
+ */
+ AccessController.doPrivileged(
+ new PrivilegedAction<Void>() {
+ public Void run() {
+ m.setAccessible(true);
+ return null;
+ }
+ });
+ map.put(Util.computeMethodHash(m), m);
+ }
+ }
+ }
+ }
+ return map;
+ }
+ }
+}