Viraj
Viraj

Reputation: 5411

How to serialize Exception object using Kyro?

I have following custom exception which i need to serialize using kryo library.

public class CustomException extends Exception {
 public CustomException(){}
}

This is my seralizer

Kryo kryo = new Kryo();
kryo.writeClassAndObject(out, obj);

But serialization throws following exception.

com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor):    java.util.Collections$UnmodifiableRandomAccessList
Serialization trace:
suppressedExceptions (CustomException)
at      com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy.newInstantiatorOf(      Kryo.java:1272)
    at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1078)

I have resolved this registering the CustomException with JavaSerializer

 kryo.register(CustomException.class, new JavaSerializer());

But since JavaSerializer is very inefficient ( advised avoid using if possible in JavaSerializer javadoc) is there other way to serialize Exception object in kryo ?

Upvotes: 1

Views: 2297

Answers (1)

mad_fox
mad_fox

Reputation: 3188

Per the documentation, this can be fixed in several ways:

Serializers for a specific type use Java code to create a new instance of that type. Serializers such as FieldSerializer are generic and must handle creating a new instance of any class. By default, if a class has a zero argument constructor then it is invoked via ReflectASM or reflection, otherwise an exception is thrown. If the zero argument constructor is private, an attempt is made to access it via reflection using setAccessible. If this is acceptable, a private zero argument constructor is a good way to allow Kryo to create instances of a class without affecting the public API.

When ReflectASM or reflection cannot be used, Kryo can be configured to use an InstantiatorStrategy to handle creating instances of a class. Objenesis provides StdInstantiatorStrategy which uses JVM specific APIs to create an instance of a class without calling any constructor at all. While this works on many JVMs, a zero argument is generally more portable.

kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); Note that classes must be designed to be created in this way. If a class

expects its constructor to be called, it may be in an uninitialized state when created through this mechanism.

In many situations, you may want to have a strategy, where Kryo first tries to find and use a no-arg constructor and if it fails to do so, it should try to use StdInstantiatorStrategy as a fallback, because this one does not invoke any constructor at all. The configuration for this behavior could be expressed like this:

kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); However, the default behavior is to require a no-arg constructor.

Objenesis can also create new objects using Java's built-in serialization mechanism. Using this, the class must implement java.io.Serializable and the first zero argument constructor in a super class is invoked.

kryo.setInstantiatorStrategy(new SerializingInstantiatorStrategy()); You may also write your own

InstantiatorStrategy.

To customize only how a specific type is created, an ObjectInstantiator can be set. This will override ReflectASM, reflection, and the InstantiatorStrategy.

Registration registration = kryo.register(SomeClass.class);
registration.setObjectInstantiator(...); Alternatively, some serializers provide methods that can be overridden to customize object

creation.

kryo.register(SomeClass.class, new FieldSerializer(kryo, SomeClass.class) {
   public Object create (Kryo kryo, Input input, Class type) {
      return new SomeClass("some constructor arguments", 1234);
   }
});

Example:

/**
 * Because the creation/initialization of Kryo instances is rather expensive, 
 * in a multithreaded scenario you should pool Kryo instances. A very simple solution 
 * is to bind Kryo instances to Threads using ThreadLocal, like this:
 */
private static final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>() {
    @Override
    protected Kryo initialValue() {
        Kryo kryo = new Kryo();
        /*
         * In many situations, you may want to have a strategy, where Kryo first tries to find and use a no-arg constructor 
         * and if it fails to do so, it should try to use StdInstantiatorStrategy as a fallback, because this one does 
         * not invoke any constructor at all. The configuration for this behavior could be expressed like this:
         */
        kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
        return kryo;
    }
};

Upvotes: 1

Related Questions