Last active
March 28, 2018 19:36
-
-
Save mcollovati/c1fecceca661c6c6344fd86672baf099 to your computer and use it in GitHub Desktop.
Vertx Session Serialization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ExtendedSessionImpl ExtendedSessionImpl implements ExtendedSession, Shareable, ClusterSerializable { | |
// The original Vert.x Session | |
// I need to expand it with the createdAt attribute | |
Session delegate; | |
@Override | |
public void writeToBuffer(Buffer buffer) { | |
buffer.appendLong(createdAt); | |
if (useKryo) { | |
Kryo kryo = KryoSupport.kryo(); | |
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |
try (Output output = new Output(outputStream)) { | |
kryo.writeClassAndObject(output, delegate); | |
} | |
byte[] bytes = outputStream.toByteArray(); | |
buffer.appendInt(bytes.length); | |
buffer.appendBytes(bytes); | |
} else { | |
((ClusterSerializable)delegate).writeToBuffer(buffer); | |
} | |
} | |
@Override | |
public int readFromBuffer(int pos, Buffer buffer) { | |
createdAt = buffer.getLong(pos); | |
pos += 8; | |
int end; | |
if (useKryo) { | |
int delegateLength = buffer.getInt(pos); | |
pos += 4; | |
end = pos + delegateLength; | |
Kryo kryo = KryoSupport.kryo(); | |
try (Input input = new Input(buffer.getBytes(pos, pos + delegateLength))) { | |
input.close(); | |
this.delegate = (Session) kryo.readClassAndObject(input); | |
} | |
} else { | |
end = ((ClusterSerializable)delegate).readFromBuffer(pos, buffer); | |
} | |
return end; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.mcollovati.vertx.web.sstore; | |
import java.io.ByteArrayOutputStream; | |
import java.util.Objects; | |
import com.esotericsoftware.kryo.Kryo; | |
import com.esotericsoftware.kryo.Serializer; | |
import com.esotericsoftware.kryo.io.ByteBufferInput; | |
import com.esotericsoftware.kryo.io.ByteBufferOutput; | |
import com.esotericsoftware.kryo.io.Input; | |
import com.esotericsoftware.kryo.io.Output; | |
import com.esotericsoftware.kryo.serializers.FieldSerializer; | |
import io.vertx.core.cli.impl.ReflectionUtils; | |
import org.fest.reflect.core.Reflection; | |
import org.fest.reflect.exception.ReflectionError; | |
import org.fest.reflect.field.Invoker; | |
import org.junit.Test; | |
import static org.assertj.core.api.Assertions.assertThat; | |
import static org.assertj.core.api.Assertions.in; | |
public class KryoTest { | |
Kryo kryo = KryoSupport.kryo(); | |
@Test | |
public void shouldSerializeWithNonDefaultCtor() { | |
MyClass obj = new MyClass("Marco", "Collovati"); | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
try (Output output = new ByteBufferOutput(baos)) { | |
kryo.writeObject(output, obj); | |
} | |
MyClass deserialised; | |
try (Input input = new ByteBufferInput(baos.toByteArray())) { | |
deserialised = kryo.readObject(input, MyClass.class); | |
} | |
assertThat(deserialised) | |
.isNotSameAs(obj).isEqualTo(obj); | |
} | |
@Test | |
public void shouldSerializeWithNonDefaultCtorAndCustomSerializer() { | |
MyClass obj = new MyClass("Marco", "Collovati"); | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
Serializer<MyClass> serializer = new FieldSerializer<MyClass>(kryo, MyClass.class) { | |
@Override | |
public MyClass read(Kryo kryo, Input input, Class<MyClass> type) { | |
MyClass obj = super.read(kryo, input, type); | |
Injector.deserialise(obj); | |
return obj; | |
} | |
}; | |
try (Output output = new ByteBufferOutput(baos)) { | |
kryo.writeObject(output, obj, serializer); | |
} | |
MyClass deserialised; | |
try (Input input = new ByteBufferInput(baos.toByteArray())) { | |
deserialised = kryo.readObject(input, MyClass.class, serializer); | |
} | |
assertThat(deserialised) | |
.isNotEqualTo(obj) | |
.extracting(m -> m.firstName, m -> m.lastName) | |
.containsExactly("Deserialised " + obj.firstName, obj.lastName); | |
} | |
@Test | |
public void shouldSerializeWithNonDefaultCtorAndTransientFields() { | |
MyClass2 obj = new MyClass2("Marco", "Collovati"); | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
FieldSerializer<MyClass2> serializer = new FieldSerializer<>(kryo, MyClass2.class); | |
serializer.setSerializeTransient(true); | |
try (Output output = new ByteBufferOutput(baos)) { | |
kryo.writeObject(output, obj, serializer); | |
} | |
MyClass2 deserialised; | |
try (Input input = new ByteBufferInput(baos.toByteArray())) { | |
deserialised = kryo.readObject(input, MyClass2.class, serializer); | |
} | |
assertThat(deserialised) | |
.isNotSameAs(obj).isEqualTo(obj); | |
} | |
public static class MyClass { | |
private final String firstName; | |
private final String lastName; | |
public MyClass(String firstName, String lastName) { | |
this.firstName = firstName; | |
this.lastName = lastName; | |
} | |
@Override | |
public String toString() { | |
return String.format("%s %s", firstName, lastName); | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) { return true; } | |
if (!(o instanceof MyClass)) { return false; } | |
MyClass myClass = (MyClass) o; | |
return Objects.equals(firstName, myClass.firstName) && | |
Objects.equals(lastName, myClass.lastName); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(firstName, lastName); | |
} | |
private void refreshTransients(Injector injector) { | |
assertThat(injector).isNotNull(); | |
Reflection.field("firstName").ofType(String.class).in(this) | |
.set("Deserialised " + this.firstName); | |
} | |
} | |
public static class MyClass2 { | |
private final transient String firstName; | |
private final transient String lastName; | |
public MyClass2(String firstName, String lastName) { | |
this.firstName = firstName; | |
this.lastName = lastName; | |
} | |
@Override | |
public String toString() { | |
return String.format("%s %s", firstName, lastName); | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) { return true; } | |
if (!(o instanceof MyClass2)) { return false; } | |
MyClass2 myClass = (MyClass2) o; | |
return Objects.equals(firstName, myClass.firstName) && | |
Objects.equals(lastName, myClass.lastName); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(firstName, lastName); | |
} | |
} | |
static class Injector { | |
private static final Injector injector = new Injector(); | |
static void deserialise(Object object) { | |
try { | |
org.fest.reflect.method.Invoker<Void> deserialiser = Reflection.method("refreshTransients") | |
.withReturnType(void.class) | |
.withParameterTypes(Injector.class).in(object); | |
deserialiser.invoke(injector); | |
} catch (ReflectionError ex) { | |
// No custom method found; Do nothing | |
} | |
} | |
<T> T getInstance(Class<T> type) { | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment