JDK-4802409 : can't deserialize java.util.GregorianCalendar
  • Type: Bug
  • Component: other-libs
  • Sub-Component: corba:idl
  • Affected Version: 1.4.2
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_2.6,windows_2000
  • CPU: generic,x86
  • Submitted: 2003-01-13
  • Updated: 2009-06-25
  • Resolved: 2003-03-26
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.
Other Other
1.4.1 03Fixed 1.4.2Fixed
Related Reports
Relates :  
Description
The simple CORBA example program below fails when deserializing the java.util.GregorianCalendar.

The reason for the failure is the way java.util.Calendar is written to the stream and read back. As shown in the code snippet below, java.util.Calendar.writeObject() writes optional ZoneInfo into the stream. In the example below, the ZoneInfo is null, so nothing is actually written to the stream. The java.util.Calendar.readObject(), in turn, tries to read this optional data via stream.readObject(). If the data is not in the stream, the exception is thrown but java.util.Calendar.readObject() ignores this exception.

However, the side effect of calling readObject on the IIOP stream ( com.sun.corba.se.internal.io.IIOPInputStream ) in the above scenario is that the offset in the stream gets corrupted. So, the next call to the readObject() of  java.util.GregorianCalendar results in a failure with an exception.

Code Snippets of java.util.Calendar readObject() and writeObject():

writeObject(ObjectOutputStream)
-------------------------------
     TimeZone savedZone = null;
     if (zone instanceof ZoneInfo) {
         SimpleTimeZone stz = ((ZoneInfo)zone).getLastRuleInstance();
         if (stz == null) {
           stz = new SimpleTimeZone(zone.getRawOffset(), zone.getID());
         }
         savedZone = zone;
         zone = stz;
     }
       stream.defaultWriteObject();

     if (savedZone != null) {
         stream.writeObject(savedZone);//not written in our case as savedZone is null
         zone = savedZone;
     }

readObject(ObjectInputStream)
-----------------------------

     stream.defaultReadObject();
     try {
        ZoneInfo zi = (ZoneInfo) stream.readObject();//this is wrong as ZoneInfo was never written to the stream in the         first place
         if (zi != null) {
           zone = zi;
         }
     } catch (Exception e) {
     //marshal exception is thrown and the stream is corrupted,SUN ignores this exception and continues.
     }

STEPS TO REPRODUCE THE PROBLEM (jdk 1.4.2) (all files are attached in testcase.zip)
----------------------------------------------------------------------------------------------

1. Extract the files to a directory and compile(javac -d . *.java)
2. Do rmic -iiop test.HelloImpl

3. Start the NamingService (orbd) start orbd -ORBInitialPort 1050

4. Run the Server
  java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
  -Djava.naming.provider.url=iiop://localhost:1050
  test.HelloServer
5. Run the Client   java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
  -Djava.naming.provider.url=iiop://localhost:1050
  test.HelloClient
  6. You will see a Marshal Exception in the Server which fails to read the data that is marshaled by the Client.

The stack of the Exception is something like this.

