src/java.rmi/share/classes/sun/rmi/transport/StreamRemoteCall.java
changeset 47216 71c04702a3d5
parent 45984 75fef64e21fa
child 58635 06d7236d6ef6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.rmi/share/classes/sun/rmi/transport/StreamRemoteCall.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1996, 2017, 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.transport;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.StreamCorruptedException;
+import java.rmi.RemoteException;
+import java.rmi.MarshalException;
+import java.rmi.UnmarshalException;
+import java.rmi.server.ObjID;
+import java.rmi.server.RemoteCall;
+import sun.rmi.runtime.Log;
+import sun.rmi.server.UnicastRef;
+import sun.rmi.transport.tcp.TCPEndpoint;
+
+/**
+ * Stream-based implementation of the RemoteCall interface.
+ *
+ * @author Ann Wollrath
+ */
+@SuppressWarnings("deprecation")
+public class StreamRemoteCall implements RemoteCall {
+    private ConnectionInputStream in = null;
+    private ConnectionOutputStream out = null;
+    private Connection conn;
+    private boolean resultStarted = false;
+    private Exception serverException = null;
+
+    public StreamRemoteCall(Connection c) {
+        conn = c;
+    }
+
+    public StreamRemoteCall(Connection c, ObjID id, int op, long hash)
+        throws RemoteException
+    {
+        try {
+            conn = c;
+            Transport.transportLog.log(Log.VERBOSE,
+                "write remote call header...");
+
+            // write out remote call header info...
+            // call header, part 1 (read by Transport)
+            conn.getOutputStream().write(TransportConstants.Call);
+            getOutputStream();           // creates a MarshalOutputStream
+            id.write(out);               // object id (target of call)
+            // call header, part 2 (read by Dispatcher)
+            out.writeInt(op);            // method number (operation index)
+            out.writeLong(hash);         // stub/skeleton hash
+        } catch (IOException e) {
+            throw new MarshalException("Error marshaling call header", e);
+        }
+    }
+
+    /**
+     * Return the connection associated with this call.
+     */
+    public Connection getConnection() {
+        return conn;
+    }
+
+    /**
+     * Return the output stream the stub/skeleton should put arguments/results
+     * into.
+     */
+    public ObjectOutput getOutputStream() throws IOException {
+        return getOutputStream(false);
+    }
+
+    private ObjectOutput getOutputStream(boolean resultStream)
+        throws IOException
+    {
+        if (out == null) {
+            Transport.transportLog.log(Log.VERBOSE, "getting output stream");
+
+            out = new ConnectionOutputStream(conn, resultStream);
+        }
+        return out;
+    }
+
+    /**
+     * Release the outputStream  Currently, will not complain if the
+     * output stream is released more than once.
+     */
+    public void releaseOutputStream() throws IOException {
+        try {
+            if (out != null) {
+                try {
+                    out.flush();
+                } finally {
+                    out.done();         // always start DGC ack timer
+                }
+            }
+            conn.releaseOutputStream();
+        } finally {
+            out = null;
+        }
+    }
+
+    /**
+     * Get the InputStream the stub/skeleton should get results/arguments
+     * from.
+     */
+    public ObjectInput getInputStream() throws IOException {
+        if (in == null) {
+            Transport.transportLog.log(Log.VERBOSE, "getting input stream");
+
+            in = new ConnectionInputStream(conn.getInputStream());
+        }
+        return in;
+    }
+
+    /**
+     * Release the input stream, this would allow some transports to release
+     * the channel early.
+     */
+    public void releaseInputStream() throws IOException {
+        /* WARNING: Currently, the UnicastRef.java invoke methods rely
+         * upon this method not throwing an IOException.
+         */
+
+        try {
+            if (in != null) {
+                // execute MarshalInputStream "done" callbacks
+                try {
+                    in.done();
+                } catch (RuntimeException e) {
+                }
+
+                // add saved references to DGC table
+                in.registerRefs();
+
+                /* WARNING: The connection being passed to done may have
+                 * already been freed.
+                 */
+                in.done(conn);
+            }
+            conn.releaseInputStream();
+        } finally {
+            in = null;
+        }
+    }
+
+    /**
+     * Discard any post-processing of refs the InputStream.
+     */
+    public void discardPendingRefs() {
+        in.discardRefs();
+    }
+
+    /**
+     * Returns an output stream (may put out header information
+     * relating to the success of the call).
+     * @param success If true, indicates normal return, else indicates
+     * exceptional return.
+     * @exception StreamCorruptedException If result stream previously
+     * acquired
+     * @exception IOException For any other problem with I/O.
+     */
+    public ObjectOutput getResultStream(boolean success) throws IOException {
+        /* make sure result code only marshaled once. */
+        if (resultStarted)
+            throw new StreamCorruptedException("result already in progress");
+        else
+            resultStarted = true;
+
+        // write out return header
+        // return header, part 1 (read by Transport)
+        DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
+        wr.writeByte(TransportConstants.Return);// transport op
+        getOutputStream(true);  // creates a MarshalOutputStream
+        // return header, part 2 (read by client-side RemoteCall)
+        if (success)            //
+            out.writeByte(TransportConstants.NormalReturn);
+        else
+            out.writeByte(TransportConstants.ExceptionalReturn);
+        out.writeID();          // write id for gcAck
+        return out;
+    }
+
+    /**
+     * Do whatever it takes to execute the call.
+     */
+    @SuppressWarnings("fallthrough")
+    public void executeCall() throws Exception {
+        byte returnType;
+
+        // read result header
+        DGCAckHandler ackHandler = null;
+        try {
+            if (out != null) {
+                ackHandler = out.getDGCAckHandler();
+            }
+            releaseOutputStream();
+            DataInputStream rd = new DataInputStream(conn.getInputStream());
+            byte op = rd.readByte();
+            if (op != TransportConstants.Return) {
+                if (Transport.transportLog.isLoggable(Log.BRIEF)) {
+                    Transport.transportLog.log(Log.BRIEF,
+                        "transport return code invalid: " + op);
+                }
+                throw new UnmarshalException("Transport return code invalid");
+            }
+            getInputStream();
+            returnType = in.readByte();
+            in.readID();        // id for DGC acknowledgement
+        } catch (UnmarshalException e) {
+            throw e;
+        } catch (IOException e) {
+            throw new UnmarshalException("Error unmarshaling return header",
+                                         e);
+        } finally {
+            if (ackHandler != null) {
+                ackHandler.release();
+            }
+        }
+
+        // read return value
+        switch (returnType) {
+        case TransportConstants.NormalReturn:
+            break;
+
+        case TransportConstants.ExceptionalReturn:
+            Object ex;
+            try {
+                ex = in.readObject();
+            } catch (Exception e) {
+                throw new UnmarshalException("Error unmarshaling return", e);
+            }
+
+            // An exception should have been received,
+            // if so throw it, else flag error
+            if (ex instanceof Exception) {
+                exceptionReceivedFromServer((Exception) ex);
+            } else {
+                throw new UnmarshalException("Return type not Exception");
+            }
+            // Exception is thrown before fallthrough can occur
+        default:
+            if (Transport.transportLog.isLoggable(Log.BRIEF)) {
+                Transport.transportLog.log(Log.BRIEF,
+                    "return code invalid: " + returnType);
+            }
+            throw new UnmarshalException("Return code invalid");
+        }
+    }
+
+    /**
+     * Routine that causes the stack traces of remote exceptions to be
+     * filled in with the current stack trace on the client.  Detail
+     * exceptions are filled in iteratively.
+     */
+    protected void exceptionReceivedFromServer(Exception ex) throws Exception {
+        serverException = ex;
+
+        StackTraceElement[] serverTrace = ex.getStackTrace();
+        StackTraceElement[] clientTrace = (new Throwable()).getStackTrace();
+        StackTraceElement[] combinedTrace =
+            new StackTraceElement[serverTrace.length + clientTrace.length];
+        System.arraycopy(serverTrace, 0, combinedTrace, 0,
+                         serverTrace.length);
+        System.arraycopy(clientTrace, 0, combinedTrace, serverTrace.length,
+                         clientTrace.length);
+        ex.setStackTrace(combinedTrace);
+
+        /*
+         * Log the details of a server exception thrown as a result of a
+         * remote method invocation.
+         */
+        if (UnicastRef.clientCallLog.isLoggable(Log.BRIEF)) {
+            /* log call exception returned from server before it is rethrown */
+            TCPEndpoint ep = (TCPEndpoint) conn.getChannel().getEndpoint();
+            UnicastRef.clientCallLog.log(Log.BRIEF, "outbound call " +
+                "received exception: [" + ep.getHost() + ":" +
+                ep.getPort() + "] exception: ", ex);
+        }
+
+        throw ex;
+    }
+
+    /*
+     * method to retrieve possible server side exceptions (which will
+     * be throw from exceptionReceivedFromServer(...) )
+     */
+    public Exception getServerException() {
+        return serverException;
+    }
+
+    public void done() throws IOException {
+        /* WARNING: Currently, the UnicastRef.java invoke methods rely
+         * upon this method not throwing an IOException.
+         */
+
+        releaseInputStream();
+    }
+}