1/* 2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.rmi.transport; 27 28import java.io.DataInputStream; 29import java.io.DataOutputStream; 30import java.io.IOException; 31import java.io.ObjectInput; 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 sun.rmi.runtime.Log; 40import sun.rmi.server.UnicastRef; 41import sun.rmi.transport.tcp.TCPEndpoint; 42 43/** 44 * Stream-based implementation of the RemoteCall interface. 45 * 46 * @author Ann Wollrath 47 */ 48@SuppressWarnings("deprecation") 49public class StreamRemoteCall implements RemoteCall { 50 private ConnectionInputStream in = null; 51 private ConnectionOutputStream out = null; 52 private Connection conn; 53 private boolean resultStarted = false; 54 private Exception serverException = null; 55 56 public StreamRemoteCall(Connection c) { 57 conn = c; 58 } 59 60 public StreamRemoteCall(Connection c, ObjID id, int op, long hash) 61 throws RemoteException 62 { 63 try { 64 conn = c; 65 Transport.transportLog.log(Log.VERBOSE, 66 "write remote call header..."); 67 68 // write out remote call header info... 69 // call header, part 1 (read by Transport) 70 conn.getOutputStream().write(TransportConstants.Call); 71 getOutputStream(); // creates a MarshalOutputStream 72 id.write(out); // object id (target of call) 73 // call header, part 2 (read by Dispatcher) 74 out.writeInt(op); // method number (operation index) 75 out.writeLong(hash); // stub/skeleton hash 76 } catch (IOException e) { 77 throw new MarshalException("Error marshaling call header", e); 78 } 79 } 80 81 /** 82 * Return the connection associated with this call. 83 */ 84 public Connection getConnection() { 85 return conn; 86 } 87 88 /** 89 * Return the output stream the stub/skeleton should put arguments/results 90 * into. 91 */ 92 public ObjectOutput getOutputStream() throws IOException { 93 return getOutputStream(false); 94 } 95 96 private ObjectOutput getOutputStream(boolean resultStream) 97 throws IOException 98 { 99 if (out == null) { 100 Transport.transportLog.log(Log.VERBOSE, "getting output stream"); 101 102 out = new ConnectionOutputStream(conn, resultStream); 103 } 104 return out; 105 } 106 107 /** 108 * Release the outputStream Currently, will not complain if the 109 * output stream is released more than once. 110 */ 111 public void releaseOutputStream() throws IOException { 112 try { 113 if (out != null) { 114 try { 115 out.flush(); 116 } finally { 117 out.done(); // always start DGC ack timer 118 } 119 } 120 conn.releaseOutputStream(); 121 } finally { 122 out = null; 123 } 124 } 125 126 /** 127 * Get the InputStream the stub/skeleton should get results/arguments 128 * from. 129 */ 130 public ObjectInput getInputStream() throws IOException { 131 if (in == null) { 132 Transport.transportLog.log(Log.VERBOSE, "getting input stream"); 133 134 in = new ConnectionInputStream(conn.getInputStream()); 135 } 136 return in; 137 } 138 139 /** 140 * Release the input stream, this would allow some transports to release 141 * the channel early. 142 */ 143 public void releaseInputStream() throws IOException { 144 /* WARNING: Currently, the UnicastRef.java invoke methods rely 145 * upon this method not throwing an IOException. 146 */ 147 148 try { 149 if (in != null) { 150 // execute MarshalInputStream "done" callbacks 151 try { 152 in.done(); 153 } catch (RuntimeException e) { 154 } 155 156 // add saved references to DGC table 157 in.registerRefs(); 158 159 /* WARNING: The connection being passed to done may have 160 * already been freed. 161 */ 162 in.done(conn); 163 } 164 conn.releaseInputStream(); 165 } finally { 166 in = null; 167 } 168 } 169 170 /** 171 * Discard any post-processing of refs the InputStream. 172 */ 173 public void discardPendingRefs() { 174 in.discardRefs(); 175 } 176 177 /** 178 * Returns an output stream (may put out header information 179 * relating to the success of the call). 180 * @param success If true, indicates normal return, else indicates 181 * exceptional return. 182 * @exception StreamCorruptedException If result stream previously 183 * acquired 184 * @exception IOException For any other problem with I/O. 185 */ 186 public ObjectOutput getResultStream(boolean success) throws IOException { 187 /* make sure result code only marshaled once. */ 188 if (resultStarted) 189 throw new StreamCorruptedException("result already in progress"); 190 else 191 resultStarted = true; 192 193 // write out return header 194 // return header, part 1 (read by Transport) 195 DataOutputStream wr = new DataOutputStream(conn.getOutputStream()); 196 wr.writeByte(TransportConstants.Return);// transport op 197 getOutputStream(true); // creates a MarshalOutputStream 198 // return header, part 2 (read by client-side RemoteCall) 199 if (success) // 200 out.writeByte(TransportConstants.NormalReturn); 201 else 202 out.writeByte(TransportConstants.ExceptionalReturn); 203 out.writeID(); // write id for gcAck 204 return out; 205 } 206 207 /** 208 * Do whatever it takes to execute the call. 209 */ 210 @SuppressWarnings("fallthrough") 211 public void executeCall() throws Exception { 212 byte returnType; 213 214 // read result header 215 DGCAckHandler ackHandler = null; 216 try { 217 if (out != null) { 218 ackHandler = out.getDGCAckHandler(); 219 } 220 releaseOutputStream(); 221 DataInputStream rd = new DataInputStream(conn.getInputStream()); 222 byte op = rd.readByte(); 223 if (op != TransportConstants.Return) { 224 if (Transport.transportLog.isLoggable(Log.BRIEF)) { 225 Transport.transportLog.log(Log.BRIEF, 226 "transport return code invalid: " + op); 227 } 228 throw new UnmarshalException("Transport return code invalid"); 229 } 230 getInputStream(); 231 returnType = in.readByte(); 232 in.readID(); // id for DGC acknowledgement 233 } catch (UnmarshalException e) { 234 throw e; 235 } catch (IOException e) { 236 throw new UnmarshalException("Error unmarshaling return header", 237 e); 238 } finally { 239 if (ackHandler != null) { 240 ackHandler.release(); 241 } 242 } 243 244 // read return value 245 switch (returnType) { 246 case TransportConstants.NormalReturn: 247 break; 248 249 case TransportConstants.ExceptionalReturn: 250 Object ex; 251 try { 252 ex = in.readObject(); 253 } catch (Exception e) { 254 throw new UnmarshalException("Error unmarshaling return", e); 255 } 256 257 // An exception should have been received, 258 // if so throw it, else flag error 259 if (ex instanceof Exception) { 260 exceptionReceivedFromServer((Exception) ex); 261 } else { 262 throw new UnmarshalException("Return type not Exception"); 263 } 264 // Exception is thrown before fallthrough can occur 265 default: 266 if (Transport.transportLog.isLoggable(Log.BRIEF)) { 267 Transport.transportLog.log(Log.BRIEF, 268 "return code invalid: " + returnType); 269 } 270 throw new UnmarshalException("Return code invalid"); 271 } 272 } 273 274 /** 275 * Routine that causes the stack traces of remote exceptions to be 276 * filled in with the current stack trace on the client. Detail 277 * exceptions are filled in iteratively. 278 */ 279 protected void exceptionReceivedFromServer(Exception ex) throws Exception { 280 serverException = ex; 281 282 StackTraceElement[] serverTrace = ex.getStackTrace(); 283 StackTraceElement[] clientTrace = (new Throwable()).getStackTrace(); 284 StackTraceElement[] combinedTrace = 285 new StackTraceElement[serverTrace.length + clientTrace.length]; 286 System.arraycopy(serverTrace, 0, combinedTrace, 0, 287 serverTrace.length); 288 System.arraycopy(clientTrace, 0, combinedTrace, serverTrace.length, 289 clientTrace.length); 290 ex.setStackTrace(combinedTrace); 291 292 /* 293 * Log the details of a server exception thrown as a result of a 294 * remote method invocation. 295 */ 296 if (UnicastRef.clientCallLog.isLoggable(Log.BRIEF)) { 297 /* log call exception returned from server before it is rethrown */ 298 TCPEndpoint ep = (TCPEndpoint) conn.getChannel().getEndpoint(); 299 UnicastRef.clientCallLog.log(Log.BRIEF, "outbound call " + 300 "received exception: [" + ep.getHost() + ":" + 301 ep.getPort() + "] exception: ", ex); 302 } 303 304 throw ex; 305 } 306 307 /* 308 * method to retrieve possible server side exceptions (which will 309 * be throw from exceptionReceivedFromServer(...) ) 310 */ 311 public Exception getServerException() { 312 return serverException; 313 } 314 315 public void done() throws IOException { 316 /* WARNING: Currently, the UnicastRef.java invoke methods rely 317 * upon this method not throwing an IOException. 318 */ 319 320 releaseInputStream(); 321 } 322} 323