JDK-6680198 : UnmarshalException caused by incompatible serialVersionUID
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.sql
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-03-26
  • Updated: 2011-02-16
  • Resolved: 2008-06-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u10 b26Fixed 7Fixed
Description
FULL PRODUCT VERSION :
Server
--------
Java version "1.5.0_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-b03)
Java HotSpot(TM) Server VM (build 1.5.0_07-b03, mixed mode)


Client
--------
Java Plug-in 1.6.0_05
Using JRE version 1.6.0_05 Java HotSpot(TM) Client VM

ADDITIONAL OS VERSION INFORMATION :
Windows XP SP2

EXTRA RELEVANT SYSTEM CONFIGURATION :
RMI server runtime is JRE 1.5.0_07
RMI Client runtime is JRE 1.6_05 (latest auto-update)

A DESCRIPTION OF THE PROBLEM :
We have a project that uses WebRowSetImpl class to pass data to the client using RMI. The code for both the RMI Server and RMI Client was developed in JDK 1.5.0. The RMI Server is deployed using 1.5.0_07. The Client (an applet) runs  java versions from 1.5.0_11 to 1.6.0_05 (depending on the user's java plug-in). The code for both RMI Server and Client was compiled on JDK 1.5.0

The application works fine for versions lesser than 1.6.0_05 (which throws a java.rmi.UnmarshalException due to incompatible serialVersionUID).

Here is the full StackTrace:
------------------------------------
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at com.company.project.client.ui.thread.projectWorker.run(projectWorker.java:66)
	at com.company.project.client.ui.thread.ThreadManager$WorkerThread.run(ThreadManager.java:118)
Caused by: java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at sun.rmi.server.UnicastRef.invoke(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
	at $Proxy2.execute(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	... 1 more
Caused by: java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
	at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
	at java.io.ObjectInputStream.readClassDesc(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
	at java.io.ObjectInputStream.readSerialData(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
	... 13 more


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
0. Please use the code from the 'Source Code' section of this bug report.
1. Run the RMIServer class using JRE 1.50_07.
2. Run the RMIClientApplet class using JRE 1.6.0_05 (preferrably from a java IDE like Netbeans. Deploying it as a web applet requires signing the applet jar).
3. The RMIClientApplet will through a java.rmi.UnmarshalException due to the incompatible classdesc serialVersionUID.

Sample StackTrace:
===============
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected a WebRowSet object which can be accessed as a ResultSet.
ACTUAL -
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122


ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at com.company.project.client.ui.thread.projectWorker.run(projectWorker.java:66)
	at com.company.project.client.ui.thread.ThreadManager$WorkerThread.run(ThreadManager.java:118)
Caused by: java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at sun.rmi.server.UnicastRef.invoke(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
	at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
	at $Proxy2.execute(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	... 1 more
Caused by: java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
	at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
	at java.io.ObjectInputStream.readClassDesc(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
	at java.io.ObjectInputStream.readSerialData(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
	... 13 more


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
RMIServer (Sample)
------------------------------
import com.sun.rowset.WebRowSetImpl;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.rowset.WebRowSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class RMIServer {

    private static final Log logger = LogFactory.getLog(RMIServer.class);
    
    public static void main(String[] args) {
        new RMIServer();
    }
    
    public RMIServer() {
        
        try {
            Registry r = LocateRegistry.createRegistry(7500);

            DBInterfaceManager dbInterfaceManager = new DBInterfaceManagerImpl();
            r.rebind("dbInterfaceManager", dbInterfaceManager);
            
            logger.info("Registry created and the following Objects have been bound...");

            for (String service: r.list()) {
                logger.info(service);
            }


        } catch (Throwable t) {
            
            t.printStackTrace();
            logger.error(t.getMessage());

            // Don't start RMI service
            System.exit(-1);
        }
    }
    
    public class DBInterfaceManagerImpl extends UnicastRemoteObject implements DBInterfaceManager {
        
        public DBInterfaceManagerImpl() throws RemoteException {
        }

        public ResultSet executeQuery(String query) throws RemoteException {

            Connection con = null;

            try {
                logger.debug(query);
                
                con = DBConnectionPool.getConnection();

                WebRowSet wRS = new WebRowSetImpl();

                Statement stmt = con.createStatement();
                logger.debug("About to execute.");

                wRS.populate(stmt.executeQuery(query));

                logger.debug("rs populated.");

                return wRS;

            } catch (SQLException sqle) {
                
                logger.error(query);
                logger.error(sqle.getMessage());
                throw new RemoteException(sqle.getMessage());

            } finally {

                if (con != null)
                    DBConnectionPool.releaseConnection(con);
            }

        }
    }
    
    /**
     * PlaceHolder class
     */
    private static class DBConnectionPool {

        public static void init() {
            //Setup the Connection pool
        }
        
        public static synchronized Connection getConnection() throws SQLException {
            //Fetches connection from the Connection pool
            return null;
        }
        
        public static synchronized void releaseConnection(Connection con) {
            //Releases the connection to the Connection pool
        }
    }
}


========================================


RMIClient (Sample Applet)
====================

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

import java.security.Permission;

import java.sql.ResultSet;

import javax.swing.JApplet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class RMIClientApplet extends JApplet {

    private DBInterfaceManager dbM = null;

    private static final Log logger = LogFactory.getLog(RMIClientApplet.class);

    
    public void init() {

        System.setSecurityManager(new CustomRMISecurityManager());

        String host = getCodeBase().getHost();
        
        String rmiURL = "rmi://" + host + ":" + 7500 + "/";
        
        try {
            
            dbM = (DBInterfaceManager)Naming.lookup(rmiURL + "dbInterfaceManager");
            
            //Actual application uses a ThreadManager to run queries in background
            //instead of using the EventThread. The System breaks during the
            //Unmarshal phase of the RMI return.
            ResultSet rs = dbM.executeQuery("select sysdate from dual");
            
            if ( rs.next() ) {
            
                System.out.println(rs.getString(1));
            }
            
        } catch (Exception e) {
            
            logger.error(e);
        }
    }

    private static class CustomRMISecurityManager extends RMISecurityManager {
    
        public void checkConnect(String host, int port) {}
        public void checkCreateClassLoader() {}
        public void checkConnect(String host, int port, Object context) {}
        public void checkPermission(Permission perm, Object context) {}
        public void checkPermission(Permission perm) {}
    }
}

---------- END SOURCE ----------

Release Regression From : 6u4
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION Failure to deserialize the arguments or result of a remote invocation naturally causes a java.rmi.UnmarshalException. The serialVersionUID of class com.sun.rowset.providers.RIOptimisticProvider changed from -3143367176751761936L in 6u3 to -8429279896237029122L in 6u4. Unfortunately, it does not declare an explicit serialVersionUID, so some change affected the default serialVersionUID computation. It appears that a (serializable) field was added, "resBundle", which would have that effect. I'm not sure the extent to which instances of this class are expected to be serialized. Recategorizing to JDBC category.
26-03-2008