org.omg.CORBA.MARSHAL: Unmarshaller requested more data after end of stream  vmcid: SUN  minor code: 207  completed: No
    at com.sun.corba.se.internal.iiop.BufferManagerReadStream.underflow(BufferManagerReadStream.java:56)
    at com.sun.corba.se.internal.iiop.CDRInputStream_1_1.grow(CDRInputStream_1_1.java:73)
    at com.sun.corba.se.internal.iiop.CDRInputStream_1_2.alignAndCheck(CDRInputStream_1_2.java:28)
    at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_longlong(CDRInputStream_1_0.java:409)
    at com.sun.corba.se.internal.iiop.CDRInputStream.read_longlong(CDRInputStream.java:181)
    at com.sun.corba.se.internal.io.IIOPInputStream.inputPrimitiveField(IIOPInputStream.java:1373)
    at com.sun.corba.se.internal.io.IIOPInputStream.inputClassFields(IIOPInputStream.java:1774)
    at com.sun.corba.se.internal.io.IIOPInputStream.defaultReadObjectDelegate(IIOPInputStream.java:436)
    at com.sun.corba.se.internal.io.InputStreamHook.defaultReadObject(InputStreamHook.java:163)
    at java.util.GregorianCalendar.readObject(GregorianCalendar.java:2122)
    at com.sun.corba.se.internal.io.IIOPInputStream.readObject(Native Method)
    at com.sun.corba.se.internal.io.IIOPInputStream.invokeObjectReader(IIOPInputStream.java:1298)
    at com.sun.corba.se.internal.io.IIOPInputStream.inputObject(IIOPInputStream.java:908)
    at com.sun.corba.se.internal.io.IIOPInputStream.simpleReadObject(IIOPInputStream.java:261)
    at com.sun.corba.se.internal.io.ValueHandlerImpl.readValueInternal(ValueHandlerImpl.java:247)
    at com.sun.corba.se.internal.io.ValueHandlerImpl.readValue(ValueHandlerImpl.java:209)
    at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:1075)
    at com.sun.corba.se.internal.iiop.CDRInputStream.read_value(CDRInputStream.java:293)
    at com.sun.corba.se.internal.io.IIOPInputStream.inputObjectField(IIOPInputStream.java:1571)
    at com.sun.corba.se.internal.io.IIOPInputStream.inputClassFields(IIOPInputStream.java:1784)
    at com.sun.corba.se.internal.io.IIOPInputStream.inputObject(IIOPInputStream.java:913)
    at com.sun.corba.se.internal.io.IIOPInputStream.simpleReadObject(IIOPInputStream.java:261)
    at com.sun.corba.se.internal.io.ValueHandlerImpl.readValueInternal(ValueHandlerImpl.java:247)
    at com.sun.corba.se.internal.io.ValueHandlerImpl.readValue(ValueHandlerImpl.java:209)
    at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:1075)
    at com.sun.corba.se.internal.iiop.CDRInputStream.read_value(CDRInputStream.java:293)
    at test._HelloImpl_Tie._invoke(_HelloImpl_Tie.java:64)
    at com.sun.corba.se.internal.corba.ServerDelegate.dispatch(ServerDelegate.java:352)
    at com.sun.corba.se.internal.iiop.ORB.process(ORB.java:252)
    at com.sun.corba.se.internal.iiop.RequestProcessor.process(RequestProcessor.java:81)
    at com.sun.corba.se.internal.orbutil.ThreadPool$PooledThread.run(ThreadPool.java:106)

Also, having modified the java.util.Calendar.readObject() to print the ignored exception, the following exception is observed:

org.omg.CORBA.MARSHAL: Bad string length: -110842880  vmcid: SUN  minor code: 218 completed: Maybe
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.checkForNegativeLength(CDRInputStream_1_0.java:436)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.readStringOrIndirection(CDRInputStream_1_0.java:455)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_string(CDRInputStream_1_0.java:526)
   at com.sun.corba.se.internal.iiop.CDRInputStream.read_string(CDRInputStream.java:197)
   at com.sun.corba.se.internal.ior.IOR.<init>(IOR.java:161)
   at com.sun.corba.se.internal.core.IOR.<init>(IOR.java:224)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_Object(CDRInputStream_1_0.java:652)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_abstract_interface(CDRInputStream_1_0.java:857)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_abstract_interface(CDRInputStream_1_0.java:851)
   at com.sun.corba.se.internal.iiop.CDRInputStream.read_abstract_interface(CDRInputStream.java:309)
   at com.sun.corba.se.internal.io.IIOPInputStream.readObjectDelegate(IIOPInputStream.java:228)
   at com.sun.corba.se.internal.io.IIOPInputStream.readObjectOverride(IIOPInputStream.java:381)
   at java.io.ObjectInputStream.readObject(ObjectInputStream.java:318)
   at java.util.Calendar.readObject(Calendar.java:1707)
   at com.sun.corba.se.internal.io.IIOPInputStream.readObject(Native Method)
   at com.sun.corba.se.internal.io.IIOPInputStream.invokeObjectReader(IIOPInputStream.java:1298)
   at com.sun.corba.se.internal.io.IIOPInputStream.inputObject(IIOPInputStream.java:908)
   at com.sun.corba.se.internal.io.IIOPInputStream.simpleReadObject(IIOPInputStream.java:261)
   at com.sun.corba.se.internal.io.ValueHandlerImpl.readValueInternal(ValueHandlerImpl.java:247)
   at com.sun.corba.se.internal.io.ValueHandlerImpl.readValue(ValueHandlerImpl.java:209)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:1084)
   at com.sun.corba.se.internal.iiop.CDRInputStream.read_value(CDRInputStream.java:293)
   at com.sun.corba.se.internal.io.IIOPInputStream.inputObjectField(IIOPInputStream.java:1577)
   at com.sun.corba.se.internal.io.IIOPInputStream.inputClassFields(IIOPInputStream.java:1796)
   at com.sun.corba.se.internal.io.IIOPInputStream.inputObject(IIOPInputStream.java:913)
   at com.sun.corba.se.internal.io.IIOPInputStream.simpleReadObject(IIOPInputStream.java:261)
   at com.sun.corba.se.internal.io.ValueHandlerImpl.readValueInternal(ValueHandlerImpl.java:247)
   at com.sun.corba.se.internal.io.ValueHandlerImpl.readValue(ValueHandlerImpl.java:209)
   at com.sun.corba.se.internal.iiop.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:1084)
   at com.sun.corba.se.internal.iiop.CDRInputStream.read_value(CDRInputStream.java:293)
   at test._HelloImpl_Tie._invoke(Unknown Source)
   at com.sun.corba.se.internal.corba.ServerDelegate.dispatch(ServerDelegate.java:353)
   at com.sun.corba.se.internal.iiop.ORB.process(ORB.java:252)
   at com.sun.corba.se.internal.iiop.RequestProcessor.process(RequestProcessor.java:81)
   at com.sun.corba.se.internal.orbutil.ThreadPool$PooledThread.run(ThreadPool.java:106)

