001: /*
002: * Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.rmi.transport;
027:
028: import java.io.DataInputStream;
029: import java.io.DataOutputStream;
030: import java.io.IOException;
031: import java.io.ObjectInput;
032: import java.io.ObjectOutput;
033: import java.io.StreamCorruptedException;
034: import java.rmi.RemoteException;
035: import java.rmi.MarshalException;
036: import java.rmi.UnmarshalException;
037: import java.rmi.server.ObjID;
038: import java.rmi.server.RemoteCall;
039: import sun.rmi.runtime.Log;
040: import sun.rmi.server.UnicastRef;
041: import sun.rmi.transport.tcp.TCPEndpoint;
042:
043: /**
044: * Stream-based implementation of the RemoteCall interface.
045: *
046: * @author Ann Wollrath
047: */
048: public class StreamRemoteCall implements RemoteCall {
049: private ConnectionInputStream in = null;
050: private ConnectionOutputStream out = null;
051: private Connection conn;
052: private boolean resultStarted = false;
053: private Exception serverException = null;
054:
055: public StreamRemoteCall(Connection c) {
056: conn = c;
057: }
058:
059: public StreamRemoteCall(Connection c, ObjID id, int op, long hash)
060: throws RemoteException {
061: try {
062: conn = c;
063: Transport.transportLog.log(Log.VERBOSE,
064: "write remote call header...");
065:
066: // write out remote call header info...
067: // call header, part 1 (read by Transport)
068: conn.getOutputStream().write(TransportConstants.Call);
069: getOutputStream(); // creates a MarshalOutputStream
070: id.write(out); // object id (target of call)
071: // call header, part 2 (read by Dispatcher)
072: out.writeInt(op); // method number (operation index)
073: out.writeLong(hash); // stub/skeleton hash
074: } catch (IOException e) {
075: throw new MarshalException("Error marshaling call header",
076: e);
077: }
078: }
079:
080: /**
081: * Return the connection associated with this call.
082: */
083: public Connection getConnection() {
084: return conn;
085: }
086:
087: /**
088: * Return the output stream the stub/skeleton should put arguments/results
089: * into.
090: */
091: public ObjectOutput getOutputStream() throws IOException {
092: return getOutputStream(false);
093: }
094:
095: private ObjectOutput getOutputStream(boolean resultStream)
096: throws IOException {
097: if (out == null) {
098: Transport.transportLog.log(Log.VERBOSE,
099: "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,
132: "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: * Returns an output stream (may put out header information
172: * relating to the success of the call).
173: * @param success If true, indicates normal return, else indicates
174: * exceptional return.
175: * @exception StreamCorruptedException If result stream previously
176: * acquired
177: * @exception IOException For any other problem with I/O.
178: */
179: public ObjectOutput getResultStream(boolean success)
180: throws IOException {
181: /* make sure result code only marshaled once. */
182: if (resultStarted)
183: throw new StreamCorruptedException(
184: "result already in progress");
185: else
186: resultStarted = true;
187:
188: // write out return header
189: // return header, part 1 (read by Transport)
190: DataOutputStream wr = new DataOutputStream(conn
191: .getOutputStream());
192: wr.writeByte(TransportConstants.Return);// transport op
193: getOutputStream(true); // creates a MarshalOutputStream
194: // return header, part 2 (read by client-side RemoteCall)
195: if (success) //
196: out.writeByte(TransportConstants.NormalReturn);
197: else
198: out.writeByte(TransportConstants.ExceptionalReturn);
199: out.writeID(); // write id for gcAck
200: return out;
201: }
202:
203: /**
204: * Do whatever it takes to execute the call.
205: */
206: public void executeCall() throws Exception {
207: byte returnType;
208:
209: // read result header
210: DGCAckHandler ackHandler = null;
211: try {
212: if (out != null) {
213: ackHandler = out.getDGCAckHandler();
214: }
215: releaseOutputStream();
216: DataInputStream rd = new DataInputStream(conn
217: .getInputStream());
218: byte op = rd.readByte();
219: if (op != TransportConstants.Return) {
220: if (Transport.transportLog.isLoggable(Log.BRIEF)) {
221: Transport.transportLog.log(Log.BRIEF,
222: "transport return code invalid: " + op);
223: }
224: throw new UnmarshalException(
225: "Transport return code invalid");
226: }
227: getInputStream();
228: returnType = in.readByte();
229: in.readID(); // id for DGC acknowledgement
230: } catch (UnmarshalException e) {
231: throw e;
232: } catch (IOException e) {
233: throw new UnmarshalException(
234: "Error unmarshaling return header", e);
235: } finally {
236: if (ackHandler != null) {
237: ackHandler.release();
238: }
239: }
240:
241: // read return value
242: switch (returnType) {
243: case TransportConstants.NormalReturn:
244: break;
245:
246: case TransportConstants.ExceptionalReturn:
247: Object ex;
248: try {
249: ex = in.readObject();
250: } catch (Exception e) {
251: throw new UnmarshalException(
252: "Error unmarshaling return", e);
253: }
254:
255: // An exception should have been received,
256: // if so throw it, else flag error
257: if (ex instanceof Exception) {
258: exceptionReceivedFromServer((Exception) ex);
259: } else {
260: throw new UnmarshalException(
261: "Return type not Exception");
262: }
263: default:
264: if (Transport.transportLog.isLoggable(Log.BRIEF)) {
265: Transport.transportLog.log(Log.BRIEF,
266: "return code invalid: " + returnType);
267: }
268: throw new UnmarshalException("Return code invalid");
269: }
270: }
271:
272: /**
273: * Routine that causes the stack traces of remote exceptions to be
274: * filled in with the current stack trace on the client. Detail
275: * exceptions are filled in iteratively.
276: */
277: protected void exceptionReceivedFromServer(Exception ex)
278: throws Exception {
279: serverException = ex;
280:
281: StackTraceElement[] serverTrace = ex.getStackTrace();
282: StackTraceElement[] clientTrace = (new Throwable())
283: .getStackTrace();
284: StackTraceElement[] combinedTrace = new StackTraceElement[serverTrace.length
285: + clientTrace.length];
286: System.arraycopy(serverTrace, 0, combinedTrace, 0,
287: serverTrace.length);
288: System.arraycopy(clientTrace, 0, combinedTrace,
289: serverTrace.length, 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()
299: .getEndpoint();
300: UnicastRef.clientCallLog.log(Log.BRIEF, "outbound call "
301: + "received exception: [" + ep.getHost() + ":"
302: + ep.getPort() + "] exception: ", ex);
303: }
304:
305: throw ex;
306: }
307:
308: /*
309: * method to retrieve possible server side exceptions (which will
310: * be throw from exceptionReceivedFromServer(...) )
311: */
312: public Exception getServerException() {
313: return serverException;
314: }
315:
316: public void done() throws IOException {
317: /* WARNING: Currently, the UnicastRef.java invoke methods rely
318: * upon this method not throwing an IOException.
319: */
320:
321: releaseInputStream();
322: }
323: }
|