0N/A/*
2362N/A * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.rmi.server;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.ObjectInput;
0N/Aimport java.io.ObjectOutput;
0N/Aimport java.lang.reflect.Method;
0N/Aimport java.rmi.MarshalException;
0N/Aimport java.rmi.Remote;
0N/Aimport java.rmi.RemoteException;
0N/Aimport java.rmi.ServerException;
0N/Aimport java.rmi.UnmarshalException;
0N/Aimport java.rmi.server.Operation;
0N/Aimport java.rmi.server.RemoteCall;
0N/Aimport java.rmi.server.RemoteObject;
0N/Aimport java.rmi.server.RemoteRef;
0N/Aimport java.security.AccessController;
0N/Aimport sun.rmi.runtime.Log;
0N/Aimport sun.rmi.transport.Connection;
0N/Aimport sun.rmi.transport.LiveRef;
0N/Aimport sun.rmi.transport.StreamRemoteCall;
0N/Aimport sun.security.action.GetBooleanAction;
0N/A
0N/A/**
0N/A * NOTE: There is a JDK-internal dependency on the existence of this
0N/A * class's getLiveRef method (as it is inherited by UnicastRef2) in
0N/A * the implementation of javax.management.remote.rmi.RMIConnector.
0N/A **/
0N/Apublic class UnicastRef implements RemoteRef {
0N/A
0N/A /**
0N/A * Client-side transport log.
0N/A */
0N/A public static final Log clientRefLog =
0N/A Log.getLog("sun.rmi.client.ref", "transport", Util.logLevel);
0N/A
0N/A /**
0N/A * Client-side call log.
0N/A */
0N/A public static final Log clientCallLog =
0N/A Log.getLog("sun.rmi.client.call", "RMI",
0N/A AccessController.doPrivileged(
0N/A new GetBooleanAction("sun.rmi.client.logCalls")));
5559N/A private static final long serialVersionUID = 8258372400816541186L;
0N/A
0N/A protected LiveRef ref;
0N/A
0N/A /**
0N/A * Create a new (empty) Unicast remote reference.
0N/A */
0N/A public UnicastRef() {
0N/A }
0N/A
0N/A /**
0N/A * Create a new Unicast RemoteRef.
0N/A */
0N/A public UnicastRef(LiveRef liveRef) {
0N/A ref = liveRef;
0N/A }
0N/A
0N/A /**
0N/A * Returns the current value of this UnicastRef's underlying
0N/A * LiveRef.
0N/A *
0N/A * NOTE: There is a JDK-internal dependency on the existence of
0N/A * this method (as it is inherited by UnicastRef) in the
0N/A * implementation of javax.management.remote.rmi.RMIConnector.
0N/A **/
0N/A public LiveRef getLiveRef() {
0N/A return ref;
0N/A }
0N/A
0N/A /**
0N/A * Invoke a method. This form of delegating method invocation
0N/A * to the reference allows the reference to take care of
0N/A * setting up the connection to the remote host, marshalling
0N/A * some representation for the method and parameters, then
0N/A * communicating the method invocation to the remote host.
0N/A * This method either returns the result of a method invocation
0N/A * on the remote object which resides on the remote host or
0N/A * throws a RemoteException if the call failed or an
0N/A * application-level exception if the remote invocation throws
0N/A * an exception.
0N/A *
0N/A * @param obj the proxy for the remote object
0N/A * @param method the method to be invoked
0N/A * @param params the parameter list
0N/A * @param opnum a hash that may be used to represent the method
0N/A * @since 1.2
0N/A */
0N/A public Object invoke(Remote obj,
0N/A Method method,
0N/A Object[] params,
0N/A long opnum)
0N/A throws Exception
0N/A {
0N/A if (clientRefLog.isLoggable(Log.VERBOSE)) {
0N/A clientRefLog.log(Log.VERBOSE, "method: " + method);
0N/A }
0N/A
0N/A if (clientCallLog.isLoggable(Log.VERBOSE)) {
0N/A logClientCall(obj, method);
0N/A }
0N/A
0N/A Connection conn = ref.getChannel().newConnection();
0N/A RemoteCall call = null;
0N/A boolean reuse = true;
0N/A
0N/A /* If the call connection is "reused" early, remember not to
0N/A * reuse again.
0N/A */
0N/A boolean alreadyFreed = false;
0N/A
0N/A try {
0N/A if (clientRefLog.isLoggable(Log.VERBOSE)) {
0N/A clientRefLog.log(Log.VERBOSE, "opnum = " + opnum);
0N/A }
0N/A
0N/A // create call context
0N/A call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);
0N/A
0N/A // marshal parameters
0N/A try {
0N/A ObjectOutput out = call.getOutputStream();
0N/A marshalCustomCallData(out);
0N/A Class<?>[] types = method.getParameterTypes();
0N/A for (int i = 0; i < types.length; i++) {
0N/A marshalValue(types[i], params[i], out);
0N/A }
0N/A } catch (IOException e) {
0N/A clientRefLog.log(Log.BRIEF,
0N/A "IOException marshalling arguments: ", e);
0N/A throw new MarshalException("error marshalling arguments", e);
0N/A }
0N/A
0N/A // unmarshal return
0N/A call.executeCall();
0N/A
0N/A try {
0N/A Class<?> rtype = method.getReturnType();
0N/A if (rtype == void.class)
0N/A return null;
0N/A ObjectInput in = call.getInputStream();
0N/A
0N/A /* StreamRemoteCall.done() does not actually make use
0N/A * of conn, therefore it is safe to reuse this
0N/A * connection before the dirty call is sent for
0N/A * registered refs.
0N/A */
0N/A Object returnValue = unmarshalValue(rtype, in);
0N/A
0N/A /* we are freeing the connection now, do not free
0N/A * again or reuse.
0N/A */
0N/A alreadyFreed = true;
0N/A
0N/A /* if we got to this point, reuse must have been true. */
0N/A clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
0N/A
0N/A /* Free the call's connection early. */
0N/A ref.getChannel().free(conn, true);
0N/A
0N/A return returnValue;
0N/A
0N/A } catch (IOException e) {
0N/A clientRefLog.log(Log.BRIEF,
0N/A "IOException unmarshalling return: ", e);
0N/A throw new UnmarshalException("error unmarshalling return", e);
0N/A } catch (ClassNotFoundException e) {
0N/A clientRefLog.log(Log.BRIEF,
0N/A "ClassNotFoundException unmarshalling return: ", e);
0N/A
0N/A throw new UnmarshalException("error unmarshalling return", e);
0N/A } finally {
0N/A try {
0N/A call.done();
0N/A } catch (IOException e) {
0N/A /* WARNING: If the conn has been reused early,
0N/A * then it is too late to recover from thrown
0N/A * IOExceptions caught here. This code is relying
0N/A * on StreamRemoteCall.done() not actually
0N/A * throwing IOExceptions.
0N/A */
0N/A reuse = false;
0N/A }
0N/A }
0N/A
0N/A } catch (RuntimeException e) {
0N/A /*
0N/A * Need to distinguish between client (generated by the
0N/A * invoke method itself) and server RuntimeExceptions.
0N/A * Client side RuntimeExceptions are likely to have
0N/A * corrupted the call connection and those from the server
0N/A * are not likely to have done so. If the exception came
0N/A * from the server the call connection should be reused.
0N/A */
0N/A if ((call == null) ||
0N/A (((StreamRemoteCall) call).getServerException() != e))
0N/A {
0N/A reuse = false;
0N/A }
0N/A throw e;
0N/A
0N/A } catch (RemoteException e) {
0N/A /*
0N/A * Some failure during call; assume connection cannot
0N/A * be reused. Must assume failure even if ServerException
0N/A * or ServerError occurs since these failures can happen
0N/A * during parameter deserialization which would leave
0N/A * the connection in a corrupted state.
0N/A */
0N/A reuse = false;
0N/A throw e;
0N/A
0N/A } catch (Error e) {
0N/A /* If errors occurred, the connection is most likely not
0N/A * reusable.
0N/A */
0N/A reuse = false;
0N/A throw e;
0N/A
0N/A } finally {
0N/A
0N/A /* alreadyFreed ensures that we do not log a reuse that
0N/A * may have already happened.
0N/A */
0N/A if (!alreadyFreed) {
0N/A if (clientRefLog.isLoggable(Log.BRIEF)) {
0N/A clientRefLog.log(Log.BRIEF, "free connection (reuse = " +
0N/A reuse + ")");
0N/A }
0N/A ref.getChannel().free(conn, reuse);
0N/A }
0N/A }
0N/A }
0N/A
0N/A protected void marshalCustomCallData(ObjectOutput out) throws IOException
0N/A {}
0N/A
0N/A /**
0N/A * Marshal value to an ObjectOutput sink using RMI's serialization
0N/A * format for parameters or return values.
0N/A */
0N/A protected static void marshalValue(Class<?> type, Object value,
0N/A ObjectOutput out)
0N/A throws IOException
0N/A {
0N/A if (type.isPrimitive()) {
0N/A if (type == int.class) {
0N/A out.writeInt(((Integer) value).intValue());
0N/A } else if (type == boolean.class) {
0N/A out.writeBoolean(((Boolean) value).booleanValue());
0N/A } else if (type == byte.class) {
0N/A out.writeByte(((Byte) value).byteValue());
0N/A } else if (type == char.class) {
0N/A out.writeChar(((Character) value).charValue());
0N/A } else if (type == short.class) {
0N/A out.writeShort(((Short) value).shortValue());
0N/A } else if (type == long.class) {
0N/A out.writeLong(((Long) value).longValue());
0N/A } else if (type == float.class) {
0N/A out.writeFloat(((Float) value).floatValue());
0N/A } else if (type == double.class) {
0N/A out.writeDouble(((Double) value).doubleValue());
0N/A } else {
0N/A throw new Error("Unrecognized primitive type: " + type);
0N/A }
0N/A } else {
0N/A out.writeObject(value);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Unmarshal value from an ObjectInput source using RMI's serialization
0N/A * format for parameters or return values.
0N/A */
0N/A protected static Object unmarshalValue(Class<?> type, ObjectInput in)
0N/A throws IOException, ClassNotFoundException
0N/A {
0N/A if (type.isPrimitive()) {
0N/A if (type == int.class) {
0N/A return Integer.valueOf(in.readInt());
0N/A } else if (type == boolean.class) {
0N/A return Boolean.valueOf(in.readBoolean());
0N/A } else if (type == byte.class) {
0N/A return Byte.valueOf(in.readByte());
0N/A } else if (type == char.class) {
0N/A return Character.valueOf(in.readChar());
0N/A } else if (type == short.class) {
0N/A return Short.valueOf(in.readShort());
0N/A } else if (type == long.class) {
0N/A return Long.valueOf(in.readLong());
0N/A } else if (type == float.class) {
0N/A return Float.valueOf(in.readFloat());
0N/A } else if (type == double.class) {
0N/A return Double.valueOf(in.readDouble());
0N/A } else {
0N/A throw new Error("Unrecognized primitive type: " + type);
0N/A }
0N/A } else {
0N/A return in.readObject();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Create an appropriate call object for a new call on this object.
0N/A * Passing operation array and index, allows the stubs generator to
0N/A * assign the operation indexes and interpret them. The RemoteRef
0N/A * may need the operation to encode in for the call.
0N/A */
0N/A public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum,
0N/A long hash)
0N/A throws RemoteException
0N/A {
0N/A clientRefLog.log(Log.BRIEF, "get connection");
0N/A
0N/A Connection conn = ref.getChannel().newConnection();
0N/A try {
0N/A clientRefLog.log(Log.VERBOSE, "create call context");
0N/A
0N/A /* log information about the outgoing call */
0N/A if (clientCallLog.isLoggable(Log.VERBOSE)) {
0N/A logClientCall(obj, ops[opnum]);
0N/A }
0N/A
0N/A RemoteCall call =
0N/A new StreamRemoteCall(conn, ref.getObjID(), opnum, hash);
0N/A try {
0N/A marshalCustomCallData(call.getOutputStream());
0N/A } catch (IOException e) {
0N/A throw new MarshalException("error marshaling " +
0N/A "custom call data");
0N/A }
0N/A return call;
0N/A } catch (RemoteException e) {
0N/A ref.getChannel().free(conn, false);
0N/A throw e;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Invoke makes the remote call present in the RemoteCall object.
0N/A *
0N/A * Invoke will raise any "user" exceptions which
0N/A * should pass through and not be caught by the stub. If any
0N/A * exception is raised during the remote invocation, invoke should
0N/A * take care of cleaning up the connection before raising the
0N/A * "user" or remote exception.
0N/A */
0N/A public void invoke(RemoteCall call) throws Exception {
0N/A try {
0N/A clientRefLog.log(Log.VERBOSE, "execute call");
0N/A
0N/A call.executeCall();
0N/A
0N/A } catch (RemoteException e) {
0N/A /*
0N/A * Call did not complete; connection can't be reused.
0N/A */
0N/A clientRefLog.log(Log.BRIEF, "exception: ", e);
0N/A free(call, false);
0N/A throw e;
0N/A
0N/A } catch (Error e) {
0N/A /* If errors occurred, the connection is most likely not
0N/A * reusable.
0N/A */
0N/A clientRefLog.log(Log.BRIEF, "error: ", e);
0N/A free(call, false);
0N/A throw e;
0N/A
0N/A } catch (RuntimeException e) {
0N/A /*
0N/A * REMIND: Since runtime exceptions are no longer wrapped,
0N/A * we can't assue that the connection was left in
0N/A * a reusable state. Is this okay?
0N/A */
0N/A clientRefLog.log(Log.BRIEF, "exception: ", e);
0N/A free(call, false);
0N/A throw e;
0N/A
0N/A } catch (Exception e) {
0N/A /*
0N/A * Assume that these other exceptions are user exceptions
0N/A * and leave the connection in a reusable state.
0N/A */
0N/A clientRefLog.log(Log.BRIEF, "exception: ", e);
0N/A free(call, true);
0N/A /* reraise user (and unknown) exceptions. */
0N/A throw e;
0N/A }
0N/A
0N/A /*
0N/A * Don't free the connection if an exception did not
0N/A * occur because the stub needs to unmarshal the
0N/A * return value. The connection will be freed
0N/A * by a call to the "done" method.
0N/A */
0N/A }
0N/A
0N/A /**
0N/A * Private method to free a connection.
0N/A */
0N/A private void free(RemoteCall call, boolean reuse) throws RemoteException {
0N/A Connection conn = ((StreamRemoteCall)call).getConnection();
0N/A ref.getChannel().free(conn, reuse);
0N/A }
0N/A
0N/A /**
0N/A * Done should only be called if the invoke returns successfully
0N/A * (non-exceptionally) to the stub. It allows the remote reference to
0N/A * clean up (or reuse) the connection.
0N/A */
0N/A public void done(RemoteCall call) throws RemoteException {
0N/A
0N/A /* Done only uses the connection inside the call to obtain the
0N/A * channel the connection uses. Once all information is read
0N/A * from the connection, the connection may be freed.
0N/A */
0N/A clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
0N/A
0N/A /* Free the call connection early. */
0N/A free(call, true);
0N/A
0N/A try {
0N/A call.done();
0N/A } catch (IOException e) {
0N/A /* WARNING: If the conn has been reused early, then it is
0N/A * too late to recover from thrown IOExceptions caught
0N/A * here. This code is relying on StreamRemoteCall.done()
0N/A * not actually throwing IOExceptions.
0N/A */
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Log the details of an outgoing call. The method parameter is either of
0N/A * type java.lang.reflect.Method or java.rmi.server.Operation.
0N/A */
0N/A void logClientCall(Object obj, Object method) {
0N/A clientCallLog.log(Log.VERBOSE, "outbound call: " +
0N/A ref + " : " + obj.getClass().getName() +
0N/A ref.getObjID().toString() + ": " + method);
0N/A }
0N/A
0N/A /**
0N/A * Returns the class of the ref type to be serialized
0N/A */
0N/A public String getRefClass(ObjectOutput out) {
0N/A return "UnicastRef";
0N/A }
0N/A
0N/A /**
0N/A * Write out external representation for remote ref.
0N/A */
0N/A public void writeExternal(ObjectOutput out) throws IOException {
0N/A ref.write(out, false);
0N/A }
0N/A
0N/A /**
0N/A * Read in external representation for remote ref.
0N/A * @exception ClassNotFoundException If the class for an object
0N/A * being restored cannot be found.
0N/A */
0N/A public void readExternal(ObjectInput in)
0N/A throws IOException, ClassNotFoundException
0N/A {
0N/A ref = LiveRef.read(in, false);
0N/A }
0N/A
0N/A //----------------------------------------------------------------------;
0N/A /**
0N/A * Method from object, forward from RemoteObject
0N/A */
0N/A public String remoteToString() {
0N/A return Util.getUnqualifiedName(getClass()) + " [liveRef: " + ref + "]";
0N/A }
0N/A
0N/A /**
0N/A * default implementation of hashCode for remote objects
0N/A */
0N/A public int remoteHashCode() {
0N/A return ref.hashCode();
0N/A }
0N/A
0N/A /** default implementation of equals for remote objects
0N/A */
0N/A public boolean remoteEquals(RemoteRef sub) {
0N/A if (sub instanceof UnicastRef)
0N/A return ref.remoteEquals(((UnicastRef)sub).ref);
0N/A return false;
0N/A }
0N/A}