JDK-8000950 : Exception throws due to the changes in JDK 7 object tranversal and break backward compatibility
  • Type: Backport
  • Backport of: JDK-7169395
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2012-10-15
  • Updated: 2012-11-19
  • Resolved: 2012-10-16
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
7u10 b12Fixed 8Fixed
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.