Many of us have came across lot of crs that has StreamCorruptedExceptions from the following code in java.io.ObjectInputStream:
private ObjectStreamClass readNonProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_CLASSDESC) {
throw new StreamCorruptedException();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);
passHandle = NULL_HANDLE;
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
// REMIND: do something less drastic here?
throw new StreamCorruptedException();
}
Class cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
try {
if ((cl = resolveClass(readDesc)) == null) {
throw new ClassNotFoundException("null class");
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
skipCustomData();
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
I highligted the place where it is throwing StreamCorruptedException. It is throwing StreamCorruptedException when it gets ClassNotFoundException while reading ClassDescriptor. Many of us actually gone through crs that has StreamCorruptedExceptions from this code and we were little confused intially that why we would experience this exception. Later on after looking at the code we realized that it is result of ClassCastException.
There is also a reminder that says 'do something less drastic here'. This wasn't like this in 1.3.1_x. In 1.3.1_x, it is using inputClassDescriptor() method and that method throws ClassNotFoundException. From 1.4.1 onwards they changed the code and calling this readNonProxyDesc() method and it doesn't have ClassNotFoundException in the throws class and hence they are throwing StreamCorruptedExceptions.
crs -> customer complaints received by BEA

EVALUATION
The change in behavior between 1.3 and 1.4 was a consequence of the fix for
bugs 4313167 ("ClassNotFoundException in skipped objects causes serialization
to fail") and 4312433 ("reading back reference to obj with unresolved class
should throw exception"). The fix for these bugs involved reworking the way
that ObjectInputStream handles ClassNotFoundExceptions to be more
robust--starting in JDK 1.4, ObjectInputStream is able to tolerate class
resolution failures and continue to parse the stream, allowing it to
deserialize objects appearing later in the stream (which is necessary in cases
where the class resolution failure is non-fatal--for instance, if the missing
class corresponds to a "skipped" object that is not referenced by the
deserialized object graph).
ObjectInputStream is able to tolerate class resolution failures because the
serialization stream is self-describing--even if a class isn't present, its
class descriptor in the stream describes the length and layout of the data for
instances of that class. If, however, an error occurs while reading in the
class descriptor itself, then the stream becomes unparsable, because the data
layout information is lost. For this reason, a ClassNotFoundException
encountered while reading in a class descriptor is more serious than an
"ordinary" ClassNotFoundException, since it leaves the stream in an unknown,
unparsable state. Because of this, such a ClassNotFoundException cannot simply
be propagated to the caller--other ObjectInputStream code further up the call
stack would unsuccessfully attempt to recover from the ClassNotFoundException,
in most cases ultimately resulting in a StreamCorruptedException. This
highlights the need to distinguish ClassNotFoundExceptions thrown while reading
in class descriptors, which should be considered fatal stream errors, from
"ordinary" ClassNotFoundExceptions, which the stream can tolerate.
That said, the bug report is justified in pointing out that throwing
StreamCorruptedException in this case leads to confusion; furthermore, the
cause of the thrown exception is not set to the original
ClassNotFoundException, thus further obscuring the original problem. A better
solution would be to instead throw an InvalidClassException whose cause is
set to be the original ClassNotFoundException. This has the following
benefits:
- InvalidClassException more clearly indicates that the problem is related to
class data deserialization
- InvalidClassException is a subclass of ObjectStreamException, as is
StreamCorruptedException, so any code catching IOExceptions or
ObjectStreamExceptions thrown by ObjectInputStream in this situation would
not be affected by the change
- InvalidClassException is (obviously) not a ClassNotFoundException, and hence
avoids the problems with propagating the ClassNotFoundException unchanged, as
alluded to above
###@###.### 2003-10-02