312 lines · 10.7 KB · Java
RawDownload
/* * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.rmi.transport; import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectOutput;import java.io.StreamCorruptedException;import java.rmi.RemoteException;import java.rmi.MarshalException;import java.rmi.UnmarshalException;import java.rmi.server.ObjID;import java.rmi.server.RemoteCall;import sun.rmi.runtime.Log;import sun.rmi.server.UnicastRef;import sun.rmi.transport.tcp.TCPEndpoint; /** * Stream-based implementation of the RemoteCall interface. * * @author Ann Wollrath */public class StreamRemoteCall implements RemoteCall {    private ConnectionInputStream in = null;    private ConnectionOutputStream out = null;    private Connection conn;    private boolean resultStarted = false;    private Exception serverException = null;     public StreamRemoteCall(Connection c) {        conn = c;    }     public StreamRemoteCall(Connection c, ObjID id, int op, long hash)        throws RemoteException    {        try {            conn = c;            Transport.transportLog.log(Log.VERBOSE,                "write remote call header...");             // write out remote call header info...            // call header, part 1 (read by Transport)            conn.getOutputStream().write(TransportConstants.Call);            getOutputStream();           // creates a MarshalOutputStream            id.write(out);               // object id (target of call)            // call header, part 2 (read by Dispatcher)            out.writeInt(op);            // method number (operation index)            out.writeLong(hash);         // stub/skeleton hash        } catch (IOException e) {            throw new MarshalException("Error marshaling call header", e);        }    }     /**     * Return the connection associated with this call.     */    public Connection getConnection() {        return conn;    }     /**     * Return the output stream the stub/skeleton should put arguments/results     * into.     */    public ObjectOutput getOutputStream() throws IOException {        return getOutputStream(false);    }     private ObjectOutput getOutputStream(boolean resultStream)        throws IOException    {        if (out == null) {            Transport.transportLog.log(Log.VERBOSE, "getting output stream");             out = new ConnectionOutputStream(conn, resultStream);        }        return out;    }     /**     * Release the outputStream  Currently, will not complain if the     * output stream is released more than once.     */    public void releaseOutputStream() throws IOException {        try {            if (out != null) {                try {                    out.flush();                } finally {                    out.done();         // always start DGC ack timer                }            }            conn.releaseOutputStream();        } finally {            out = null;        }    }     /**     * Get the InputStream the stub/skeleton should get results/arguments     * from.     */    public ObjectInput getInputStream() throws IOException {        if (in == null) {            Transport.transportLog.log(Log.VERBOSE, "getting input stream");             in = new ConnectionInputStream(conn.getInputStream());        }        return in;    }     /**     * Release the input stream, this would allow some transports to release     * the channel early.     */    public void releaseInputStream() throws IOException {        /* WARNING: Currently, the UnicastRef.java invoke methods rely         * upon this method not throwing an IOException.         */         try {            if (in != null) {                // execute MarshalInputStream "done" callbacks                try {                    in.done();                } catch (RuntimeException e) {                }                 // add saved references to DGC table                in.registerRefs();                 /* WARNING: The connection being passed to done may have                 * already been freed.                 */                in.done(conn);            }            conn.releaseInputStream();        } finally {            in = null;        }    }     /**     * Returns an output stream (may put out header information     * relating to the success of the call).     * @param success If true, indicates normal return, else indicates     * exceptional return.     * @exception StreamCorruptedException If result stream previously     * acquired     * @exception IOException For any other problem with I/O.     */    public ObjectOutput getResultStream(boolean success) throws IOException {        /* make sure result code only marshaled once. */        if (resultStarted)            throw new StreamCorruptedException("result already in progress");        else            resultStarted = true;         // write out return header        // return header, part 1 (read by Transport)        DataOutputStream wr = new DataOutputStream(conn.getOutputStream());        wr.writeByte(TransportConstants.Return);// transport op        getOutputStream(true);  // creates a MarshalOutputStream        // return header, part 2 (read by client-side RemoteCall)        if (success)            //            out.writeByte(TransportConstants.NormalReturn);        else            out.writeByte(TransportConstants.ExceptionalReturn);        out.writeID();          // write id for gcAck        return out;    }     /**     * Do whatever it takes to execute the call.     */    public void executeCall() throws Exception {        byte returnType;         // read result header        DGCAckHandler ackHandler = null;        try {            if (out != null) {                ackHandler = out.getDGCAckHandler();            }            releaseOutputStream();            DataInputStream rd = new DataInputStream(conn.getInputStream());            byte op = rd.readByte();            if (op != TransportConstants.Return) {                if (Transport.transportLog.isLoggable(Log.BRIEF)) {                    Transport.transportLog.log(Log.BRIEF,                        "transport return code invalid: " + op);                }                throw new UnmarshalException("Transport return code invalid");            }            getInputStream();            returnType = in.readByte();            in.readID();        // id for DGC acknowledgement        } catch (UnmarshalException e) {            throw e;        } catch (IOException e) {            throw new UnmarshalException("Error unmarshaling return header",                                         e);        } finally {            if (ackHandler != null) {                ackHandler.release();            }        }         // read return value        switch (returnType) {        case TransportConstants.NormalReturn:            break;         case TransportConstants.ExceptionalReturn:            Object ex;            try {                ex = in.readObject();            } catch (Exception e) {                throw new UnmarshalException("Error unmarshaling return", e);            }             // An exception should have been received,            // if so throw it, else flag error            if (ex instanceof Exception) {                exceptionReceivedFromServer((Exception) ex);            } else {                throw new UnmarshalException("Return type not Exception");            }        default:            if (Transport.transportLog.isLoggable(Log.BRIEF)) {                Transport.transportLog.log(Log.BRIEF,                    "return code invalid: " + returnType);            }            throw new UnmarshalException("Return code invalid");        }    }     /**     * Routine that causes the stack traces of remote exceptions to be     * filled in with the current stack trace on the client.  Detail     * exceptions are filled in iteratively.     */    protected void exceptionReceivedFromServer(Exception ex) throws Exception {        serverException = ex;         StackTraceElement[] serverTrace = ex.getStackTrace();        StackTraceElement[] clientTrace = (new Throwable()).getStackTrace();        StackTraceElement[] combinedTrace =            new StackTraceElement[serverTrace.length + clientTrace.length];        System.arraycopy(serverTrace, 0, combinedTrace, 0,                         serverTrace.length);        System.arraycopy(clientTrace, 0, combinedTrace, serverTrace.length,                         clientTrace.length);        ex.setStackTrace(combinedTrace);         /*         * Log the details of a server exception thrown as a result of a         * remote method invocation.         */        if (UnicastRef.clientCallLog.isLoggable(Log.BRIEF)) {            /* log call exception returned from server before it is rethrown */            TCPEndpoint ep = (TCPEndpoint) conn.getChannel().getEndpoint();            UnicastRef.clientCallLog.log(Log.BRIEF, "outbound call " +                "received exception: [" + ep.getHost() + ":" +                ep.getPort() + "] exception: ", ex);        }         throw ex;    }     /*     * method to retrieve possible server side exceptions (which will     * be throw from exceptionReceivedFromServer(...) )     */    public Exception getServerException() {        return serverException;    }     public void done() throws IOException {        /* WARNING: Currently, the UnicastRef.java invoke methods rely         * upon this method not throwing an IOException.         */         releaseInputStream();    }}