-
Bug
-
Resolution: Fixed
-
P4
-
1.2.2
-
tiger
-
x86
-
windows_nt
Name: nt126004 Date: 03/14/2002
FULL PRODUCT VERSION :
Classic VM (build JDK-1.2.2-001, native threads, symcjit)
also:
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
FULL OPERATING SYSTEM VERSION : Windows NT Version 4.0
Service Pack 6
EXTRA RELEVANT SYSTEM CONFIGURATION :
I don't see any need to reopen the other bugs referenced
in this submission, such as 4089896 or 4075132. These
bugs were caused by Hashtable calling put() in the rehash()
method (according to the submissions), whereas the bug
I am reporting here is caused by Hashtable calling put()
in the readObject() method.
A DESCRIPTION OF THE PROBLEM :
Examining the source code for Hashtable.java shows that
this bug is also present in jdk 1.3.1 and 1.4.
During deserialization, Hashtable.readObject() calls
Hashtable.put(). When Hashtable is subclassed, the
derived class put() method is called. If the derived
class wraps its values before putting them into Hashtable,
and unwraps them when getting them out, then this causes
the values to be wrapped twice during deserialization.
This bug has been reported several times before in slightly
different contexts: bug 4089896, 4075132, 4201900, 4208530.
Bug 4075132 has a particulary good description. Basically,
the problem arises from Hashtable calling its public
methods for private purposes. This is not safe when these
public methods are overridden.
4089896 or 4075132
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the attached test code.
2. The test code throws a RuntimeException when the bug occurs.
EXPECTED VERSUS ACTUAL BEHAVIOR :
Whatever value the subclassed put() method puts into the
Hashtable should be retrieved by the subclassed get()
method. This is not the case.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
C:\abugs>c:\jdk1.2.2\jre\bin\java -classpath c:\jdk1.2.2
\jre\lib\rt.jar;c:\jdk1.2.2\jre\lib\i18n.jar;. MyHashtable
java.lang.RuntimeException: Hashtable.put bug: value is already wrapped
at MyHashtable.put(MyHashtable.java:61)
at java.util.Hashtable.readObject(Hashtable.java:774)
at java.lang.reflect.Method.invoke(Native Method)
at java.io.ObjectInputStream.invokeObjectReader
(ObjectInputStream.java:1685)
at java.io.ObjectInputStream.inputObject(ObjectInputStream.java:1165)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:232)
at MyHashtable.copyObject(MyHashtable.java:88)
at MyHashtable.main(MyHashtable.java:104)
Exception in thread "main" java.lang.NullPointerException
at MyHashtable.main(MyHashtable.java:105)
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/**
* imports
*/
import java.util.Hashtable;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
/**
* Class that extends Hashtable to demonstrate bug when
* subclass wraps the values put into the Hashtable.
*/
public class MyHashtable extends Hashtable
{
/**
* Wraps the values put into MyHashtable objects
*/
class ValueWrapper implements Serializable
{
//holds the wrapper value
private Object mValue;
/**
* Constructs the wrapper
*/
ValueWrapper(Object value)
{
mValue = value;
}
/**
* gets the value from the wrapper
*/
Object getValue()
{
return mValue;
}
};
/*
* Overrides Hashtable.get()
*/
public Object get(Object key)
{
ValueWrapper valueWrapper = (ValueWrapper)super.get(key);
Object value = valueWrapper.getValue();
if(value instanceof ValueWrapper)
throw new RuntimeException("Hashtable.get bug");
return value;
}
/**
* Overrides Hashtable.put()
*/
public Object put(Object key, Object value)
{
if(value instanceof ValueWrapper)
throw new RuntimeException("Hashtable.put bug:
value is already wrapped");
ValueWrapper valueWrapper = new ValueWrapper(value);
super.put(key, valueWrapper);
return value;
}
/**
* Uses serialization to perform a deep copy of an object
* @param Object to copy
* @return Object that is a copy of the original
*/
private static Object copyObject(Object oldObj)
{
Object newObj = null;
try
{
//Create a stream in which to serialize the object.
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
ObjectOutputStream p = new ObjectOutputStream(ostream);
//Serialize the object into the stream
p.writeObject(oldObj);
//p.flush();
//Create an input stream from which to deserialize the object
byte[] byteArray = ostream.toByteArray();
ByteArrayInputStream istream = new ByteArrayInputStream(byteArray);
ObjectInputStream q = new ObjectInputStream(istream);
//Deserialize the object
newObj = q.readObject();
}
catch (Exception ex)
{
ex.printStackTrace();
}
return newObj;
}
/**
* main method for demonstrating Hashtable bug
*/
public static void main(String[] args)
{
MyHashtable myHashtable = new MyHashtable();
myHashtable.put("key", "value");
MyHashtable myHashtableCopy = (MyHashtable)copyObject(myHashtable);
String value = (String)myHashtableCopy.get("key");
}
};
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
1. Modify the put() method to detect if the value is
already wrapped, and, if so, do not wrap it again.
2. Use delegation instead of inheritance.
These workarounds were suggested by others in the bug
reports referenced above.
(Review ID: 143697)
======================================================================
- relates to
-
JDK-4772121 Unable to marshal java.util.IdentityHashMap
-
- Closed
-