Path: blob/master/src/java.rmi/share/classes/sun/rmi/transport/StreamRemoteCall.java
41154 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.rmi.transport;2627import java.io.DataInputStream;28import java.io.DataOutputStream;29import java.io.IOException;30import java.io.ObjectInput;31import java.io.ObjectInputFilter;32import java.io.ObjectOutput;33import java.io.StreamCorruptedException;34import java.rmi.RemoteException;35import java.rmi.MarshalException;36import java.rmi.UnmarshalException;37import java.rmi.server.ObjID;38import java.rmi.server.RemoteCall;39import java.security.AccessController;40import java.security.PrivilegedAction;4142import sun.rmi.runtime.Log;43import sun.rmi.server.UnicastRef;44import sun.rmi.transport.tcp.TCPEndpoint;4546/**47* Stream-based implementation of the RemoteCall interface.48*49* @author Ann Wollrath50*/51@SuppressWarnings("deprecation")52public class StreamRemoteCall implements RemoteCall {53private ConnectionInputStream in = null;54private ConnectionOutputStream out = null;55private Connection conn;56private ObjectInputFilter filter = null;57private boolean resultStarted = false;58private Exception serverException = null;5960public StreamRemoteCall(Connection c) {61conn = c;62}6364public StreamRemoteCall(Connection c, ObjID id, int op, long hash)65throws RemoteException66{67try {68conn = c;69Transport.transportLog.log(Log.VERBOSE,70"write remote call header...");7172// write out remote call header info...73// call header, part 1 (read by Transport)74conn.getOutputStream().write(TransportConstants.Call);75getOutputStream(); // creates a MarshalOutputStream76id.write(out); // object id (target of call)77// call header, part 2 (read by Dispatcher)78out.writeInt(op); // method number (operation index)79out.writeLong(hash); // stub/skeleton hash80} catch (IOException e) {81throw new MarshalException("Error marshaling call header", e);82}83}8485/**86* Return the connection associated with this call.87*/88public Connection getConnection() {89return conn;90}9192/**93* Return the output stream the stub/skeleton should put arguments/results94* into.95*/96public ObjectOutput getOutputStream() throws IOException {97return getOutputStream(false);98}99100private ObjectOutput getOutputStream(boolean resultStream)101throws IOException102{103if (out == null) {104Transport.transportLog.log(Log.VERBOSE, "getting output stream");105106out = new ConnectionOutputStream(conn, resultStream);107}108return out;109}110111/**112* Release the outputStream Currently, will not complain if the113* output stream is released more than once.114*/115public void releaseOutputStream() throws IOException {116try {117if (out != null) {118try {119out.flush();120} finally {121out.done(); // always start DGC ack timer122}123}124conn.releaseOutputStream();125} finally {126out = null;127}128}129130public void setObjectInputFilter(ObjectInputFilter filter) {131if (in != null) {132throw new IllegalStateException("set filter must occur before calling getInputStream");133}134this.filter = filter;135}136137/**138* Get the InputStream the stub/skeleton should get results/arguments139* from.140*/141@SuppressWarnings("removal")142public ObjectInput getInputStream() throws IOException {143if (in == null) {144Transport.transportLog.log(Log.VERBOSE, "getting input stream");145146in = new ConnectionInputStream(conn.getInputStream());147if (filter != null) {148AccessController.doPrivileged((PrivilegedAction<Void>) () -> {149in.setObjectInputFilter(filter);150return null;151});152}153}154return in;155}156157/**158* Release the input stream, this would allow some transports to release159* the channel early.160*/161public void releaseInputStream() throws IOException {162/* WARNING: Currently, the UnicastRef.java invoke methods rely163* upon this method not throwing an IOException.164*/165166try {167if (in != null) {168// execute MarshalInputStream "done" callbacks169try {170in.done();171} catch (RuntimeException e) {172}173174// add saved references to DGC table175in.registerRefs();176177/* WARNING: The connection being passed to done may have178* already been freed.179*/180in.done(conn);181}182conn.releaseInputStream();183} finally {184in = null;185}186}187188/**189* Discard any post-processing of refs the InputStream.190*/191public void discardPendingRefs() {192in.discardRefs();193}194195/**196* Returns an output stream (may put out header information197* relating to the success of the call).198* @param success If true, indicates normal return, else indicates199* exceptional return.200* @exception StreamCorruptedException If result stream previously201* acquired202* @exception IOException For any other problem with I/O.203*/204public ObjectOutput getResultStream(boolean success) throws IOException {205/* make sure result code only marshaled once. */206if (resultStarted)207throw new StreamCorruptedException("result already in progress");208else209resultStarted = true;210211// write out return header212// return header, part 1 (read by Transport)213DataOutputStream wr = new DataOutputStream(conn.getOutputStream());214wr.writeByte(TransportConstants.Return);// transport op215getOutputStream(true); // creates a MarshalOutputStream216// return header, part 2 (read by client-side RemoteCall)217if (success) //218out.writeByte(TransportConstants.NormalReturn);219else220out.writeByte(TransportConstants.ExceptionalReturn);221out.writeID(); // write id for gcAck222return out;223}224225/**226* Do whatever it takes to execute the call.227*/228@SuppressWarnings("fallthrough")229public void executeCall() throws Exception {230byte returnType;231232// read result header233DGCAckHandler ackHandler = null;234try {235if (out != null) {236ackHandler = out.getDGCAckHandler();237}238releaseOutputStream();239DataInputStream rd = new DataInputStream(conn.getInputStream());240byte op = rd.readByte();241if (op != TransportConstants.Return) {242if (Transport.transportLog.isLoggable(Log.BRIEF)) {243Transport.transportLog.log(Log.BRIEF,244"transport return code invalid: " + op);245}246throw new UnmarshalException("Transport return code invalid");247}248getInputStream();249returnType = in.readByte();250in.readID(); // id for DGC acknowledgement251} catch (UnmarshalException e) {252throw e;253} catch (IOException e) {254throw new UnmarshalException("Error unmarshaling return header",255e);256} finally {257if (ackHandler != null) {258ackHandler.release();259}260}261262// read return value263switch (returnType) {264case TransportConstants.NormalReturn:265break;266267case TransportConstants.ExceptionalReturn:268Object ex;269try {270ex = in.readObject();271} catch (Exception e) {272discardPendingRefs();273throw new UnmarshalException("Error unmarshaling return", e);274}275276// An exception should have been received,277// if so throw it, else flag error278if (ex instanceof Exception) {279exceptionReceivedFromServer((Exception) ex);280} else {281discardPendingRefs();282throw new UnmarshalException("Return type not Exception");283}284// Exception is thrown before fallthrough can occur285default:286if (Transport.transportLog.isLoggable(Log.BRIEF)) {287Transport.transportLog.log(Log.BRIEF,288"return code invalid: " + returnType);289}290throw new UnmarshalException("Return code invalid");291}292}293294/**295* Routine that causes the stack traces of remote exceptions to be296* filled in with the current stack trace on the client. Detail297* exceptions are filled in iteratively.298*/299protected void exceptionReceivedFromServer(Exception ex) throws Exception {300serverException = ex;301302StackTraceElement[] serverTrace = ex.getStackTrace();303StackTraceElement[] clientTrace = (new Throwable()).getStackTrace();304StackTraceElement[] combinedTrace =305new StackTraceElement[serverTrace.length + clientTrace.length];306System.arraycopy(serverTrace, 0, combinedTrace, 0,307serverTrace.length);308System.arraycopy(clientTrace, 0, combinedTrace, serverTrace.length,309clientTrace.length);310ex.setStackTrace(combinedTrace);311312/*313* Log the details of a server exception thrown as a result of a314* remote method invocation.315*/316if (UnicastRef.clientCallLog.isLoggable(Log.BRIEF)) {317/* log call exception returned from server before it is rethrown */318TCPEndpoint ep = (TCPEndpoint) conn.getChannel().getEndpoint();319UnicastRef.clientCallLog.log(Log.BRIEF, "outbound call " +320"received exception: [" + ep.getHost() + ":" +321ep.getPort() + "] exception: ", ex);322}323324throw ex;325}326327/*328* method to retrieve possible server side exceptions (which will329* be throw from exceptionReceivedFromServer(...) )330*/331public Exception getServerException() {332return serverException;333}334335public void done() throws IOException {336/* WARNING: Currently, the UnicastRef.java invoke methods rely337* upon this method not throwing an IOException.338*/339340releaseInputStream();341}342}343344345