1 /*
2 * Copyright 1996-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 }