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

Deserialization fails for cyclic object graphs, superclassing, and HashSet

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version "9.0.4"
      Java(TM) SE Runtime Environment (build 9.0.4+11)
      Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Linux prod-wgba-005.oslo-no0030.xxx.xxx 3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

      Microsoft Windows [Version 6.1.76.01]

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      Seems to be independent of operating system.


      A DESCRIPTION OF THE PROBLEM :
      Seems to be similar to the 1.4.2 bug described here
      https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6208166#

      1) Create an object graph (for example object tree) with cyclic dependencies:
      - Node class with an Long field and a hashCode() method depending on this field.
      - A parent reference.
      - A set of children (represented by a HashSet) and a parent reference.
      2)
      - Create some Node instances and chain them together so that they represent a tree (with cyclic dependencies).
      3)
      - Serialize and de-serialize the object graph from 2)

      This works with JDK8 but fails with JDK9.



      REGRESSION. Last worked in version 8u172

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the attached code with JDK9.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No output.

      ACTUAL -
      Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException
      at wg.crewmanager.scrapbook.ReproduceHashSetSerializationBug.deSerialize(ReproduceHashSetSerializationBug.java:127)
      at wg.crewmanager.scrapbook.ReproduceHashSetSerializationBug.test(ReproduceHashSetSerializationBug.java:89)
      at wg.crewmanager.scrapbook.ReproduceHashSetSerializationBug.main(ReproduceHashSetSerializationBug.java:73)
      Caused by: java.lang.NullPointerException
      at wg.crewmanager.scrapbook.ReproduceHashSetSerializationBug$Superclass.hashCode(ReproduceHashSetSerializationBug.java:38)
      at java.base/java.util.HashMap.hash(HashMap.java:339)
      at java.base/java.util.HashMap.put(HashMap.java:612)
      at java.base/java.util.HashSet.readObject(HashSet.java:342)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.base/java.lang.reflect.Method.invoke(Method.java:564)
      at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1160)
      at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2207)
      at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2078)
      at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1585)
      at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2346)
      at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2240)
      at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2078)
      at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1585)
      at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2346)
      at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2240)
      at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2078)
      at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1585)
      at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
      at java.base/java.util.ArrayList.readObject(ArrayList.java:825)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.base/java.lang.reflect.Method.invoke(Method.java:564)
      at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1160)
      at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2207)
      at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2078)
      at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1585)
      at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
      at wg.crewmanager.scrapbook.ReproduceHashSetSerializationBug.deSerialize(ReproduceHashSetSerializationBug.java:123)
      ... 2 more


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package wg.crewmanager.scrapbook;

      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.io.Serializable;
      import java.util.ArrayList;
      import java.util.HashSet;
      import java.util.List;
      import java.util.Random;
      import java.util.Set;

      /**
       * Test serialization of {@link HashSet}.
       *
       */
      public class ReproduceHashSetSerializationBug
      {

         private static final Random RANDOM = new Random();

         public static class Superclass implements Serializable
         {

            private final Long id;

            public Superclass()
            {
               this.id = RANDOM.nextLong();
            }

            public int hashCode()
            {
               return this.id.hashCode();
            }

            /*
            private void readObject(java.io.ObjectInputStream in)
               throws IOException, ClassNotFoundException
            {
               in.defaultReadObject();
            }
            */

         }


         public static class Node extends Superclass implements Serializable
         {

            private final String name;
            private final Node parent;
            private final Set<Node> children = new HashSet<>();

            public Node(String name, Node parent)
            {
               this.name = name;
               this.parent = parent;
               if (parent != null)
               {
                  parent.children.add(this);
               }
            }

         }

         public static void main(String[] args)
         {
            new ReproduceHashSetSerializationBug().test();
         }

         @SuppressWarnings("unchecked")
         private void test()
         {

            Node a = new Node("a", null);
            Node b = new Node("b", a);

            List<Node> elements = new ArrayList<>();
            elements.add(b);
            elements.add(a);

            byte[] bytes = serialize((Serializable) elements);

            List<Node> deserialized = (List<Node>) deSerialize(bytes);
         }

         /**
          * Serializes the given object into a byte array.
          *
          * @param serializable not null
          * @return a byte array, not null
          */
         private static byte[] serialize(Serializable serializable)
         {
            try
            {
               ByteArrayOutputStream out = new ByteArrayOutputStream();
               ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
               objectOutputStream.writeObject(serializable);
               return out.toByteArray();
            }
            catch (Exception e)
            {
               throw new RuntimeException(e);
            }
         }

         /**
          * De-serializes the given byte array into an object.
          *
          * @param bytes the byte array to de-serializes, not null
          * @return the de-serialized object
          */
         private static Serializable deSerialize(byte[] bytes)
         {
            try
            {
               return (Serializable) new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
            }
            catch (Exception e)
            {
               throw new RuntimeException(e);
            }
         }

      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      Explicit readObject in super class (remove comments and run again).

      Not sure why this makes it work.




            smarks Stuart Marks
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: