Storage can now register classes with the serializer via the builder pattern. Added default serialization class to make registering defaults with kryo easier

This commit is contained in:
nathan 2020-08-06 02:34:47 +02:00
parent f59ee3ca24
commit 693bf4cef2
6 changed files with 195 additions and 11 deletions

View File

@ -0,0 +1,75 @@
package dorkbox.util.serialization;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Registration;
import com.esotericsoftware.kryo.Serializer;
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer;
public
class SerializationDefaults {
/**
* Allows for the kryo registration of sensible defaults in a common, well used way.
*/
public static
void register(Kryo kryo) {
// these are registered using the default serializers. We don't customize these, because we don't care about it.
kryo.register(String.class);
kryo.register(String[].class);
kryo.register(int[].class);
kryo.register(short[].class);
kryo.register(float[].class);
kryo.register(double[].class);
kryo.register(long[].class);
kryo.register(byte[].class);
kryo.register(char[].class);
kryo.register(boolean[].class);
kryo.register(Integer[].class);
kryo.register(Short[].class);
kryo.register(Float[].class);
kryo.register(Double[].class);
kryo.register(Long[].class);
kryo.register(Byte[].class);
kryo.register(Character[].class);
kryo.register(Boolean[].class);
kryo.register(Object[].class);
kryo.register(Object[][].class);
kryo.register(Class.class);
// necessary for the transport of exceptions.
kryo.register(StackTraceElement.class);
kryo.register(StackTraceElement[].class);
kryo.register(ArrayList.class);
kryo.register(HashMap.class);
kryo.register(HashSet.class);
kryo.register(Collections.emptyList().getClass());
kryo.register(Collections.emptySet().getClass());
kryo.register(Collections.emptyMap().getClass());
kryo.register(Collections.emptyNavigableSet().getClass());
kryo.register(Collections.emptyNavigableMap().getClass());
// hacky way to register unmodifiable serializers
Kryo kryoHack = new Kryo() {
@Override
public
Registration register(final Class type, final Serializer serializer) {
kryo.register(type, serializer);
return null;
}
};
UnmodifiableCollectionsSerializer.registerSerializers(kryoHack);
}
}

View File

@ -18,6 +18,7 @@ package dorkbox.util.serialization;
import java.io.IOException;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

View File

@ -23,6 +23,7 @@ import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.minlog.Log;
import dorkbox.util.serialization.SerializationDefaults;
import dorkbox.util.serialization.SerializationManager;
import io.netty.buffer.ByteBuf;
@ -32,6 +33,11 @@ class DefaultStorageSerializationManager implements SerializationManager {
Log.set(Log.LEVEL_ERROR);
}};
public
DefaultStorageSerializationManager() {
SerializationDefaults.register(kryo);
}
@Override
public
<T> SerializationManager register(final Class<T> clazz) {

View File

@ -37,7 +37,7 @@ import org.slf4j.Logger;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import dorkbox.util.OS;
import dorkbox.os.OS;
import dorkbox.util.serialization.SerializationManager;

View File

@ -1,9 +1,61 @@
package dorkbox.util.storage;
/**
*
*/
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.Serializer;
public
interface StorageBuilder {
/**
* Builds the storage using the specified configuration
*/
Storage build();
/**
* Registers the class using the lowest, next available integer ID and the {@link Kryo#getDefaultSerializer(Class) default serializer}.
* If the class is already registered, the existing entry is updated with the new serializer.
* <p>
* Registering a primitive also affects the corresponding primitive wrapper.
* <p>
* Because the ID assigned is affected by the IDs registered before it, the order classes are registered is important when using this
* method. The order must be the same at deserialization as it was for serialization.
*/
<T> StorageBuilder register(Class<T> clazz);
/**
* Registers the class using the specified ID. If the ID is already in use by the same type, the old entry is overwritten. If the ID
* is already in use by a different type, a {@link KryoException} is thrown.
* <p>
* Registering a primitive also affects the corresponding primitive wrapper.
* <p>
* IDs must be the same at deserialization as they were for serialization.
*
* @param id Must be >= 0. Smaller IDs are serialized more efficiently. IDs 0-8 are used by default for primitive types and String, but
* these IDs can be repurposed.
*/
<T> StorageBuilder register(Class<T> clazz, int id);
/**
* Registers the class using the lowest, next available integer ID and the specified serializer. If the class is already registered,
* the existing entry is updated with the new serializer.
* <p>
* Registering a primitive also affects the corresponding primitive wrapper.
* <p>
* Because the ID assigned is affected by the IDs registered before it, the order classes are registered is important when using this
* method. The order must be the same at deserialization as it was for serialization.
*/
<T> StorageBuilder register(Class<T> clazz, Serializer<T> serializer);
/**
* Registers the class using the specified ID and serializer. If the ID is already in use by the same type, the old entry is
* overwritten. If the ID is already in use by a different type, a {@link KryoException} is thrown.
* <p>
* Registering a primitive also affects the corresponding primitive wrapper.
* <p>
* IDs must be the same at deserialization as they were for serialization.
*
* @param id Must be >= 0. Smaller IDs are serialized more efficiently. IDs 0-8 are used by default for primitive types and String, but
* these IDs can be repurposed.
*/
<T> StorageBuilder register(Class<T> clazz, Serializer<T> serializer, int id);
}

View File

@ -24,8 +24,10 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.helpers.NOPLogger;
import com.esotericsoftware.kryo.Serializer;
import dorkbox.os.OS;
import dorkbox.util.FileUtil;
import dorkbox.util.OS;
import dorkbox.util.serialization.SerializationManager;
public
@ -151,7 +153,7 @@ class StorageSystem {
public static
class DiskBuilder implements StorageBuilder {
private File file;
private SerializationManager serializationManager;
private SerializationManager serializationManager = createDefaultSerializationManager(); // default
private boolean readOnly = false;
private Logger logger = null;
private long saveDelayInMilliseconds = 3000L; // default
@ -220,6 +222,34 @@ class StorageSystem {
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz) {
this.serializationManager.register(clazz);
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz, final int id) {
this.serializationManager.register(clazz, id);
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz, final Serializer<T> serializer) {
this.serializationManager.register(clazz, serializer);
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz, final Serializer<T> serializer, final int id) {
this.serializationManager.register(clazz, serializer, id);
return this;
}
/**
* Makes the storage system
*/
@ -230,10 +260,6 @@ class StorageSystem {
throw new IllegalArgumentException("file cannot be null!");
}
if (this.serializationManager == null) {
this.serializationManager = createDefaultSerializationManager();
}
// if we load from a NEW storage at the same location as an ALREADY EXISTING storage,
// without saving the existing storage first --- whoops!
synchronized (storages) {
@ -283,7 +309,7 @@ class StorageSystem {
/**
* Creates an in-memory only storage system
* Creates an in-memory only storage system. This storage system DOES NOT care about serializing data, so `register` has no effect.
*/
public static
class MemoryBuilder implements StorageBuilder {
@ -296,5 +322,29 @@ class StorageSystem {
Storage build() {
return new MemoryStorage();
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz) {
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz, final int id) {
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz, final Serializer<T> serializer) {
return this;
}
@Override
public
<T> StorageBuilder register(final Class<T> clazz, final Serializer<T> serializer, final int id) {
return this;
}
}
}