--- /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();
+ }
+}