JDK-8013416 : Java Bean Persistence with XMLEncoder
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 7u4
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • Submitted: 2013-04-12
  • Updated: 2014-11-17
  • Resolved: 2013-05-24
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
7u40Fixed 8 b93Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_17 " 
Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

EXTRA RELEVANT SYSTEM CONFIGURATION :
The following jar files are required:  guava-14.0.1.jar and junit-4.10.jar. They are available at https://code.google.com/p/guava-libraries/ and https://github.com/junit-team/junit/wiki

A DESCRIPTION OF THE PROBLEM :
Encoding and decoding of a valid Java Bean, containing a com.google.common.collect.HashMultimap, fails. The error results from an issue in com.sun.beans.finder.MethodFinder.findMethod. The HashMultimap class inherits it's  " put "  method from the package private implementation in: StandardSetMultimap (and above). But that method isn't callable as the class that declares it isn't public. What the FindMethod method should do is find the public method from the Multimap interface and return that - since that is the only method that can be called without special privileges.

REGRESSION.  Last worked in version 7

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) compile the unit test
javac -cp guava-14.0.1.jar;junit-4.10.jar;. XmlEncoderTest.java

2) run the unit test
java -cp guava-14.0.1.jar;junit-4.10.jar;. org.junit.runner.JUnitCore XmlEncoderTest

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The unit test should produce a valid XML file containing the following:

<?xml version= " 1.0 "  encoding= " UTF-8 " ?>
<java version= " 1.7.0_17 "  class= " java.beans.XMLDecoder " >
 <object class= " XmlEncoderTest$SomeBean " >
  <void property= " stateMap " >
   <object class= " com.google.common.collect.HashMultimap "  method= " create " >
    <void method= " put " >
     <string>pz3</string>
     <string>pz4</string>
    </void>
    <void method= " put " >
     <string>pz1</string>
     <string>pz2</string>
    </void>
   </object>
  </void>
 </object>
</java>

The unit test should then decode the XML into a SomeBean object contains a HashMultimap with 2 entries (pz1,pz2) and (pz3,pz4).
ACTUAL -
The unit test produces a valid XML file; however, the following statements are written to output. Additionally, the decode SomeBean object's HashMultimap contains no entries.

.java.lang.Exception: Encoder: discarding statement HashMultimap.put( " pz3 " ,  " pz4
 " );
Continuing ...
java.lang.Exception: Encoder: discarding statement HashMultimap.put( " pz1 " ,  " pz2 " 
);
Continuing ...
java.lang.NoSuchMethodException: <unbound>=HashMultimap.put( " pz3 " ,  " pz4 " );
Continuing ...
java.lang.NoSuchMethodException: <unbound>=HashMultimap.put( " pz1 " ,  " pz2 " );
Continuing ...

ERROR MESSAGES/STACK TRACES THAT OCCUR :
JUnit version 4.10
.java.lang.Exception: Encoder: discarding statement HashMultimap.put( " pz3 " ,  " pz4
 " );
Continuing ...
java.lang.Exception: Encoder: discarding statement HashMultimap.put( " pz1 " ,  " pz2 " 
);
Continuing ...
java.lang.NoSuchMethodException: <unbound>=HashMultimap.put( " pz3 " ,  " pz4 " );
Continuing ...
java.lang.NoSuchMethodException: <unbound>=HashMultimap.put( " pz1 " ,  " pz2 " );
Continuing ...
E
Time: 0.308
There was 1 failure:
1) testEncoding(XmlEncoderTest)
java.lang.AssertionError
        at org.junit.Assert.fail(Assert.java:92)
        at org.junit.Assert.assertTrue(Assert.java:43)
        at org.junit.Assert.assertTrue(Assert.java:54)
        at XmlEncoderTest.testEncoding(XmlEncoderTest.java:46)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(Framework
Method.java:45)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCal
lable.java:15)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMe
thod.java:42)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMet
hod.java:20)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRun
ner.java:68)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRun
ner.java:47)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
        at org.junit.runners.Suite.runChild(Suite.java:128)
        at org.junit.runners.Suite.runChild(Suite.java:24)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:117)
        at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98)
        at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53)
        at org.junit.runner.JUnitCore.main(JUnitCore.java:45)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import static org.junit.Assert.*;
 
import java.beans.DefaultPersistenceDelegate;
import java.beans.Encoder;
import java.beans.Expression;
import java.beans.Statement;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Map;
 
import org.junit.Test;
 
import com.google.common.collect.HashMultimap;
 
public class XmlEncoderTest {
 
    @Test
    public void testEncoding() throws Exception {
 
        // create the bean
        SomeBean sb = new SomeBean();
 
        // add some data
        HashMultimap<String, String> stateMap = HashMultimap.create();
        stateMap.put( " pz1 " ,  " pz2 " );
        stateMap.put( " pz3 " ,  " pz4 " );
 
        sb.setStateMap(stateMap);
 
        String fileName =  " myXMLFile.xml " ;
        // encode as xml
        FileOutputStream os = new FileOutputStream(fileName);
        XMLEncoder encoder = new XMLEncoder(os);
        encoder.setPersistenceDelegate(HashMultimap.class, new CustomPersistenceDelegate());
        encoder.writeObject(sb);
        encoder.close();
 
        // decode the xml
        XMLDecoder decoder = new XMLDecoder(new FileInputStream(fileName));
        Object deSerializedObject = decoder.readObject();
        assertNotNull(deSerializedObject);
 
        HashMultimap<String, String> map = ((SomeBean) deSerializedObject).getStateMap();
        assertTrue(map.containsKey( " pz1 " ));
        assertTrue(map.containsKey( " pz3 " ));
 
    }
 
    static class CustomPersistenceDelegate extends DefaultPersistenceDelegate {
        @Override
        protected Expression instantiate(Object oldInstance, Encoder out) {
            return new Expression(oldInstance, oldInstance.getClass(),  " create " , null);
        }
 
        @Override
        protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
            super.initialize(type, oldInstance, newInstance, out);
 
            com.google.common.collect.HashMultimap<String, String> m = (com.google.common.collect.HashMultimap) oldInstance;
 
            for (Map.Entry<String, String> entry : m.entries()) {
                out.writeStatement(new Statement(oldInstance,  " put " , new Object[] { entry.getKey(), entry.getValue() }));
            }
 
        }
    }
 
    static public class SomeBean {
        private HashMultimap<String, String> stateMap;
 
        public HashMultimap<String, String> getStateMap() {
            return stateMap;
        }
 
        public void setStateMap(HashMultimap<String, String> stateMap) {
            this.stateMap = stateMap;
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use JRE 7 u3 or lower. The bug is present in JRE 7 u4 and up.
Comments
http://cr.openjdk.java.net/~malenkov/8013416.8.0/
24-05-2013

Seems it is a regression after the 7092744 fix. We should find another way to solve that issue.
24-05-2013

Reproducible on Win7 64 with Java 1.7.0_17.
29-04-2013

test1
12-04-2013