Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6989223

DefaultPersistenceDelegate not called when property initialised to non null

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P4 P4
    • None
    • 6u21
    • client-libs
    • x86
    • windows_vista

      FULL PRODUCT VERSION :
      java version "1.6.0_20"
      Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
      Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)

      And other versions too


      ADDITIONAL OS VERSION INFORMATION :
      All operating systems

      A DESCRIPTION OF THE PROBLEM :
      I think there is a bug in the DefaultPersistenceDelegate class in the method:

         private static boolean definesEquals(Class type) {
         try {
             return type == type.getMethod("equals", Object.class).getDeclaringClass();
         }
         catch(NoSuchMethodException e) {
             return false;
         }
      }

      When used on a class like Rectangle2D.Double it returns false (because the equals() method is defined in the enclosing class).

      This means that DefaultPersistenceDelegates may NOT get called if a bean property has been initialised to anything other than null and where the property equals method is not declared directly in the class.

      Without documentation I cannot determine the intent of the existing code for definesEquals() , but if it is to return true if a class has
      a suitable equals() method then this would seem to work:

      private static boolean definesEquals(Class type) {
      try {
      // return true if type defines a method with signature: boolean equals(Object o)
      return boolean.class == type.getMethod("equals", Object.class).getReturnType();
      } catch (NoSuchMethodException e) {
      return false;
      }
      }


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the test application twice setting THIS_WORKS to true ad then false.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      THIS_WORKS=true
      bean as XML is :
      <?xml version="1.0" encoding="UTF-8"?>
      <java version="1.6.0_20" class="java.beans.XMLDecoder">
       <object class="TestBean">
        <void property="bounds">
         <object class="java.awt.geom.Rectangle2D$Double">
          <double>1.0</double>
          <double>2.0</double>
          <double>3.0</double>
          <double>5.0</double>
         </object>
        </void>
       </object>
      </java>

      bean.getBounds() as XML is :
      <?xml version="1.0" encoding="UTF-8"?>
      <java version="1.6.0_20" class="java.beans.XMLDecoder">
       <object class="java.awt.geom.Rectangle2D$Double">
        <double>1.0</double>
        <double>2.0</double>
        <double>3.0</double>
        <double>5.0</double>
       </object>
      </java>

      ACTUAL -
      THIS_WORKS=false
      bean as XML is :
      <?xml version="1.0" encoding="UTF-8"?>
      <java version="1.6.0_20" class="java.beans.XMLDecoder">
       <object class="TestBean"/>
      </java>

      bean.getBounds() as XML is :
      <?xml version="1.0" encoding="UTF-8"?>
      <java version="1.6.0_20" class="java.beans.XMLDecoder">
       <object class="java.awt.geom.Rectangle2D$Double">
        <double>1.0</double>
        <double>2.0</double>
        <double>3.0</double>
        <double>5.0</double>
       </object>
      </java>


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.awt.geom.Rectangle2D;
      import java.beans.DefaultPersistenceDelegate;
      import java.beans.XMLEncoder;
      import java.io.ByteArrayOutputStream;

      /**
       *
       * Bug with XMLEncoder and DefaultPersistenceDelegate
       *
       * DefaultPersistenceDelegate is not called when a bean property has been
       * initialised to non null?
       *
       * This TestBean has a bounds property of type Rectangle2D.Double. The
       * XMLEncoder requires a persistenceDelegate in order to encode
       * Rectangle2D.Double.
       * <p>
       * However, if TestBean is initialised with its bounds property set to anything
       * other than null, the persistenceDelegate is not invoked for the bounds
       * property when writeObject is used on the TestBean. The persistenceDelegate
       * still gets called if you call writeObject on the bounds property rather than
       * the TestBean.
       * <p>
       *
       * Try running this program changing THIS_WORKS between true and false.
       *
       *
       * @author Michael Ellis.
       */
      public class TestBean {

      private static boolean THIS_WORKS = false;
      Rectangle2D.Double bounds;

      /**
      *
      */
      public TestBean() {
      if (THIS_WORKS) {
      bounds = null; // THIS WORKS
      } else {
      bounds = new Rectangle2D.Double(1, 2, 3, 4); // THIS DOESN'T WORK
      }
      }

      public Rectangle2D.Double getBounds() {
      return bounds;
      }

      public void setBounds(Rectangle2D.Double bounds) {
      this.bounds = bounds;
      }

      public static void main(String[] args) {
      TestBean bean = new TestBean();
      bean.setBounds(new Rectangle2D.Double(1, 2, 3, 5));

      System.out.printf("THIS_WORKS=%b\n", THIS_WORKS);
      System.out.printf("bean as XML is :\n%s\n", objectToXML(bean));
      System.out.printf("bean.getBounds() as XML is :\n%s\n", objectToXML(bean.getBounds()));
      }

      static String objectToXML(Object o) {
      final ByteArrayOutputStream bos = new ByteArrayOutputStream();
      final XMLEncoder encoder = new XMLEncoder(bos);

      encoder.setPersistenceDelegate(Rectangle2D.Double.class, new DefaultPersistenceDelegate(
      new String[] { "x", "y", "width", "height" }));

      encoder.writeObject(o);
      encoder.close();
      return bos.toString();
      }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Override the mutatesTo() method when using DefaultPersistenceDelegate, for example:

      encoder.setPersistenceDelegate(Rectangle2D.Double.class, new DefaultPersistenceDelegate(
      new String[] { "x", "y", "width", "height" }) {
      @Override
      protected boolean mutatesTo(Object oldInstance, Object newInstance) {
      return (super.mutatesTo(oldInstance, newInstance) && oldInstance.equals(newInstance));
      }
      });

            malenkov Sergey Malenkov (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: