You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@harmony.apache.org by "Jimmy,Jing Lv" <fi...@gmail.com> on 2010/02/08 14:21:25 UTC

Re: Harmony-6439: Bug in Serialization found while running Berkeley DB Java Edition on Android

Hi Charles,

     Sorry for late reply.
     Thanks a lot for reporting this bug for Harmony and the testcase is
perfect. I've been reading the code, found it is mainly caused by the
Harmony ObjectStreamField does not resolve the primitive fields which are
declared in the serializable correctly and miss-initialize the string type.
I've got a quick fix, it works well on your testcase. I am running the full
testcases on it. Will commit soon if everything appears good.
     Please review and tell if you have more questions.

2010/2/5 Charles Lamb <ch...@gmail.com>

> Hello,
>
> I have submitted Jira HARMONY-6439 for this issue.  Credit to my
> colleagues Mark Hayes and Haomian Wang for narrowing this down.
>
> Harmony object serialization has an initialization bug that causes
> NullPointerException when overriding the
> ObjectInputStream.readClassDescriptor and
> ObjectOutputStream.writeClassDescriptor methods, in order to store the
> ObjectStreamClass elsewhere.  In Berkeley DB Java Edition, overriding
> these methods is necessary to store the ObjectStreamClass in a separate
> database, to avoid repeating it redundantly in every database record.
> This type of overriding is supported by the Java specification, and the
> problem does not occur in other Java class libraries (specifically, the
> bug does not occur on the Sun, IBM and BEA Java platforms).
>
> The attached test demonstrates the problem by simply serializing and
> deserializing an object to/from a byte array, when readClassDescriptor
> and writeClassDescriptor are overridden.  A NullPointerException occurs
> because of an uninitialized field during the call to readObject.
>
> C:\temp>java -classpath . ClassDescriptorOverrideBug
> java.lang.NullPointerException
>         at java.io.ObjectStreamField.resolve(ObjectStreamField.java:336)
>         at
> java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1838)
>         at
> java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:799)
>         at
> java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:2039)
>         at
>
> java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:902)
>         at
> java.io.ObjectInputStream.readObject(ObjectInputStream.java:2251)
>         at
> java.io.ObjectInputStream.readObject(ObjectInputStream.java:2208)
>         at
> ClassDescriptorOverrideBug.doTest(ClassDescriptorOverrideBug.java:49)
>         at
> ClassDescriptorOverrideBug.main(ClassDescriptorOverrideBug.java:29)
>
> Note that this problem only occurs when primitive fields are declared in
> the serializable class.
>
> A workaround for this bug is to serialize and deserialize the
> ObjectStreamClass before it is used.  Apparently deserialization causes
> initialization to occur correctly.
>
>
>
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.InputStream;
> import java.io.IOException;
> import java.io.ObjectInputStream;
> import java.io.ObjectOutputStream;
> import java.io.ObjectStreamClass;
> import java.io.OutputStream;
> import java.io.Serializable;
> import java.util.HashMap;
> import java.util.Map;
>
> /**
>  * Demonstrates a Harmony bug in Java serialization, where overriding the
>  * ObjectInputStream.readClassDescriptor and
>  * ObjectOutputStream.writeClassDescriptor methods, in order to store the
>  * ObjectStreamClass elsewhere, causes a NullPointerException.
>  */
> public class ClassDescriptorOverrideBug {
>
>    private final Map<Integer, ObjectStreamClass> idToDescMap =
>        new HashMap<Integer, ObjectStreamClass>();
>    private final Map<String, Integer> nameToIdMap =
>        new HashMap<String, Integer>();
>    private int nextId = 1;
>
>    public static void main(String argv[]) {
>        try {
>            new ClassDescriptorOverrideBug().doTest();
>            System.out.println("SUCCESS");
>            System.exit(0);
>        } catch (Exception e) {
>            e.printStackTrace();
>            System.exit(1);
>        }
>    }
>
>    /* Write/serialize and read/de-serialize an object. */
>    void doTest()
>        throws IOException, ClassNotFoundException {
>
>        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
>        final MyObjectOutputStream oos = new MyObjectOutputStream(baos);
>        oos.writeObject(new Data());
>        final byte[] bytes = baos.toByteArray();
>        final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
>        final MyObjectInputStream ois = new MyObjectInputStream(bais);
>        /* NullPointerException is thrown by the readObject call below. */
>        ois.readObject();
>    }
>
>    /* Primitive fields are necessary to cause the NullPointerException. */
>    static class Data implements Serializable {
>        String field1 = "field1";
>        String field2 = "field2";
>        int field3 = 333;
>        int field4 = 444;
>        String field5 = "field5";
>    }
>
>    /* Overrides writeClassDescriptor to store ObjectStreamClass in map. */
>    class MyObjectOutputStream extends ObjectOutputStream {
>
>        MyObjectOutputStream(OutputStream out)
>            throws IOException {
>
>            super(out);
>        }
>
>        @Override
>        protected void writeClassDescriptor(ObjectStreamClass desc)
>            throws IOException {
>
>            final String className = desc.getName();
>            final int id;
>            if (nameToIdMap.containsKey(className)) {
>                /* Known class: get ID from map. */
>                id = nameToIdMap.get(className);
>            } else {
>                /* New class: assign ID and store in maps. */
>                id = nextId;
>                nextId += 1;
>                idToDescMap.put(id, desc);
>                nameToIdMap.put(className, id);
>            }
>            /* Write ID of ObjectStreamClass. */
>            writeInt(id);
>        }
>    }
>
>    /* Overrides readClassDescriptor to get ObjectStreamClass from map. */
>    class MyObjectInputStream extends ObjectInputStream {
>
>        MyObjectInputStream(InputStream in)
>            throws IOException {
>
>            super(in);
>        }
>
>        @Override
>        protected ObjectStreamClass readClassDescriptor()
>            throws IOException, ClassNotFoundException {
>
>            /* Read the ID and get the ObjectStreamClass from a map. */
>            final int id = readInt();
>            final ObjectStreamClass desc = idToDescMap.get(id);
>            if (desc == null) {
>                throw new ClassNotFoundException("id not found: " + id);
>            }
>            return desc;
>        }
>    }
> }
>
>
>
>


