javadeserializationunsafechroniclechronicle-map

Chronicle: object not initialized during deserialization


I currently have class which have some fields initialized in declaration, like this:

public class SomeClass implements Externalizable {

    private long id; 

    private final List<Hit> hits = new ArrayList<>();

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeLong(id);
        out.writeInt(hits.size());
        for (int i = 0; i < hits.size(); i++) {
            out.writeObject(hits.get(i));
        }
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        id = in.readLong();
        int size = in.readInt();
        for (int i = 0; i < size; i++) {
             hits.add((Hit) in.readObject()); //<--Nullpointer here, hits == null
        }
    }
}

And this class is used in filebased chronicle-map configured like this:

ChronicleMap<Long, SomeClass> storage = ChronicleMapBuilder
            .of(Long.class, SomeClass.class)
            .averageValueSize(avgEntrySize)
            .entries(entries)
            .createPersistedTo(new File(path));

The problem is that when I restart my application I get NullpointerException when chronicle tries to read saved map, because hits field wasn't initialized, meaning it is null.

I did some investigation and found that before calling readExternal chronical creates object of this class using UNSAFE.allocateInstance(in ExternalizableMarshaller):

protected E getInstance() throws Exception {
    return (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled);
}

So basically this is the reason why its not initialized. What I am trying to understand why it is using such approach instead of MethodHandle or reflection?

And maybe there is another way to fix this without modifying SomeClass, like some chronicle configuration property maybe?


Solution

  • This appears to be an issue with version 2.x which is no longer supported.

    In version 3.x it should call the default constructor if one exists. It will use Unsafe if there is no default constructor. I have added a test case which shows this works in 3.x

    https://github.com/OpenHFT/Chronicle-Map/blob/master/src/test/java/net/openhft/chronicle/map/externalizable/ExternalizableTest.java

    For version 2.x I suggest you need to check whether the list is null and set it as needed.