io.hekate.codec.kryo.KryoCodecFactory Maven / Gradle / Ivy
Show all versions of hekate-core Show documentation
/*
* Copyright 2020 The Hekate Project
*
* The Hekate Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.hekate.codec.kryo;
import com.esotericsoftware.kryo.ClassResolver;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Kryo.DefaultInstantiatorStrategy;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.UnsafeInput;
import com.esotericsoftware.kryo.io.UnsafeOutput;
import io.hekate.codec.Codec;
import io.hekate.codec.CodecFactory;
import io.hekate.codec.DataReader;
import io.hekate.codec.DataWriter;
import io.hekate.core.Hekate;
import io.hekate.core.HekateBootstrap;
import io.hekate.util.format.ToString;
import io.hekate.util.format.ToStringIgnore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objenesis.strategy.InstantiatorStrategy;
import org.objenesis.strategy.StdInstantiatorStrategy;
/**
* « start hereKryo-based
* implementation of {@link CodecFactory} interface.
*
* Module dependency
*
* Kryo integration is provided by the 'hekate-codec-kryo' module and can be imported into the project dependency management system as
* in the example below:
*
*
*
*
* {@code
*
* io.hekate
* hekate-codec-kryo
* REPLACE_VERSION
*
* }
*
*
* {@code
* compile group: 'io.hekate', name: 'hekate-codec-kryo', version: 'REPLACE_VERSION'
* }
*
*
* {@code
*
* }
*
*
*
* Configuration
*
* Each call of {@link #createCodec()} method will produces a new independent {@link Kryo} instance and will configure it
* according to properties of this factory (see description of property setters).
* ${source: codec/kryo/KryoCodecFactoryJavadocTest.java#configuration}
*
*
* @param Base type of data that should be supported by this factory.
*
* @see HekateBootstrap#setDefaultCodec(CodecFactory)
*/
public class KryoCodecFactory implements CodecFactory {
/** See {@link #setKnownTypes(List)}. */
private List> knownTypes;
/** See {@link #setRegistrationRequired(boolean)}. */
private boolean registrationRequired;
/** See {@link #setSerializers(Map)}. */
private Map, Serializer>> serializers;
/** See {@link #setDefaultSerializers(Map)}. */
private Map, Serializer>> defaultSerializers;
/** See {@link #setUnsafeIo(boolean)}. */
private boolean unsafeIo = true;
/** See {@link #setReferences(Boolean)}. */
private Boolean references;
/** See {@link #setCacheUnknownTypes(boolean)}. */
private boolean cacheUnknownTypes;
/** See {@link #setInstantiatorStrategy(InstantiatorStrategy)}. */
@ToStringIgnore
private InstantiatorStrategy instantiatorStrategy = new DefaultInstantiatorStrategy(new StdInstantiatorStrategy());
@Override
@SuppressWarnings("unchecked")
public Codec createCodec() {
return (Codec)new KryoCodec(this);
}
/**
* Returns the list of known java types (see {@link #setKnownTypes(List)}).
*
* @return List of known types.
*/
public List> getKnownTypes() {
return knownTypes;
}
/**
* Sets the list of known Java types. Such types will be registered via {@link Kryo#register(Class)}.
*
*
* Notice:
* Exactly the same list of types should be registered on all cluster nodes. Otherwise Kryo will not be able deserialize data.
* Fore more details please see {@link Kryo#register(Class)}.
*
*
* @param knownTypes List of known Java types.
*/
public void setKnownTypes(List> knownTypes) {
this.knownTypes = knownTypes;
}
/**
* Fluent-style version of {@link #setKnownTypes(List)}.
*
* @param type Type.
*
* @return This instance.
*/
public KryoCodecFactory withKnownType(Class> type) {
if (knownTypes == null) {
knownTypes = new ArrayList<>();
}
knownTypes.add(type);
return this;
}
/**
* Fluent-style version of {@link #setKnownTypes(List)}.
*
* @param types List of known Java types.
*
* @return This instance.
*/
public KryoCodecFactory withKnownTypes(List> types) {
if (knownTypes == null) {
knownTypes = new ArrayList<>();
}
knownTypes.addAll(types);
return this;
}
/**
* Returns the flag indicating if registration is required for all serializable classes (see {@link #setRegistrationRequired(boolean)}).
*
* @return Flag indicating if registration is required for all serializable classes.
*/
public boolean isRegistrationRequired() {
return registrationRequired;
}
/**
* Sets the flag indicating that all serializable classes must be registered via {@link #setKnownTypes(List)}.
*
*
* In order to get the best performance of data serialization it is recommended to register all serializable classes in advance.
* If this flag is set to {@code true} then Kryo will throw an error when trying to serialize a class that is not {@link
* #setKnownTypes(List) registered}. However sometimes it is not feasible to do so, in such case this flag should be set {@code false}.
*
*
*
* Default value of this parameter is {@code false} (i.e. registration is not required).
*
*
* @param registrationRequired {@code true} if all serializable classes should be {@link #setKnownTypes(List) registered} in advance.
*/
public void setRegistrationRequired(boolean registrationRequired) {
this.registrationRequired = registrationRequired;
}
/**
* Fluent-style version of {@link #setRegistrationRequired(boolean)}.
*
* @param registrationRequired {@code true} if all serializable classes should be {@link #setKnownTypes(List) registered} in advance.
*
* @return This instance.
*/
public KryoCodecFactory withRegistrationRequired(boolean registrationRequired) {
setRegistrationRequired(registrationRequired);
return this;
}
/**
* Returns the map of custom serializer (see {@link #setSerializers(Map)}).
*
* @return Map of custom serializer.
*/
public Map, Serializer>> getSerializers() {
return serializers;
}
/**
* Sets the map of custom serializers. Such serializers will be registered via {@link Kryo#register(Class, Serializer)}.
*
* @param serializers Map of serializers.
*/
public void setSerializers(Map, Serializer>> serializers) {
this.serializers = serializers;
}
/**
* Fluent-style version of {@link #setSerializers(Map)}.
*
* @param type Type.
* @param serializer Type serializer.
*
* @return This instance.
*/
public KryoCodecFactory withSerializer(Class> type, Serializer> serializer) {
if (serializers == null) {
serializers = new HashMap<>();
}
serializers.put(type, serializer);
return this;
}
/**
* Returns the map of default serializers (see {@link #setSerializers(Map)}).
*
* @return Map of default serializer.
*/
public Map, Serializer>> getDefaultSerializers() {
return defaultSerializers;
}
/**
* Sets the map of default serializer. Such serializers will be registered via {@link Kryo#addDefaultSerializer(Class, Serializer)}.
*
* @param defaultSerializers Map of default serializer.
*/
public void setDefaultSerializers(Map, Serializer>> defaultSerializers) {
this.defaultSerializers = defaultSerializers;
}
/**
* Fluent style version of {@link #setDefaultSerializers(Map)}.
*
* @param type Type to serialize.
* @param serializer Serializer.
* @param Type to serialize.
*
* @return This instance.
*/
public KryoCodecFactory withDefaultSerializer(Class type, Serializer serializer) {
if (defaultSerializers == null) {
defaultSerializers = new HashMap<>();
}
defaultSerializers.put(type, serializer);
return this;
}
/**
* Returns the instantiator strategy (see {@link #setInstantiatorStrategy(InstantiatorStrategy)}).
*
* @return Instantiator strategy.
*/
public InstantiatorStrategy getInstantiatorStrategy() {
return instantiatorStrategy;
}
/**
* Sets instantiator strategy. Such strategy will be registered via {@link Kryo#setInstantiatorStrategy(InstantiatorStrategy)}.
*
*
* By default this parameter is set to {@link DefaultInstantiatorStrategy} with a fallback to {@link StdInstantiatorStrategy}.
*
*
* @param instantiatorStrategy Instantiator strategy.
*/
public void setInstantiatorStrategy(InstantiatorStrategy instantiatorStrategy) {
this.instantiatorStrategy = instantiatorStrategy;
}
/**
* Fluent-style version of {@link #setInstantiatorStrategy(InstantiatorStrategy)}.
*
* @param instantiatorStrategy Instantiator strategy.
*
* @return This instance.
*/
public KryoCodecFactory withInstantiatorStrategy(InstantiatorStrategy instantiatorStrategy) {
setInstantiatorStrategy(instantiatorStrategy);
return this;
}
/**
* Returns {@code true} if {@code Unsafe}-based IO should be used by Kryo (see {@link #setUnsafeIo(boolean)}).
*
* @return {@code true} if {@code Unsafe}-based IO should be used by Kryo.
*/
public boolean isUnsafeIo() {
return unsafeIo;
}
/**
* Set to {@code true} in order to use {@link UnsafeInput}/{@link UnsafeOutput} with Kryo.
*
*
* Default value of this parameter is {@code true}.
*
*
* @param unsafeIo {@code true} to enable {@code Unsafe}-based IO.
*/
public void setUnsafeIo(boolean unsafeIo) {
this.unsafeIo = unsafeIo;
}
/**
* Fluent-style version of {@link #setUnsafeIo(boolean)}.
*
* @param unsafeIo {@code true} to enable {@code Unsafe}-based IO.
*
* @return This instance.
*/
public KryoCodecFactory withUnsafeIo(boolean unsafeIo) {
setUnsafeIo(unsafeIo);
return this;
}
/**
* Returns the value that should override {@link Kryo#setReferences(boolean)} flag (see {@link #setReferences(Boolean)}).
*
* @return Value that should override {@link Kryo#setReferences(boolean)} flag.
*/
public Boolean getReferences() {
return references;
}
/**
* Sets the value that should override the {@link Kryo#setReferences(boolean)} flag. If value of this parameter is {@code null}
* (default) then {@link Kryo#setReferences(boolean)} will not be called during Kryo instance construction.
*
* @param references Value that should override the {@link Kryo#setReferences(boolean)} flag.
*/
public void setReferences(Boolean references) {
this.references = references;
}
/**
* Fluent-style version of {@link #setReferences(Boolean)}.
*
* @param references Value that should override the {@link Kryo#setReferences(boolean)} flag.
*
* @return This instance.
*/
public KryoCodecFactory withReferences(Boolean references) {
setReferences(references);
return this;
}
/**
* Returns the flag that controls whether class-to-identifiers mapping should be cached (see {@link #setCacheUnknownTypes(boolean)}).
*
* @return Flag that controls whether class-to-identifiers mapping should be cached.
*/
public boolean isCacheUnknownTypes() {
return cacheUnknownTypes;
}
/**
* Sets the flag that controls the behavior of unmapped classes caching.
*
*
* If set to {@code true} then mapping of unknown classes (that were not explicitly registered via {@link #withKnownType(Class)})
* to their generated identifiers will be preserved across multiple calls of {@link Codec#encode(Object, DataWriter) encode}/{@link
* Codec#decode(DataReader) decode} methods when called on the same {@link Codec} instance. If set to {@code false} then such
* mapping will be reset (see {@link ClassResolver#reset()}) after each {@link Codec#encode(Object, DataWriter) encode}/{@link
* Codec#decode(DataReader) decode} operation.
*
*
*
* Note that codec factories with classes caching enabled can not be used as the
* {@link HekateBootstrap#setDefaultCodec(CodecFactory) default codec} of {@link Hekate} node, as the default codec must be stateless.
*
*
*
* Default value of this parameter is {@code false}.
*
*
* @param cacheUnknownTypes Flag that controls whether class-to-identifiers mapping should be cached.
*/
public void setCacheUnknownTypes(boolean cacheUnknownTypes) {
this.cacheUnknownTypes = cacheUnknownTypes;
}
/**
* Fluent-style version of {@link #setCacheUnknownTypes(boolean)}.
*
* @param cacheUnknownTypes Flag that controls whether class-to-identifiers mapping should be cached.
*
* @return This instance.
*/
public KryoCodecFactory withCacheUnknownTypes(boolean cacheUnknownTypes) {
setCacheUnknownTypes(cacheUnknownTypes);
return this;
}
@Override
public String toString() {
return ToString.format(this);
}
}