Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6358365

(cal) REGRESSION: NPE in GregCal getActualMaximum() in JDK1.5.0_06 (serialization)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Duplicate
    • P3
    • None
    • 5.0
    • core-libs

    Description

      FULL PRODUCT VERSION :
      java version "1.5.0_06"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
      Java HotSpot(TM) Server VM (build 1.5.0_06-b05, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      Linux [host] 2.4.21-37.ELsmp #1 SMP Wed Sep 7 13:28:55 EDT 2005 i686 i686 i386 GNU/Linux


      A DESCRIPTION OF THE PROBLEM :
      This was working in 1.5.0_05, and is broken in 1.5.0_06: If you serialize and then de-serialize a GregorianCalendar object, calling getActualMaximum() or getActualMinimum() will cause a NullPointerException to be thrown. As an example we have line 1608 of GregorianCalendar.getActualMaximum():

      1605: GregorianCalendar gc = getNormalizedCalendar();
      1606: BaseCalendar.Date date = gc.cdate;
      1607: BaseCalendar cal = gc.calsys;
      1608: int normalizedYear = date.getNormalizedYear();

      The "date" variable is null on line 1608, as the call to getNormalizedCalendar() did not set the "cdate" field of "gc": In getNormalizedCalendar() we have:

      2604: gc = (GregorianCalendar) this.clone();
      2605: gc.setLenient(true);
      2606: gc.complete();

      Line 2604 will clone "this", where "this" has a null "cdate": it is a transient field that was lost when we serialized the object. In the "cdate" declaration in GregorianCalendar, there is a comment stating that it is *guaranteed* to be set after complete() is called (as we do on line 2606, above). The complete() method belongs to Calendar, and calls back into GregorianCalendar.computeFields(). The diff for computeFields() from 1.5.0_05 to 1.5.0_06 is:

      1963,1967c1963,1967
      < mask |= computeFields(fieldMask,
      < (mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK))
      < == (ZONE_OFFSET_MASK|DST_OFFSET_MASK) ? fields : null,
      < ZONE_OFFSET);
      < assert mask == ALL_FIELDS;
      ---
      > if (fieldMask != 0) {
      > mask |= computeFields(fieldMask,
      > mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
      > assert mask == ALL_FIELDS;
      > }

      The lower section of code is 1.5.0_06, and when called "fieldMask" is 0. On the surface it makes sense to not call GregorianDate.computeFields() when "fieldMask" is 0, since there are no fields to compute. However, computeFields() has a side effect:

      2065: cdate = gdate;
      [...or...]
      2070: cdate = (BaseCalendar.Date) jcal.newCalendarDate(getZone());

      That is, it sets our "cdate" field, preventing the NullPointerException we originally received. In the code from 1.5.0_05 in the above diff, we see that computeFields() is always called, even when not needed, as it sets inner state of the class.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1) Create a GregorianCalendar object and set its time;
      2) Serialize the object;
      3) De-serialize the object; and
      4) Call getActualMaximum on the new object.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      % javac Test.java; java Test
      Before serialization: 31
      Serialized OK
      After serialization: 31
      ACTUAL -
      % javac Test.java; java Test
      Before serialization: 31
      Serialized OK
      After serialization: Exception in thread "main" java.lang.NullPointerException
              at java.util.GregorianCalendar.getActualMaximum(GregorianCalendar.java:1608)
              at Test.main(Test.java:27)

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Exception in thread "main" java.lang.NullPointerException
              at java.util.GregorianCalendar.getActualMaximum(GregorianCalendar.java:1608)
              at Test.main(Test.java:27)

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------

      import java.io.*;
      import java.util.*;


      public class Test
      {
          public static void main(String [] argv)
                  throws Exception
          {
              GregorianCalendar calendar = new GregorianCalendar();
              calendar.setTime(new Date());
              System.err.println("Before serialization: " +
                      calendar.getActualMaximum(Calendar.DATE));

              ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
              new ObjectOutputStream(outputStream).writeObject(calendar);

              byte[] serialized = outputStream.toByteArray();
              System.err.println("Serialized OK");

              ObjectInputStream inputStream = new ObjectInputStream(
                       new ByteArrayInputStream(serialized));
              calendar = (GregorianCalendar)inputStream.readObject();

              System.err.print("After serialization: ");
              System.err.println(calendar.getActualMaximum(Calendar.DATE));
          }
      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      After de-serializing a GregorianCalendar, call:

              calendar.setTime(calendar.getTime());

      Although a simple workaround exists, it is not practical in our case. We are seeing the error after the unmarshalling of arguments when using remote J2EE session beans. As such we don't have much control over implementing the above workaround. A more complicated workaround could be created for our specific case, but we have decided to stay with 1.5.0_05 until the problem is fixed.

      Release Regression From : 5.0u5
      The above release value was the last known release where this
      bug was known to work. Since then there has been a regression.

      Attachments

        Issue Links

          Activity

            People

              okutsu Masayoshi Okutsu
              jssunw Jitender S (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: