-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes to support better separation between applying global and type serialization #7
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package info.jerrinot.subzero; | ||
|
||
import info.jerrinot.subzero.internal.strategy.GlobalKryoStrategy; | ||
|
||
/** | ||
* This class is for registering Kryo serialization globally with custom serialization for specific types | ||
* programatically rather than through use of subzero-serializers.properties properties file. | ||
* | ||
* <code> | ||
* import static info.jerrinot.subzero.UserSerializerConfig.register; | ||
* [...] | ||
* | ||
* public static class MyGlobalSerializer extends AbstractGlobalUserSerializer { | ||
* | ||
* public MyGlobalSerializer() { | ||
* super(register(MyObject.class, new MyObjectKryoSerializer()) | ||
* .register(MyOtherObject.class, new MyOtherObjectKryoSerializer()); | ||
* } | ||
* | ||
* } | ||
* </code> | ||
* | ||
*/ | ||
public abstract class AbstractGlobalUserSerializer extends AbstractSerializer { | ||
|
||
public AbstractGlobalUserSerializer(UserSerializerConfig.UserSerializationBuilder userSerializer) { | ||
super(new GlobalKryoStrategy(userSerializer)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package info.jerrinot.subzero; | ||
|
||
import com.hazelcast.core.HazelcastInstance; | ||
import com.hazelcast.core.HazelcastInstanceAware; | ||
import com.hazelcast.nio.ObjectDataInput; | ||
import com.hazelcast.nio.ObjectDataOutput; | ||
import com.hazelcast.nio.serialization.StreamSerializer; | ||
import info.jerrinot.subzero.internal.strategy.KryoStrategy; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
|
||
abstract class AbstractSerializer<T> implements StreamSerializer<T>, HazelcastInstanceAware { | ||
private int autoGeneratedTypeId; | ||
private HazelcastInstance hazelcastInstance; | ||
private KryoStrategy<T> strategy; | ||
|
||
AbstractSerializer(KryoStrategy<T> strategy) { | ||
this.strategy = strategy; | ||
} | ||
|
||
@Override | ||
public final void write(ObjectDataOutput out, T object) throws IOException { | ||
strategy.write((OutputStream) out, object); | ||
} | ||
|
||
@Override | ||
public final T read(ObjectDataInput in) throws IOException { | ||
return strategy.read((InputStream) in); | ||
} | ||
|
||
/** | ||
* Override this method to returns your own type ID. | ||
* <p> | ||
* Default implementation relies on serializers registration order - all | ||
* your cluster members have to register SubZero in the same order otherwise | ||
* things will get out-of-sync you you will get tons of serializations errors. | ||
* <p> | ||
* All serializers registered in Hazelcast have to return a unique type ID. | ||
* | ||
* @return serializer type ID. | ||
*/ | ||
@Override | ||
public int getTypeId() { | ||
return autoGeneratedTypeId; | ||
} | ||
|
||
@Override | ||
public final void destroy() { | ||
strategy.destroy(hazelcastInstance); | ||
} | ||
|
||
@Override | ||
public final void setHazelcastInstance(HazelcastInstance hazelcastInstance) { | ||
strategy.setHazelcastInstance(hazelcastInstance); | ||
this.hazelcastInstance = hazelcastInstance; | ||
this.autoGeneratedTypeId = strategy.newId(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package info.jerrinot.subzero; | ||
|
||
import info.jerrinot.subzero.internal.PropertyUserSerializer; | ||
import info.jerrinot.subzero.internal.strategy.TypedKryoStrategy; | ||
|
||
|
||
/** | ||
* This class is for registering Kryo serialization for a type where you need custom serialization and/or you want | ||
* control over the serializable class type ID. | ||
* | ||
* The class is intended to be extended. It optionally takes a custom serializer | ||
* {@link com.esotericsoftware.kryo.Serializer} that will be registered with the internal Kryo serializer instance for | ||
* the provided class. By not providing a custom serializer, the behaviour of the class is to check for a custom | ||
* serializer registration in the subzero-serializers.properties properties file. | ||
* | ||
* <code> | ||
* public static class MySerializer extends AbstractTypeSpecificUserSerializer<MyObject> { | ||
* | ||
* public MySerializer() { | ||
* super(MyObject.class, new MyObjectKryoSerializer()); | ||
* } | ||
* | ||
* } | ||
* </code> | ||
* | ||
* It is also possible to define a static class type ID through overriding {@link #getTypeId()} which would otherwise | ||
* return an auto-generated ID unique to the type. | ||
* | ||
* @param <T> type to register Kyro serialization against | ||
*/ | ||
public abstract class AbstractTypeSpecificUserSerializer<T> extends AbstractSerializer<T> { | ||
|
||
public AbstractTypeSpecificUserSerializer(Class<T> clazz, com.esotericsoftware.kryo.Serializer serializer) { | ||
super(new TypedKryoStrategy<T>(clazz, UserSerializerConfig.register(clazz, serializer))); | ||
} | ||
|
||
/** | ||
* @param clazz class this serializer uses. | ||
* | ||
*/ | ||
public AbstractTypeSpecificUserSerializer(Class<T> clazz) { | ||
super(new TypedKryoStrategy<T>(clazz, new PropertyUserSerializer())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,21 @@ | ||
package info.jerrinot.subzero; | ||
|
||
import com.hazelcast.core.HazelcastInstance; | ||
import com.hazelcast.core.HazelcastInstanceAware; | ||
import com.hazelcast.nio.ObjectDataInput; | ||
import com.hazelcast.nio.ObjectDataOutput; | ||
import com.hazelcast.nio.serialization.StreamSerializer; | ||
import info.jerrinot.subzero.internal.PropertyUserSerializer; | ||
import info.jerrinot.subzero.internal.strategy.GlobalKryoStrategy; | ||
import info.jerrinot.subzero.internal.strategy.KryoStrategy; | ||
import info.jerrinot.subzero.internal.strategy.TypedKryoStrategy; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
|
||
public class Serializer<T> implements StreamSerializer<T>, HazelcastInstanceAware { | ||
private int autoGeneratedTypeId; | ||
private HazelcastInstance hazelcastInstance; | ||
private KryoStrategy<T> strategy; | ||
public final class Serializer<T> extends AbstractSerializer<T> { | ||
|
||
Serializer() { | ||
this.strategy = new GlobalKryoStrategy<T>(userSerializers()); | ||
super(new GlobalKryoStrategy<T>(new PropertyUserSerializer())); | ||
} | ||
|
||
/** | ||
* @param clazz class this serializer uses. | ||
* | ||
*/ | ||
public Serializer(Class<T> clazz) { | ||
this.strategy = new TypedKryoStrategy<T>(clazz, userSerializers()); | ||
} | ||
|
||
@Override | ||
public final void write(ObjectDataOutput out, T object) throws IOException { | ||
strategy.write((OutputStream) out, object); | ||
} | ||
|
||
@Override | ||
public final T read(ObjectDataInput in) throws IOException { | ||
return strategy.read((InputStream) in); | ||
super(new TypedKryoStrategy<T>(clazz, new PropertyUserSerializer())); | ||
} | ||
|
||
public UserSerializer userSerializers() { | ||
return new PropertyUserSerializer(); | ||
} | ||
|
||
/** | ||
* Override this method to returns your own type ID. | ||
* | ||
* Default implementation relies on serializers registration order - all | ||
* your cluster members have to register SubZero in the same order otherwise | ||
* things will get out-of-sync you you will get tons of serializations errors. | ||
* | ||
* All serializers registered in Hazelcast have to return a unique type ID. | ||
* | ||
* @return serializer type ID. | ||
*/ | ||
@Override | ||
public int getTypeId() { | ||
return autoGeneratedTypeId; | ||
} | ||
|
||
@Override | ||
public final void destroy() { | ||
strategy.destroy(hazelcastInstance); | ||
} | ||
|
||
@Override | ||
public final void setHazelcastInstance(HazelcastInstance hazelcastInstance) { | ||
strategy.setHazelcastInstance(hazelcastInstance); | ||
this.hazelcastInstance = hazelcastInstance; | ||
this.autoGeneratedTypeId = strategy.newId(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,22 +23,38 @@ private SubZero() { | |
* Use SubZero as a global serializer. | ||
* | ||
* This method configures Hazelcast to delegate a class serialization to SubZero when the class | ||
* has no explicit strategy configured. | ||
* has no explicit strategy configured. Uses {@link Serializer} serializer implementation | ||
* internally. | ||
* | ||
* @param config Hazelcast configuration to inject SubZero into | ||
* @return Hazelcast configuration. | ||
*/ | ||
public static Config useAsGlobalSerializer(Config config) { | ||
return useAsGlobalSerializerInternal(config, Serializer.class); | ||
} | ||
|
||
/** | ||
* Use SubZero as a global serializer. | ||
* | ||
* This method configures Hazelcast to delegate a class serialization to SubZero when the class | ||
* has no explicit strategy configured. | ||
* | ||
* @param config Hazelcast configuration to inject SubZero into | ||
* @param serializerClazz Class of global serializer implementation to use | ||
* @return Hazelcast configuration. | ||
*/ | ||
public static <T> Config useAsGlobalSerializer(Config config, Class<? extends AbstractGlobalUserSerializer> serializerClazz) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not add this method. Methods in the I expect majority of users won't/shouldn't need to create custom subclasses. The extra methods here could be confusing for new users. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way I see it, SubZero is all about providing convenience, so I'm not convinced about not having some convenience method to add custom serialization configuration. The alternative is forcing the user to write out what's already available internally to SubZero class when registering the custom global serializer to hazelcast, which seems a waste. I couldn't comment on how widely used custom serialization is (probably about 10% of Kryo users going by github stars https://github.com/magro/kryo-serializers) but I will remove the methods if you're not convinced. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, makes sense. let's keep it in. |
||
SerializationConfig serializationConfig = config.getSerializationConfig(); | ||
injectSubZero(serializationConfig); | ||
injectSubZero(serializationConfig, serializerClazz); | ||
return config; | ||
} | ||
|
||
/** | ||
* Use SubZero as a global serializer. | ||
* | ||
* This method configures Hazelcast to delegate a class serialization to SubZero when the class | ||
* has no explicit strategy configured. | ||
* has no explicit strategy configured. Uses {@link Serializer} serializer implementation | ||
* internally. | ||
* | ||
* This method it intended to be used to configure {@link ClientConfig} instances, but | ||
* I do not want to create a hard-dependency on Hazelcast Client module. | ||
|
@@ -47,6 +63,27 @@ public static Config useAsGlobalSerializer(Config config) { | |
* @return Hazelcast configuration. | ||
*/ | ||
public static <T> T useAsGlobalSerializer(T config) { | ||
return useAsGlobalSerializerInternal(config, Serializer.class); | ||
} | ||
|
||
/** | ||
* Use SubZero as a global serializer. | ||
* | ||
* This method configures Hazelcast to delegate a class serialization to SubZero when the class | ||
* has no explicit strategy configured. | ||
* | ||
* This method it intended to be used to configure {@link ClientConfig} instances, but | ||
* I do not want to create a hard-dependency on Hazelcast Client module. | ||
* | ||
* @param config Hazelcast configuration to inject SubZero into | ||
* @param serializerClazz Class of global serializer implementation to use | ||
* @return Hazelcast configuration. | ||
*/ | ||
public static <T> T useAsGlobalSerializer(T config, Class<? extends AbstractGlobalUserSerializer> serializerClazz) { | ||
return useAsGlobalSerializer(config, serializerClazz); | ||
} | ||
|
||
private static <T> T useAsGlobalSerializerInternal(T config, Class<? extends AbstractSerializer> serializerClazz) { | ||
String className = config.getClass().getName(); | ||
SerializationConfig serializationConfig; | ||
if (className.equals("com.hazelcast.client.config.ClientConfig")) { | ||
|
@@ -58,17 +95,17 @@ public static <T> T useAsGlobalSerializer(T config) { | |
} else { | ||
throw new IllegalArgumentException("Unknown configuration object " + config); | ||
} | ||
injectSubZero(serializationConfig); | ||
injectSubZero(serializationConfig, serializerClazz); | ||
return config; | ||
} | ||
|
||
private static void injectSubZero(SerializationConfig serializationConfig) { | ||
private static void injectSubZero(SerializationConfig serializationConfig, Class<? extends AbstractSerializer> serializerClazz) { | ||
GlobalSerializerConfig globalSerializerConfig = serializationConfig.getGlobalSerializerConfig(); | ||
if (globalSerializerConfig == null) { | ||
globalSerializerConfig = new GlobalSerializerConfig(); | ||
serializationConfig.setGlobalSerializerConfig(globalSerializerConfig); | ||
} | ||
globalSerializerConfig.setClassName(Serializer.class.getName()).setOverrideJavaSerialization(true); | ||
globalSerializerConfig.setClassName(serializerClazz.getName()).setOverrideJavaSerialization(true); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JavaDoc is needed here. with a description when and how-to extend this class