-- 

Best Regards!

Jimmy, Jing Lv
China Software Development Lab, IBM

Re: Harmony-6439: Bug in Serialization found while running Berkeley DB Java Edition on Android

Posted by nisen <ni...@gmail.com>.
Oh。it's so great。From this mail I notice an effective ask, a good
answer 。In the different companies to different people collaborate
with each other to promote technology development
I was a newer,  I see the power of the community, it's great.

Charles may be come from oracle,
Jimmy come from IBM  in China,
I come from China .

Everyone can  get benefits:
Charles of the issue is resolved
Jimmy received a feedback
I learned more about how to participate in community.


2010/2/8 Jimmy,Jing Lv <fi...@gmail.com>:
> Hi Charles,
>
>     Sorry for late reply.
>     Thanks a lot for reporting this bug for Harmony and the testcase is
> perfect. I've been reading the code, found it is mainly caused by the
> Harmony ObjectStreamField does not resolve the primitive fields which are
> declared in the serializable correctly and miss-initialize the string type.
> I've got a quick fix, it works well on your testcase. I am running the full
> testcases on it. Will commit soon if everything appears good.
>     Please review and tell if you have more questions.
>
> 2010/2/5 Charles Lamb <ch...@gmail.com>
>
>> Hello,
>>
>> I have submitted Jira HARMONY-6439 for this issue.  Credit to my
>> colleagues Mark Hayes and Haomian Wang for narrowing this down.
>>
>> Harmony object serialization has an initialization bug that causes
>> NullPointerException when overriding the
>> ObjectInputStream.readClassDescriptor and
>> ObjectOutputStream.writeClassDescriptor methods, in order to store the
>> ObjectStreamClass elsewhere.  In Berkeley DB Java Edition, overriding
>> these methods is necessary to store the ObjectStreamClass in a separate
>> database, to avoid repeating it redundantly in every database record.
>> This type of overriding is supported by the Java specification, and the
>> problem does not occur in other Java class libraries (specifically, the
>> bug does not occur on the Sun, IBM and BEA Java platforms).
>>
>> The attached test demonstrates the problem by simply serializing and
>> deserializing an object to/from a byte array, when readClassDescriptor
>> and writeClassDescriptor are overridden.  A NullPointerException occurs
>> because of an uninitialized field during the call to readObject.
>>
>> C:\temp>java -classpath . ClassDescriptorOverrideBug
>> java.lang.NullPointerException
>>         at java.io.ObjectStreamField.resolve(ObjectStreamField.java:336)
>>         at
>> java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1838)
>>         at
>> java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:799)
>>         at
>> java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:2039)
>>         at
>>
>> java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:902)
>>         at
>> java.io.ObjectInputStream.readObject(ObjectInputStream.java:2251)
>>         at
>> java.io.ObjectInputStream.readObject(ObjectInputStream.java:2208)
>>         at
>> ClassDescriptorOverrideBug.doTest(ClassDescriptorOverrideBug.java:49)
>>         at
>> ClassDescriptorOverrideBug.main(ClassDescriptorOverrideBug.java:29)
>>
>> Note that this problem only occurs when primitive fields are declared in
>> the serializable class.
>>
>> A workaround for this bug is to serialize and deserialize the
>> ObjectStreamClass before it is used.  Apparently deserialization causes
>> initialization to occur correctly.
>>
>>
>>
>> import java.io.ByteArrayInputStream;
>> import java.io.ByteArrayOutputStream;
>> import java.io.InputStream;
>> import java.io.IOException;
>> import java.io.ObjectInputStream;
>> import java.io.ObjectOutputStream;
>> import java.io.ObjectStreamClass;
>> import java.io.OutputStream;
>> import java.io.Serializable;
>> import java.util.HashMap;
>> import java.util.Map;
>>
>> /**
>>  * Demonstrates a Harmony bug in Java serialization, where overriding the
>>  * ObjectInputStream.readClassDescriptor and
>>  * ObjectOutputStream.writeClassDescriptor methods, in order to store the
>>  * ObjectStreamClass elsewhere, causes a NullPointerException.
>>  */
>> public class ClassDescriptorOverrideBug {
>>
>>    private final Map<Integer, ObjectStreamClass> idToDescMap =
>>        new HashMap<Integer, ObjectStreamClass>();
>>    private final Map<String, Integer> nameToIdMap =
>>        new HashMap<String, Integer>();
>>    private int nextId = 1;
>>
>>    public static void main(String argv[]) {
>>        try {
>>            new ClassDescriptorOverrideBug().doTest();
>>            System.out.println("SUCCESS");
>>            System.exit(0);
>>        } catch (Exception e) {
>>            e.printStackTrace();
>>            System.exit(1);
>>        }
>>    }
>>
>>    /* Write/serialize and read/de-serialize an object. */
>>    void doTest()
>>        throws IOException, ClassNotFoundException {
>>
>>        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
>>        final MyObjectOutputStream oos = new MyObjectOutputStream(baos);
>>        oos.writeObject(new Data());
>>        final byte[] bytes = baos.toByteArray();
>>        final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
>>        final MyObjectInputStream ois = new MyObjectInputStream(bais);
>>        /* NullPointerException is thrown by the readObject call below. */
>>        ois.readObject();
>>    }
>>
>>    /* Primitive fields are necessary to cause the NullPointerException. */
>>    static class Data implements Serializable {
>>        String field1 = "field1";
>>        String field2 = "field2";
>>        int field3 = 333;
>>        int field4 = 444;
>>        String field5 = "field5";
>>    }
>>
>>    /* Overrides writeClassDescriptor to store ObjectStreamClass in map. */
>>    class MyObjectOutputStream extends ObjectOutputStream {
>>
>>        MyObjectOutputStream(OutputStream out)
>>            throws IOException {
>>
>>            super(out);
>>        }
>>
>>        @Override
>>        protected void writeClassDescriptor(ObjectStreamClass desc)
>>            throws IOException {
>>
>>            final String className = desc.getName();
>>            final int id;
>>            if (nameToIdMap.containsKey(className)) {
>>                /* Known class: get ID from map. */
>>                id = nameToIdMap.get(className);
>>            } else {
>>                /* New class: assign ID and store in maps. */
>>                id = nextId;
>>                nextId += 1;
>>                idToDescMap.put(id, desc);
>>                nameToIdMap.put(className, id);
>>            }
>>            /* Write ID of ObjectStreamClass. */
>>            writeInt(id);
>>        }
>>    }
>>
>>    /* Overrides readClassDescriptor to get ObjectStreamClass from map. */
>>    class MyObjectInputStream extends ObjectInputStream {
>>
>>        MyObjectInputStream(InputStream in)
>>            throws IOException {
>>
>>            super(in);
>>        }
>>
>>        @Override
>>        protected ObjectStreamClass readClassDescriptor()
>>            throws IOException, ClassNotFoundException {
>>
>>            /* Read the ID and get the ObjectStreamClass from a map. */
>>            final int id = readInt();
>>            final ObjectStreamClass desc = idToDescMap.get(id);
>>            if (desc == null) {
>>                throw new ClassNotFoundException("id not found: " + id);
>>            }
>>            return desc;
>>        }
>>    }
>> }
>>
>>
>>
>>
>
>
> --
>
> Best Regards!
>
> Jimmy, Jing Lv
> China Software Development Lab, IBM
>