The attached TestListBean.java demonstrates the problem:
; javac TestListBean.java
; java TestListBean
The expected result is seen when running on Java 5:
; java -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Server VM (build 1.5.0_11-b03, mixed mode)
; javac TestListBean.java
; java TestListBean
serialized: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
deserialized: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
encoded: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
decoded: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
On Java 6, this fails due to 6505888 'LTP: Java 6 breaks XML encoding/decoding of immutable list member and "id" property':
; java -version
java version "1.6.0-rc"
Java(TM) SE Runtime Environment (build 1.6.0-rc-b100)
Java HotSpot(TM) Server VM (build 1.6.0-rc-b100, mixed mode)
; javac TestListBean.java
; java TestListBean
serialized: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
deserialized: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
encoded: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
java.lang.InstantiationException: java.util.Collections$UnmodifiableRandomAccessList
Continuing ...
java.lang.Exception: XMLEncoder: discarding statement XMLEncoder.writeObject(TestListBean$Stinky);
Continuing ...
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at com.sun.beans.ObjectHandler.dequeueResult(ObjectHandler.java:139)
at java.beans.XMLDecoder.readObject(XMLDecoder.java:201)
at TestListBean.performBeanTest(TestListBean.java:98)
at TestListBean.main(TestListBean.java:286)
Bug 6505888, targeting 6u2, was fixed in build b01. The above test should work again as expected with that fix. I tested it on 6u2 obtained from
/net/jano/export/disk29/jcg/NightlyBuilds/update.int/6u2/archived/2007-04-04.6u2
However, I saw a new problem:
; java -version
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b01)
Java HotSpot(TM) Server VM (build 1.6.0_01-b06, mixed mode)
; javac TestListBean.java
; java TestListBean
serialized: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
deserialized: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
encoded: TestListBean$Stinky[name = cat, count = 9, tickets = [TestListBean$Ticket[number =1], TestListBean$Ticket[number =2]]]
Exception in thread "main" java.lang.StackOverflowError
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:243)
at java.beans.Statement.invoke(Statement.java:214)
at java.beans.Expression.getValue(Expression.java:98)
at java.beans.Encoder.getValue(Encoder.java:85)
at java.beans.Encoder.get(Encoder.java:200)
at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:94)
... repeated many times ...
at java.beans.Encoder.writeObject(Encoder.java:54)
at java.beans.XMLEncoder.writeObject(XMLEncoder.java:257)
at java.beans.Encoder.writeExpression(Encoder.java:279)
at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:372)
at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:97)
... repeated many times ...
What's interesting is that the test succeeds if either of the encoded classes overrides equals(). Uncommenting either of the equals() methods in the attached TestListBean.java causes the test to succeed:
@Override
public boolean
equals(Object o)
{
if (o instanceof Ticket) {
Ticket t = (Ticket)o;
return (number == t.number);
}
return false;
}
or
@Override
public boolean
equals(Object o)
{
if (o instanceof Stinky) {
Stinky s = (Stinky)o;
return ((name == null ? s.name == null :
name.equals(s.name)) &&
(count == s.count) &&
tickets.equals(s.tickets));
}
return false;
}
Of course it does not make sense for all classes to override equals(), therefore such a hidden requirement is broken (and inconsistent with Java 5).