INTERESTING NOTE:
-------------------------
The same is not reproducible when writing/reading the same GregorianCalendar object to a conventional ObjectOutputStream. It looks like the stream doesn't get corrupted in such case. The ignored exception from java.util.Calendar.readObject() looks quite differently too:

java.io.OptionalDataException
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1294)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:324)
        at java.util.Calendar.readObject(Calendar.java:1707)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:324)
        at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:838
)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1746)

        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1
646)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1274)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:324)
        at sertest.main(sertest.java:16)

This leads to a conlcusion that IIOPInputStream works improperly and corrupts the stream. 
So, either IIOPInputStream or java.util.Calendar writeObject() / readObject() need to be fixed.


Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.4.1_03 mantis-beta FIXED IN: 1.4.1_03 mantis-rc INTEGRATED IN: 1.4.1_03 mantis-b22 mantis-rc
14-06-2004

EVALUATION The fix is way too complex to go into 1.4.2. Will fix it in Tiger. ###@###.### 2003-01-21 When doing serialization CORBA put IIOPOutputStream the following sequences: 1) a boolean indicates a Object or a value. 2) if Object, the typeId(String) 3) if Object, the object itself When Deserialization, it will read: 1) boolean, true is Object 2) if Object, construct IOR using a typeId which is from IIOPInputStream 3) if Object, read in the Object Calendar's writeObject did not write anything if ZoneInfo is a null, so the deserialization failed to fetch typeID, throws MASHAL exception which is not handled then. The stream seems corrupted. The same happens with Mantis latest. Fixing in Calendar, see suggested fix, this will make readObject get a null correctly if it is null at serialization. ###@###.### 2003-03-13 A new bug 4844924 filed against real fix in Tiger in IIOP. This fix is a workaround fix, and for mixed usage of 1.3.1/1.4 it won't work --- the real fix should fix the problem. ###@###.### 2003-04-08
08-04-2003

WORK AROUND Use TimeZone.getTimeZone() instead of creating a SimpleTimeZone object. For the test case in this bug report, the change is: *** HelloClient.java Fri Mar 14 17:46:12 2003 --- HelloClient.java.new Fri Mar 14 17:44:39 2003 *************** *** 29,35 **** hi = (HelloInterface) PortableRemoteObject.narrow( objref, HelloInterface.class); String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000); ! SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]); Calendar calendar = new GregorianCalendar(pdt); A d = new A(calendar); A c = hi.sayHello(d); --- 29,35 ---- hi = (HelloInterface) PortableRemoteObject.narrow( objref, HelloInterface.class); String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000); ! TimeZone pdt = TimeZone.getTimeZone(ids[0]); Calendar calendar = new GregorianCalendar(pdt); A d = new A(calendar); A c = hi.sayHello(d); If a TimeZone without daylight saving time is needed, a custom time zone can be specified (e.g., "GMT-08:00" for the test case). ###@###.### 2003-03-14
14-03-2003

SUGGESTED FIX http://jpsesvr.sfbay.sun.com:8080/ctetools/servlet/sun.cte.codeMngt.ViewDetail?op=view&id=600 ###@###.### 2003-03-13
13-03-2003