The WrapperBufferInput Class Does Not Deserialize Any Evolvable Future Data If The DataInput Class Does Not Implement The com.tangosol.io.ReadBuffer Interface (Doc ID 1342044.1)

Last updated on NOVEMBER 03, 2016

Applies to:

Oracle Coherence - Version: 3.6.1 and later   [Release: AS10g and later ]
Information in this document applies to any platform.

Symptoms

The problem is seen when deserializing an object that includes an evolvable object inside it, where the evolvable object is not the last item to be read in from the POF (Portable Object Format) stream, and the code performing the deserialization has an older implementation of the evolvable class.

During deserialization, fields that are read after the evolvable object has been read are set to null, and do not have the values that they were set to when the object was serialized.

For example, you have a simple class, SimpleObjectWrapper, that contains the evolvable object:

import java.io.IOException;
import com.tangosol.io.AbstractEvolvable;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofSerializer;
import com.tangosol.io.pof.PofWriter;

public class SimpleObjectWrapper extends AbstractEvolvable {

    private WrappedObject wrappedObject;
    private String otherField;
    private final static int VERSION = 1;

    public static class Serializer implements PofSerializer {

        private final static int OBJECT_FIELD = 0;
        private final static int OTHER_FIELD  = 1;
   
        public Object deserialize(PofReader in) throws IOException {
            SimpleObjectWrapper wrapper = new SimpleObjectWrapper();
            wrapper.setDataVersion(in.getVersionId());
            wrapper.wrappedObject = (WrappedObject) in.readObject(OBJECT_FIELD);
            wrapper.otherField = in.readString(OTHER_FIELD);
            wrapper.setFutureData(in.readRemainder());
            return wrapper;
        }
       
        public void serialize(PofWriter out, Object o) throws IOException {
            SimpleObjectWrapper wrapper = (SimpleObjectWrapper) o;
            int dataVersion = Math.max(wrapper.getDataVersion(),
                                       wrapper.getImplVersion());
            out.setVersionId(dataVersion);
            out.writeObject(OBJECT_FIELD, wrapper.wrappedObject);
            out.writeObject(OTHER_FIELD, wrapper.otherField);
            out.writeRemainder(wrapper.getFutureData());
        }
    } 
   
    @Override
    public int getImplVersion() { return VERSION; }
       
    @Override
    public String toString() {
        return "\nVersion: " + getImplVersion() +
               "\nWrapped Object: " + wrappedObject +
               "\nOther Field: " + otherField + "\n";
    }  
       
    public String getOtherField() { return otherField; }
       
    public void setOtherField(String s) {otherField = s; }
       
    public WrappedObject getWrappedObject() { return wrappedObject; }

    public void setWrappedObject(WrappedObject obj) { wrappedObject = obj; }

The version two implementation of the WrappedObject is:

import java.io.IOException;
import com.tangosol.io.AbstractEvolvable;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofSerializer;
import com.tangosol.io.pof.PofWriter;
   
public class WrappedObject extends AbstractEvolvable {
       
    private String firstField;
    private String secondField;
    private final static int VERSION = 2;
           
    public static class Serializer implements PofSerializer {
           
        private final static int FIRST_FIELD = 0;
        private final static int SECOND_FIELD = 1;
           
        public Object deserialize(PofReader in) throws IOException {
            WrappedObject object = new WrappedObject();
            object.setDataVersion(in.getVersionId());
            object.firstField = in.readString(FIRST_FIELD);
           
            if (in.getVersionId() >= VERSION) {
                object.secondField = in.readString(SECOND_FIELD);
            }
            object.setFutureData(in.readRemainder());
       
            return object;
        }
   
        public void serialize(PofWriter out, Object o) throws IOException {
            WrappedObject object = (WrappedObject) o;
            int dataVersion = Math.max(object.getDataVersion(),
                                       object.getImplVersion());
            out.setVersionId(dataVersion);
            out.writeString(FIRST_FIELD, object.firstField);
            out.writeString(SECOND_FIELD, object.secondField);
            out.writeRemainder(object.getFutureData());
        }
    }
       
    public String getFirstField() { return firstField; }
       
    public void setFirstField(String s) { firstField = s; }

    public String getSecondField() { return secondField; }

    public void setSecondField(String s) { secondField = s; }

    @Override public int getImplVersion() { return VERSION; }

    @Override
    public String toString() {
        return "\n    Version: " + getImplVersion() +
               "\n    First field: " + firstField +
               "\n    Second field: " + secondField;
    }
}

If you were to run the following code just prior to serializing the SimpleWrapperObject:

WrappedObject wrappedObject = new WrappedObject();
wrappedObject.setFirstField("first-field");
wrappedObject.setSecondField("second-field");

SimpleObjectWrapper wrapper = new SimpleObjectWrapper();
wrapper.setWrappedObject(wrappedObject);
wrapper.setOtherField("Hello World");
System.out.println(wrapper.toString());

you would see:

Version: 1
Wrapped Object:
    Version: 2
    First field: first-field
    Second field: second-field
Other Field: Hello World

As you can see both the information from both objects is fine.  If the serialized object was read back in by the same code, that is without a change in the evolvable implementations the same data would be restored.

However, if you had code that was using an earlier implementation of the WrappedObject, in this case version one, which only contained the firstField as shown below:

import java.io.IOException;

import com.tangosol.io.AbstractEvolvable;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofSerializer;
import com.tangosol.io.pof.PofWriter;

public class WrappedObject extends AbstractEvolvable {
   
   private final static int VERSION = 1;
   private String firstField;

    public static class Serializer implements PofSerializer {
       
        private final static int FIRST_FIELD = 0;
       
        public Object deserialize(PofReader in) throws IOException {
            WrappedObject object = new WrappedObject();
            object.setDataVersion(in.getVersionId());
            object.firstField = in.readString(FIRST_FIELD);
            object.setFutureData(in.readRemainder());    
            return object;
        }  
       
        public void serialize(PofWriter out, Object o) throws IOException {
            WrappedObject object = (WrappedObject) o;
            int dataVersion = Math.max(object.getDataVersion(),
                                       object.getImplVersion());
            out.setVersionId(dataVersion);
            out.writeString(FIRST_FIELD, object.firstField);
            out.writeRemainder(object.getFutureData());
        }  
    }      
       
    public String getFirstField() { return firstField; }

    public void setFirstField(String s) { firstField = s; }

    @Override
    public int getImplVersion() { return VERSION; }
   
    @Override
    public String toString() {
        return "\n    Version: " + getImplVersion() +
               "\n    First field: " + firstField;
    }
 
When you read back the serialized object with code using a com.tangosol.io.WrapperBufferInput constructed with an object that implements the java.io.DataInput interface, but not the com.tangosol.io.ReadBuffer.BufferInput interface:

// byte[] buffer is populated with the raw serialized object data

WrapperBufferInput bin = new WrapperBufferInput(new DataInputStream(new ByteArrayInputStream(buffer)));

SimpleObjectWrapper wrapper = (SimpleObjectWrapper) pofContext.deserialize(bin);
System.out.println(wrapper);

Then the output would be:

Version: 1
Wrapped Object:
    Version: 1
    First field: first-field
Other Field: null

The otherField should be set to "Hello World" but is null.

Cause

Sign In with your My Oracle Support account

Don't have a My Oracle Support account? Click to get started

My Oracle Support provides customers with access to over a
Million Knowledge Articles and hundreds of Community platforms