chroniclechronicle-wire

Does ChronicleWire support optional fields in entities?


I am experimenting with ChronicleWire. As described in features optional fields are supported out of the box.

I just created a simple self-describing entity with one optional (nullable) field:

public class Foo extends SelfDescribingMarshallable {

  private String name;
  private Baz baz;
}

where baz might or might not be null and implements/extends Marshallable.

When I try to put entity to ChronicleMap I get an error:

Exception in thread "main" java.lang.NullPointerException
    at net.openhft.chronicle.bytes.BytesMarshaller$BytesMarshallableFieldAccess.getValue(BytesMarshaller.java:211)
    at net.openhft.chronicle.bytes.BytesMarshaller$FieldAccess.write(BytesMarshaller.java:152)
    at net.openhft.chronicle.bytes.BytesMarshaller.writeMarshallable(BytesMarshaller.java:70)
    at net.openhft.chronicle.bytes.BytesUtil.writeMarshallable(BytesUtil.java:295)
    at net.openhft.chronicle.bytes.BytesMarshallable.writeMarshallable(BytesMarshallable.java:48)
    at net.openhft.chronicle.bytes.BytesMarshaller$BytesMarshallableFieldAccess.getValue(BytesMarshaller.java:211)
    at net.openhft.chronicle.bytes.BytesMarshaller$FieldAccess.write(BytesMarshaller.java:152)

When I tried to use java optional instead and my entity changed to:

    public class Foo extends SelfDescribingMarshallable {

      private String name;
      private Optional<Baz> baz = Optional.empty();
    }

then another error raised:

Caused by: java.lang.IllegalArgumentException: type=class java.util.Optional is unsupported, it must either be of type Marshallable, String or AutoBoxed primitive Object
    at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:682)
    at net.openhft.chronicle.wire.ValueOut.untypedObject(ValueOut.java:795)
    at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:519)
    at net.openhft.chronicle.wire.WireMarshaller$ObjectFieldAccess.getValue(WireMarshaller.java:669)
    at net.openhft.chronicle.wire.WireMarshaller$FieldAccess.write(WireMarshaller.java:518)
    at net.openhft.chronicle.wire.WireMarshaller.writeMarshallable(WireMarshaller.java:199)
    at net.openhft.chronicle.wire.Marshallable.writeMarshallable(Marshallable.java:132)

I did not give up and tried to implement my own optional. Here it's:

@AllArgsConstructor(staticName = "of")
@NoArgsConstructor(staticName = "empty")
public class OptionalValue<T extends Marshallable> implements Marshallable {

    @Nullable
    private T value;

    @Override
    public void readMarshallable(@NotNull WireIn wire) throws IORuntimeException {
        var val = wire.read("value");
        if (!val.isNull()) {
            val.marshallable(value);
        }
    }

    @Override
    public void writeMarshallable(@NotNull WireOut wire) {
        if (value == null) {
            wire.write("value").nu11();
        } else {
            wire.write("value").marshallable(value);
        }
    }

    boolean isEmpty() { return value == null; }

    T get() { return value; }
}

In this case, I saw another error:

Caused by: java.lang.ClassCastException

    at net.openhft.chronicle.core.util.ObjectUtils.asCCE(ObjectUtils.java:294)
    at net.openhft.chronicle.core.util.ObjectUtils$ConversionFunction.apply(ObjectUtils.java:624)
    at net.openhft.chronicle.core.util.ObjectUtils$ConversionFunction.apply(ObjectUtils.java:592)
    at net.openhft.chronicle.core.ClassLocal.computeValue(ClassLocal.java:54)
    at java.base/java.lang.ClassValue.getFromHashMap(ClassValue.java:226)
    at java.base/java.lang.ClassValue.getFromBackup(ClassValue.java:208)
    at java.base/java.lang.ClassValue.get(ClassValue.java:114)
    at net.openhft.chronicle.core.util.ObjectUtils.convertTo0(ObjectUtils.java:257)
    ... 28 more
Caused by: java.lang.NoSuchMethodException: com.redacted.entity.OptionalValue.<init>(java.lang.String)
Caused by: java.lang.NoSuchMethodException: com.redacted.entity.OptionalValue.<init>(java.lang.String)

    at java.base/java.lang.Class.getConstructor0(Class.java:3349)
    at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553)
    at net.openhft.chronicle.core.util.ObjectUtils$ConversionFunction.apply(ObjectUtils.java:620)

So does anybody know how to fix or use it properly? ChronicleWire 2.22ae6


Solution

  • As SelfDescribingMarshallable is BytesMarsahallable, Map prefers to use this lower level serialization. However, because it is so low level, it doesn't support null values.

    You can tell the builder to use Marshallable by setting the valueMarshaller

    .valueMarshaller(new MarshallableReaderWriter<>(Foo.class))