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