JDK-7169395 : Exception throws due to the changes in JDK 7 object tranversal and break backward compatibility
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 7
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2012-05-16
  • Updated: 2013-09-04
  • Resolved: 2012-09-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u10Fixed 8 b56Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
J2SE Version (please include all output from java -version flag):
7u4


Does this problem occur on J2SE 6ux or 7ux?  Yes / No (pick one)
No, works fine with 6u32


Bug Description:

This issue appears to be due in part to a weird change in ordering in Java 7's object tranversal.

In Java 6 (note can provide the Java 6 Update 32 version, but it really doesn't change apart from the version number):

    <java version="1.6.0_25" class="java.beans.XMLDecoder"> 
     <void id="MBeanLoader0" property="owner"/> 
     <object id="Emailer0" class="wt.jmx.core.mbeans.Emailer"> 
      <void id="ArrayList0" property="emailLists"> 
       <void method="add"> 
        <object class="wt.jmx.core.mbeans.EmailList"> 
         <object idref="Emailer0"/> 
         <string>JMX-Administrators</string> 
         <void property="addressList">
          <string>###@###.###</string>
         </void>
        </object> 
       </void> 
      </void> 
      <void property="emailLists"> 
       <object idref="ArrayList0"/> 
      </void> 
      <void property="ownerMBean"> 
       <object idref="MBeanLoader0"/> 
      </void> 
     </object> 
     ...

In Java 7:

    <java version="1.7.0_04" class="java.beans.XMLDecoder">
     <void id="MBeanLoader0" property="owner"/>
     <object class="wt.jmx.core.mbeans.Emailer" id="Emailer0">
      <void id="ArrayList0" property="emailLists">
       <void method="add">
        <object class="wt.jmx.core.mbeans.EmailList">
         <object idref="Emailer0">
          <void property="emailLists">
           <object idref="ArrayList0"/>
          </void>
          <void property="ownerMBean">
           <object idref="MBeanLoader0"/>
          </void>
         </object>
         <string>JMX-Administrators</string>
         <void property="addressList">
          <string>###@###.###</string>
         </void>
        </object>
       </void>
      </void>
     </object>
     ...

Java 7's output very slightly more verbose but it also seems to be "eating 
its own tail" more than the Java 6 output, i.e. it's trying to specify 
the emailLists property to be ArrayList0 in the midst of creating ArrayList0.  
The <object idref="Emailer0"> and subsequent <string>JMX-Administrators</string> 
elements are due to setting a persistence delegate of 
"new DefaultPersistenceDelegate( new String[] { "emailer", "name" } )" 
for the EmailList class.

This change was simply ugly, but the result is that XMLDecoder fails to add 
the EmailList instance to the Emailer instance, so something seems quite broken here.

Steps to Reproduce (be specific):

After changing ~70 classes, can once again persist the objects via XMLEncoder.  
The output is, however, substantially different than that produced with Java 6.  
That's fine *but* also get numerous exceptions when reading this XML in with XMLDecoder.  
These are output by the exception listener and are of the form:

java.lang.NullPointerException: target should not be null
    at java.beans.Statement.invokeInternal(Statement.java:201)
    at java.beans.Statement.access$000(Statement.java:58)
    at java.beans.Statement$2.run(Statement.java:185)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.beans.Statement.invoke(Statement.java:182)
    at java.beans.Expression.getValue(Expression.java:153)
    at com.sun.beans.decoder.ObjectElementHandler.getValueObject(ObjectElementHandler.java:166)
    at com.sun.beans.decoder.NewElementHandler.getValueObject(NewElementHandler.java:123)
    at com.sun.beans.decoder.ElementHandler.endElement(ElementHandler.java:169)
    at com.sun.beans.decoder.DocumentHandler.endElement(DocumentHandler.java:305)
    at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanEndElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserImpl.parse(Unknown Source)
    at com.sun.beans.decoder.DocumentHandler.parse(DocumentHandler.java:356)
    at java.beans.XMLDecoder.parsingComplete(XMLDecoder.java:192)
    at java.beans.XMLDecoder.readObject(XMLDecoder.java:238)
        ...

One piece that may not be 100% obvious from the XML:

    public synchronized Collection<EmailList>  getEmailLists()
    {
      return ( new ArrayList<>( emailLists.values() ) );
    }

    public synchronized void  setEmailLists( final Collection<EmailList> newEmailLists )
    {
      for ( EmailList emailList : emailLists.values() )
        emailList.deregister();
      emailLists.clear();
      for ( EmailList inEmailList : newEmailLists )
        addEmailList( inEmailList );
    }

the nuances here may explain why the new XML fails --

in that setEmailLists() doesn't literally assign the incoming Collection 
reference to a field but rather clears its internal collection and then 
copies each element from the incoming Collection.  

It would seem that XMLEncoder should work fine with an implementation like this -- 
and it did all the way through Java 5 and 6.  Reading the Java 7 generation XML, 
however, it would seem that it attempts to assign this Collection to the 
Emailer object before its first element has been successfully created, 
which is clearly not right.
Seems it is a regression after the 6921644 fix.
We should set ArrayList later, when it will be initialized.

Comments
EVALUATION I think we should provide persistence delegates for standard collections which always call instantiate() before initialize().
09-06-2012

EVALUATION Look at the XMLEncoder description from the author: http://www.oracle.com/technetwork/java/persistence4-140124.html "the encoder is able to omit operations that would set property values to their default value, producing concise documents with very little redundant information." The attached example contains the Emailer class with the emailLists property: public synchronized Collection<EmailList> getEmailLists() { return (new ArrayList<EmailList>(emailLists.values())); } public synchronized void setEmailLists(final Collection<EmailList> newEmailLists) { ... initialize internal collection } Note, that the getter returns ArrayList, which is modifiable object. So, the XMLDecoder decides to modify it instead of create new object. Consider the XML fragment from the Java6: <object id="Emailer0" class="Emailer"> <void id="ArrayList0" property="emailLists"> <void method="add"> <object class="EmailList"> <object idref="Emailer0"/> <string>JMX-Administrators</string> <void property="addressList"> <string>###@###.###</string> </void> </object> </void> </void> <void property="emailLists"> <object idref="ArrayList0"/> </void> </object> First tag says: get the value of the emailLists property and add new EmailList object to it. This is expected behavior and Java7 produces similar XML. Note, that the XMLEncoder *modifies* object as specified! Second tag says: set new value for the emailLists property from the specified variable. This is unexpected behavior because the object is already modified. Seems, it was removed by the 5023559 fix.
09-06-2012

EVALUATION the XML generated by Java 7 cannot successfully be read by Java 7 in cases like this. Exceptions occur when trying to read some of the objects and thus I lose parts of the object graph by saving and reading -- all with Java 7. a test case with simple objects representing those in the XML as provided and including the methods given below should be able to reproduce the issue.
07-06-2012

EVALUATION Do you mean that the XML generated by Java7 could not be read in Java6? It is not a backward compatibility. For example, you can't run some application in Java6 if it was compiled by Java7. Could you please provide minimized test case and steps to reproduce the problem?
07-06-2012