JDK-5023557 : LTP: XMLEncoder.mark(Object, boolean) marks statements in wrong order
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2004-03-30
  • Updated: 2010-02-24
  • Resolved: 2010-02-24
Related Reports
Duplicate :  
Description
Name: jl125535			Date: 03/30/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
OS independent

A DESCRIPTION OF THE PROBLEM :
The method XMLEncoder.mark(Object o, boolean isArgument) first marks the ValueData, and then, if the target T of the Expresson exp that produces o is not an instance of Class (i.e., it is not a static method or a simple "new"), adds exp to the statementList of T.  Then, it marks exp, which causes all Statements on which exp depends to be marked.

Since exp produces o, the transitive closure of all statements referenced by exp must be marked *before* exp is added to the statementList for T, especially if one of those statements has the target T.  Therefore, the call to mark(exp) must occur before adding exp to the statementList of T.

This never worked in 1.4.2 or 1.5.0, and I was unable to test with earlier releases.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
javac PT2.java
java PT2

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.4.2_04" class="java.beans.XMLDecoder">
 <void class="PT2$A">
  <void id="PT2$B0" method="newB"/>
  <void id="PT2$C0" method="newC">
   <object idref="PT2$B0"/>
  </void>
 </void>
 <object idref="PT2$C0"/>
</java>

ACTUAL -
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.4.2_04" class="java.beans.XMLDecoder">
 <void class="PT2$A">
  <void id="PT2$C0" method="newC">
   <void class="PT2$A">
    <void id="PT2$C0" method="newC">
     <object idref="PT2$B0"/>
    </void>
    <void id="PT2$B0" method="newB"/>
   </void>
   <object idref="PT2$B0"/>
  </void>
  <void id="PT2$B0" method="newB"/>
 </void>
 <object idref="PT2$C0"/>
</java>

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.beans.*;
import java.io.*;

public class PT2
{
    public static void main(String[] argv)
        throws Exception
    {
        XMLEncoder encoder = new XMLEncoder(System.out);
        encoder.setPersistenceDelegate(B.class, new BDelegate());
        encoder.setPersistenceDelegate(C.class, new CDelegate());
        encoder.setExceptionListener(new ExceptionListener()
                                     {
                                         public void
                                         exceptionThrown(Exception e)
                                         {  e.printStackTrace(); }
                                     });

        A a = new A();
        B b = a.newB();
        C c = a.newC(b);

        encoder.writeObject(c);
        encoder.close();
    }

    public static class A
    {
        public A()
        {}

        public B newB()
        {   return new B(this); }

        public C newC(B b)
        {   return new C(b); }
    }

    public static class B
    {
        private final A m_a;
        public B(A a)
        {   m_a = a; }

        public A getA()
        {   return m_a; }
    }

    public static class C
    {
        private final B m_b;
        public C(B b)
        {   m_b = b; }

        public B getB()
        {   return m_b; }
    }

    public static class BDelegate
        extends DefaultPersistenceDelegate
    {
        protected Expression instantiate(Object p_old, Encoder p_out)
        {
            B b = (B)p_old;
            return new Expression(b, b.getA(), "newB", new Object[0]);
        }
    }

    public static class CDelegate
        extends DefaultPersistenceDelegate
    {
        protected Expression instantiate(Object p_old, Encoder p_out)
        {
            C c = (C)p_old;
            return new Expression(c, c.getB().getA(), "newC",
                                  new Object[] { c.getB() });
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
By moving the mark(exp) to before exp is added to target's statementList, this bug is fixed.  Reference "//arh OLD" and "//arh NEW" in the source below:

private void mark(Object o, boolean isArgument) {
        // System.out.println("mark: " + instanceName(o));
        if (o == null || o == this) {
            return;
        }
        ValueData d = getValueData(o);
        Expression exp = d.exp;
        // Do not mark liternal strings. Other strings, which might,
        // for example, come from resource bundles should still be marked.
        if (o.getClass() == String.class && exp == null) {
            return;
        }
        
        // Bump the reference counts of all arguments
        if (isArgument) {
            d.refs++;
        }
        if (d.marked) {
            return;
        }
        d.marked = true;
        mark(exp);// arh NEW
        Object target = exp.getTarget();
        if (!(target instanceof Class)) {
            statementList(target).add(exp);
	    // Pending: Why does the reference count need to
	    // be incremented here?
            d.refs++;
        }
        // arh OLD mark(exp);
    }
(Incident Review ID: 245035) 
======================================================================

Comments
EVALUATION The problem could be solved together with the 6921644 CR.
24-02-2010

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: dragon
14-06-2004

EVALUATION Reproducible as described in 1.4 through 1.5. ###@###.### 2004-03-31
31-03-2004