EsotericSoftware / kryo

Java binary serialization and cloning: fast, efficient, automatic
BSD 3-Clause "New" or "Revised" License
6.19k stars 824 forks source link

deserialization of an iCal4j objects fails - missing no-arg constructor for immutable properties #913

Closed overwerk closed 2 years ago

overwerk commented 2 years ago

Hi,

the deserialization of an net.fortuna.ical4j.model.Calendar (iCal4j lib) objects fails and I don't know how to handle this. Any help is appreciated :-)

My kryo settings:

        kryo = new Kryo();
    kryo.setRegistrationRequired(false);
    kryo.setReferences(true);
    kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
    kryo.setWarnUnregisteredClasses(true);
        kryo.register(DateTime.class, new JodaDateTimeSerializer());
    kryo.register(java.util.UUID.class, new UUIDSerializer());
    kryo.register(net.fortuna.ical4j.model.component.VEvent.class, new JavaSerializer());

The exception:

    00:02  WARN: Class is not registered: net.fortuna.ical4j.model.property.CalScale$ImmutableCalScale
    Note: To register this class use: kryo.register(net.fortuna.ical4j.model.property.CalScale.ImmutableCalScale.class);
    com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor):                                              net.fortuna.ical4j.model.property.CalScale$ImmutableCalScale
    Serialization trace:
    properties (net.fortuna.ical4j.model.Calendar)
    calendar (core.Calendar)
    calendar (core.EstateManager)
        at com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy.newInstantiatorOf(DefaultInstantiatorStrategy.java:111)
        at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1190)
        at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1199)
        at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:163)
        at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:117)
        at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:877)
        at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:245)
        at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:44)
        at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:796)
        at com.esotericsoftware.kryo.serializers.ReflectField.read(ReflectField.java:134)
        at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:185)
        at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:796)
        at com.esotericsoftware.kryo.serializers.ReflectField.read(ReflectField.java:134)
        at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:185)
        at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:796)
        at com.esotericsoftware.kryo.serializers.ReflectField.read(ReflectField.java:134)
        at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.read(CompatibleFieldSerializer.java:185)
        at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:877)
        at core.XmlFacade.fromFile(XmlFacade.java:319)
        at server.ContextListener.contextInitialized(ContextListener.java:126)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4763)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3800)
        at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:297)
        at org.apache.catalina.core.StandardContext.backgroundProcess(StandardContext.java:5603)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1396)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1400)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1400)
        at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1368)
        at java.lang.Thread.run(Thread.java:748)

Relevant part in my Calendar class:

    net.fortuna.ical4j.model.Calendar calendar = new net.fortuna.ical4j.model.Calendar();
    calendar.getProperties().add(new ProdId("-//Events Calendar//iCal4j 1.0//EN"));
    calendar.getProperties().add(CalScale.GREGORIAN);
theigl commented 2 years ago

Please see the docs about object instantiation.

You can use kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); to instantiate objects without default constructors.

overwerk commented 2 years ago

Hi,

when I use kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); I get null pointer exception. Probably because it happens excatly whats stated in the docs Creating the object by bypassing its constructors may leave the object in an uninitialized or invalid state.

exception:

15:23  WARN: Class is not registered: java.util.Collections$UnmodifiableRandomAccessList
Note: To register this class use: kryo.register(java.util.Collections.UnmodifiableRandomAccessList.class);
com.esotericsoftware.kryo.KryoException: java.lang.NullPointerException
Serialization trace:
parameters (net.fortuna.ical4j.model.ParameterList)
parameters (net.fortuna.ical4j.model.property.CalScale$ImmutableCalScale)
properties (net.fortuna.ical4j.model.Calendar)
calendar (core.Calendar)
calendar (core.EstateManager)
    at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:101)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.write(CompatibleFieldSerializer.java:107)
    at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:642)
    at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:85)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.write(CompatibleFieldSerializer.java:107)
    at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:711)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:159)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:44)
    at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:642)
    at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:85)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.write(CompatibleFieldSerializer.java:107)
    at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:642)
    at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:85)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.write(CompatibleFieldSerializer.java:107)
    at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:642)
    at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:85)
    at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.write(CompatibleFieldSerializer.java:107)
    at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:711)
    at core.XmlFacade.toFile(XmlFacade.java:226)
    at core.TaskSaveData.run(TaskSaveData.java:49)
    at it.sauronsoftware.cron4j.RunnableTask.execute(Unknown Source)
    at it.sauronsoftware.cron4j.TaskExecutor$Runner.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
    at java.util.Collections$UnmodifiableCollection.size(Collections.java:1032)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:91)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:44)
    at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:642)
    at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:85)
    ... 22 more

Would be great if there is another, safe solution to the problem.

theigl commented 2 years ago

Kryo supports most JDK data structures out of the box. UnmodifiableCollection is an exception, because it is not possible to write a safe serializer for these wrapper classes without using reflection.

You can add the UnmodifiableCollectionSerializer from the kryo-serializers project.

overwerk commented 2 years ago

Aaah, thanks a lot, it works!