-
Bug
-
Resolution: Fixed
-
P4
-
1.2.0
-
None
-
beta
-
generic
-
generic
-
Verified
This bug is closely related to 4312433.
In the current implementation of serialization, ObjectInputStream has a private
readObject() method which takes a single boolean parameter "requireLocalClass".
When this parameter is set to true, the readObject() call should succeed only
if the class of the object to be deserialized can be resolved locally. If
requireLocalClass is set to false, then deserialization should succeed even if
a local class for the object is not found. This facility is used when
deserializing the values of object fields which have no equivalent in the local
class, and also when skipping over unread object data originally written by
class-defined writeObject()/writeExternal() methods. In both of these cases,
the object is not needed by whatever referenced it, so failure to resolve the
object's class shouldn't cause deserialization to fail for the referencing
object.
Unfortunately, only the immediate skipped object is exempted from the local
class check. If a ClassNotFoundException is thrown while deserializing objects
referenced indirectly through the skipped object, deserialization will fail
even though these sub-objects are not necessary for successful construction of
the outer object.
The code attached to this bug report gives a concrete example of the fault
behavior. In the attached code, the sending VM ("VM 1") defines class A as
follows:
class A implements Serializable {
private static final long serialVersionUID = 0L;
Object obj;
A() { obj = new Object[] { new B() }; }
}
class B implements Serializable {
}
The receiving VM ("VM 2") does not contain a version of class B, and defines
class A as follows:
class A implements Serializable {
private static final long serialVersionUID = 0L;
}
When VM 2 attempts to deserialize an instance a of class A sent to it by VM 1,
it recognizes that the field A.foo is absent in its local version of A.
Accordingly, it does not require that the class of the object that a.foo
references (Object[]) be present in the local VM. However, it does still
require that the classes of objects referenced indirectly through a.foo be
available. As a result, deserialization of a fails, since the class for B
cannot be found in VM 2, even though the instance of B isn't actually needed to
deserialize a.
A correct solution to this problem must also take into account bug 4312433.
For example, a naive fix for this bug would be to propagate a
"requireLocalClass" value of false down the object graph, so that missing
classes of objects referenced indirectly through a skipped object would not
cause deserialization to abort. However, if (direct) back references to the
unresolved objects are deserialized in later calls to readObject(), these later
calls to readObject() _should_ throw the proper ClassNotFoundExceptions. It
appears that correctly implementing this behavior will involve stashing
ClassNotFoundExceptions associated with deserialized objects somewhere (perhaps
in the wire handle table along with the objects themselves), so they can be
retrieved and thrown depending on the context in which the unresolved object is
referenced. Such a fix will most likely require non-trivial changes to the
internal implementation of ObjectInputStream.
In the current implementation of serialization, ObjectInputStream has a private
readObject() method which takes a single boolean parameter "requireLocalClass".
When this parameter is set to true, the readObject() call should succeed only
if the class of the object to be deserialized can be resolved locally. If
requireLocalClass is set to false, then deserialization should succeed even if
a local class for the object is not found. This facility is used when
deserializing the values of object fields which have no equivalent in the local
class, and also when skipping over unread object data originally written by
class-defined writeObject()/writeExternal() methods. In both of these cases,
the object is not needed by whatever referenced it, so failure to resolve the
object's class shouldn't cause deserialization to fail for the referencing
object.
Unfortunately, only the immediate skipped object is exempted from the local
class check. If a ClassNotFoundException is thrown while deserializing objects
referenced indirectly through the skipped object, deserialization will fail
even though these sub-objects are not necessary for successful construction of
the outer object.
The code attached to this bug report gives a concrete example of the fault
behavior. In the attached code, the sending VM ("VM 1") defines class A as
follows:
class A implements Serializable {
private static final long serialVersionUID = 0L;
Object obj;
A() { obj = new Object[] { new B() }; }
}
class B implements Serializable {
}
The receiving VM ("VM 2") does not contain a version of class B, and defines
class A as follows:
class A implements Serializable {
private static final long serialVersionUID = 0L;
}
When VM 2 attempts to deserialize an instance a of class A sent to it by VM 1,
it recognizes that the field A.foo is absent in its local version of A.
Accordingly, it does not require that the class of the object that a.foo
references (Object[]) be present in the local VM. However, it does still
require that the classes of objects referenced indirectly through a.foo be
available. As a result, deserialization of a fails, since the class for B
cannot be found in VM 2, even though the instance of B isn't actually needed to
deserialize a.
A correct solution to this problem must also take into account bug 4312433.
For example, a naive fix for this bug would be to propagate a
"requireLocalClass" value of false down the object graph, so that missing
classes of objects referenced indirectly through a skipped object would not
cause deserialization to abort. However, if (direct) back references to the
unresolved objects are deserialized in later calls to readObject(), these later
calls to readObject() _should_ throw the proper ClassNotFoundExceptions. It
appears that correctly implementing this behavior will involve stashing
ClassNotFoundExceptions associated with deserialized objects somewhere (perhaps
in the wire handle table along with the objects themselves), so they can be
retrieved and thrown depending on the context in which the unresolved object is
referenced. Such a fix will most likely require non-trivial changes to the
internal implementation of ObjectInputStream.