United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4671925 : (spec) ServerException cannot be thrown if there is a problem marshalling return

Details
Type:
Bug
Submit Date:
2002-04-20
Status:
Resolved
Updated Date:
2003-08-13
Project Name:
JDK
Resolved Date:
2003-08-13
Component:
core-libs
OS:
solaris_7
Sub-Component:
java.rmi
CPU:
sparc
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.0
Fixed Versions:
5.0 (tiger)

Related Reports
Relates:

Sub Tasks

Description
It is not clear that if IOException is thrown on server side in the
marshalling/unmarshalling process, what exception will be thrown on the client
side.

In current implementation, if an IOException occured unmarshalling remote call
parameters on server side, client will throw a ServerException with
UnmarshalException as the cause. This behavior is understandable as
ServerException should be used to wrap any RemoteException (UnmarshalException
in this case) thrown on server side.

However, if an IOException occured on marshalling return values on server side,
no MarshalException is thrown. Instead, the client will throw UnmarshalException
with a WriteAbortedException as the cause. This is hard to understand because
MarshalException wrapped by ServerException is more suitable in this case,
according to the spec.

The following program shows the behavior of current implementation:

import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.io.*;

public class MarshalTest {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(2000);
        MarshalExceptionImpl remoteObj = new MarshalExceptionImpl();
        Naming.bind("//:2000/test", remoteObj);
        MarshalExceptionRemote remote = (MarshalExceptionRemote) Naming.lookup("//:2000/test");
        System.out.println("IOException will happen on server when serializing the return object");
        try {
            remote.getObject(new SerialFailObject(false, false), false, true);
        } catch (RemoteException e) {
            System.out.println(e);
        }
        System.out.println();
        System.out.println("IOException will happen on server when deserializing parameters");
        try {
            remote.getObject(new SerialFailObject(true, false), false, false);
        } catch (RemoteException e) {
            System.out.println("Exception thrown at client side is:");
            System.out.println(e);
        }
    }
}

class SerialFailObject implements Serializable {

    boolean readFail;
    boolean writeFail;
        
    public SerialFailObject(boolean r, boolean w) {
        readFail = r;
        writeFail = w;
    }
    
    private void writeObject(java.io.ObjectOutputStream out) 
            throws IOException {
        out.defaultWriteObject();
        if (writeFail) {
            throw new IOException("IOException in serialization");
        }
    }
    
    private void readObject(java.io.ObjectInputStream in)
             throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        if (readFail) {
            throw new IOException("IOException in deserialization");
        }
    }
}

interface MarshalExceptionRemote extends Remote {
    public SerialFailObject getObject(SerialFailObject obj, boolean readFail, boolean writeFail)
        throws RemoteException;
}

class MarshalExceptionImpl extends UnicastRemoteObject implements MarshalExceptionRemote {

    public MarshalExceptionImpl() throws RemoteException {
    }
        
    public SerialFailObject getObject(SerialFailObject obj, boolean readFail, boolean writeFail) {
        return new SerialFailObject(readFail, writeFail);
    }
}

The output in build 1.4.1-beta-b09 is:

IOException will happen on server when serializing the return object
Exception thrown at client side:
java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
        java.io.WriteAbortedException: writing aborted; java.io.IOException: IOException in serialization

IOException will happen on server when deserializing parameters
Exception thrown at client side is:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
        java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
        java.io.IOException: IOException in deserialization

###@###.### 2002-04-19

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
tiger

FIXED IN:
tiger

INTEGRATED IN:
tiger
tiger-b15


                                     
2004-06-14
EVALUATION

The fact that the occurrence of a non-communication-related IOException while marshalling the result appears to the client as a java.rmi.UnmarshalException containing a java.io.WriteAbortedException is an artifact of the way that Object Serialization deals with IOExceptions that occur during the object writing process when it is still able to write more data to the stream.

In that case, ObjectOutputStream actually writes the IOException to the stream as an object preceded with a TC_EXCEPTION type code, and upon reading, the ObjectInputStream.readObject invocation that encounters the abrupt TC_EXCEPTION type code deserializes the exception object and throws it (assuming that the IOException can be written and read successfully) contained within a WriteAbortedException.  So this is the way that IOExceptions are communicated from writer to reader in the Object Serialization, and RMI pretty much follows that convention where it applies (which is basically for result marshalling/unmarshalling, as far as an application is concerned).

In summary, this behavior can be explained with the Object Serialization specification.

There is actually some confusion between the general specification for java.rmi.ServerException and how the JRMP implementation of RMI uses it:

 * A <code>ServerException</code> is thrown as a result of a remote method
 * invocation when a <code>RemoteException</code> is thrown while processing
 * the invocation on the server, either while unmarshalling the arguments,
 * executing the remote method itself, or marshalling the return value.

The JRMP implementation of RMI cannot actually perform the last case described in that specification, because once a given result (normal return or exceptional) starts getting marshalled with JRMP, if an exception occurs during that marshalling, there is no way to switch the result to a marshalled form of that exception such that the client will ignore the partially-written result and consider the following exception to be the real result of the invocation.  (It is not considered worthwhile to buffer the entire marshalled form of the result in order to pre-determine whether or not it can be marshalled without exception.)

###@###.### 2002-04-22

The specification of ServerException should be cleaned up to reflect
the actual RMI behavior.  A ServerException cannot be thrown if
there is a problem marshalling the return value.  This is due to
the behavior of object serialization (as explained above).  The text
"or marshalling the return value" should be removed from the
ServerException spec (in Tiger).

###@###.### 2002-07-16
                                     
2002-07-16



Hardware and Software, Engineered to Work Together