/* * 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.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 */ 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; } } /** * 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. * (Exception thrown before fallthrough can occur) */ @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"); } 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(); } }