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

ClassNotFoundException-tolerant readObject() variant needed

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 1.3.0, 1.4.0
    • core-libs

      An earlier RFE (4139262) requested the addition of a skipObject() method to
      ObjectInputStream for performance reasons, and was (justifiably) rejected on
      the grounds that such a method would not result in significant performance
      gains. However, the proposed solution for bugs 4313167
      ("ClassNotFoundException in skipped objects causes serialization to fail") and
      4312433 ("reading back reference to obj with unresolved class should throw
      exception") reestablishes the need for skipObject().

      The proposed solution for bugs 4313167 and 4312433 involves ObjectInputStream
      maintaining an internal table mapping wire handles for deserialized objects to
      associated ClassNotFoundExceptions. ObjectInputStream must maintain this table
      by tracking dependencies between deserialized objects: for instance, if
      deserialized object A contains a reference to deserialized object B, and B's
      class cannot be resolved in the local VM, the appropriate
      ClassNotFoundException should be associated with the handles for both A and B
      (unless A's handle was already tagged with a previous exception). Back
      references complicate the picture somewhat; e.g., if object D contains a back
      reference to C, and an exception is associated with C sometime after D has
      already been deserialized (as the result of C referencing another object whose
      class is missing), the resulting exception must be propagated back to D's
      handle.

      Now consider the interaction of ClassNotFoundException tracking with
      class-defined readObject() methods. If a class defines a readObject() method
      which calls ObjectInputStream.readObject(), and OIS.readObject() throws a
      ClassNotFoundException, should the exception automatically be associated with
      the enclosing object? At first blush, the answer would seem to be no: the
      custom readObject() method may wish to tolerate the missing class, and can do
      so by catching and swallowing the ClassNotFoundException--i.e., the exception
      should be associated with the enclosing object only if the exception is thrown
      (directly or indirectly) from the object's custom readObject method.

      The problem with this approach is that it does not take into account objects
      whose exception status is not known until sometime after OIS.readObject()
      returns. More specifically, consider the case where the call to
      OIS.readObject() reads in a back reference and returns the (partially
      deserialized) contained object. Further suppose that later on, a
      ClassNotFoundException is associated with the contained object. Should this
      exception be propagated to the container object? If no, then delayed back
      reference exceptions will never be propagated--even for "intolerant" custom
      readObject() implementations. This is clearly unacceptable.

      The alternative, then, is for the delayed exception to always be propagated to
      the container object. This, however, clashes with the "catch and swallow"
      method of tolerating exceptions described above--immediate exceptions can be
      caught and forgotten by a tolerant custom readObject() method, but delayed
      exceptions cannot be avoided. It seems that OIS.readObject() should have the
      same exception-propagation properties regardless of whether the exception is
      delayed or immediate, which suggests that OIS.readObject() should propagate
      exceptions to the container object *even if* the exception is immediate (i.e.,
      thrown by OIS.readObject(), and thus catchable).

      However, if OIS.readObject() always propagates exceptions to the containing
      object, custom readObject() methods have no means of tolerating exceptions. In
      other words, there is no way for a custom readObject() method to duplicate the
      functionality present in default object serialization, which is able to
      disregard ClassNotFoundExceptions occurring in skipped fields.

      This RFE proposes the addition of a skipObject() method to ObjectInputStream:

          void skipObject() throws IOException;

      This method would allow a custom readObject() method to indicate that any
      ClassNotFoundException (delayed or immediate) occurring in the skipped
      object should not be propagated to the containing object. As previously
      mentioned, calling ObjectInputStream.readObject() would then indicate that any
      ClassNotFoundException occuring in the read object should always be propagated
      to the containing object.

      ###@###.### 2002-05-08

      Upon further reflection, a more flexible and powerful solution would be to
      instead add a new ClassNotFoundException-tolerant readObject() method to
      ObjectInputStream. This method would allow one to attempt to read an
      object (and receive its value), but not be tainted with a ClassNotFoundException
      if the read failed. This functionality would subsume that of the proposed
      skipObject() above--to skip an object, one could simply call the "tolerant"
      readObject method and ignore its return value. One possible signature for
      the method would be:

          Object readObject(Object fallbackValue) throws IOException;

      Essentially, this variant of readObject would attempt to read in the
      object and return it. If the read failed due to an unresolvable class,
      however, readObject would return the fallbackValue provided as an argument.
      A similar fallback value could be passed to a new constructor of
      ObjectStreamField, indicating a ClassNotFoundException-tolerant serial
      field:

        ObjectStreamField(String name,
                          Class type,
                          boolean unshared,
                          Object fallbackValue);

      During deserialization, if the value for the indicated field is unresolvable,
      then the field would be set to the given fallback value (rather than
      resulting in a ClassNotFoundException being associated with the instance
      that owns the field).

            Unassigned Unassigned
            mwarressunw Michael Warres (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: