Skip to content
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

Adds support for Guava's Sets.UnmodifiableNavigableSet #54

Merged
merged 1 commit into from
Jul 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ A project that provides [kryo](https://github.com/EsotericSoftware/kryo) (v2 and
* guava/ImmutableMapSerializer - serializer for guava-libraries' ImmutableMap
* guava/ImmutableMultimapSerializer - serializer for guava-libraries' ImmutableMultimap
* guava/ImmutableSortedSetSerializer - serializer for guava-libraries' ImmutableSortedSet
* guava/UnmodifiableNavigableSetSerializer - serializer for guava-libraries' UnmodifiableNavigableSet
* jodatime/JodaDateTimeSerializer - serializer for joda's DateTime
* jodatime/JodaIntervalSerializer - serializer for joda's Interval
* jodatime/JodaLocalDateSerializer - serializer for joda's LocalDate
Expand Down Expand Up @@ -82,11 +83,12 @@ After that's done you can register the custom serializers at the kryo instance.
kryo.register( SampleProtoA.class, new ProtobufSerializer() ); // or override Kryo.getDefaultSerializer as shown below
// wicket
kryo.register( MiniMap.class, new MiniMapSerializer() );
// guava ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap
// guava ImmutableList, ImmutableSet, ImmutableMap, ImmutableMultimap, UnmodifiableNavigableSet
ImmutableListSerializer.registerSerializers( kryo );
ImmutableSetSerializer.registerSerializers( kryo );
ImmutableMapSerializer.registerSerializers( kryo );
ImmutableMultimapSerializer.registerSerializers( kryo );
UnmodifiableNavigableSetSerializer.registerSerializers( kryo );

The following code snippet shows how to use the `KryoReflectionFactorySupport` (can only be used with sun/oracly jdk!) and how other serializers are registered via the `getDefaultSerializer` lookup. If you don't want to use the `KryoReflectionFactorySupport` you can override the `getDefaultSerializer` method for your `new Kryo()` instance.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package de.javakaffee.kryoserializers.guava;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;

import java.lang.reflect.Field;
import java.util.NavigableSet;
import java.util.TreeSet;

/**
* A kryo {@link Serializer} for guava-libraries {@link ImmutableSortedSet}.
*/
public class UnmodifiableNavigableSetSerializer extends Serializer<NavigableSet<?>> {

Field delegate;

public UnmodifiableNavigableSetSerializer() {
// Do not allow nulls
super(false);
try {
Class<?> clazz = Class.forName(Sets.class.getCanonicalName() + "$UnmodifiableNavigableSet");
delegate = clazz.getDeclaredField("delegate");
delegate.setAccessible(true);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Issues reflectively writing UnmodifiableNavigableSet", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Issues reflectively writing UnmodifiableNavigableSet", e);
} catch (SecurityException e) {
throw new RuntimeException("Issues reflectively writing UnmodifiableNavigableSet", e);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Issues reflectively writing UnmodifiableNavigableSet", e);
}
}

@VisibleForTesting
protected Object getDelegateFromUnmodifiableNavigableSet(NavigableSet<?> object) {
try {
return delegate.get(object);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Issues reflectively writing UnmodifiableNavigableSet", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Issues reflectively writing UnmodifiableNavigableSet", e);
}
}

@Override
public void write(Kryo kryo, Output output, NavigableSet<?> object) {
// We want to preserve the underlying delegate class, so we need to reflectively get it and write it directly via kryo
kryo.writeClassAndObject(output, getDelegateFromUnmodifiableNavigableSet(object));
}

@Override
public NavigableSet<?> read(Kryo kryo, Input input, Class<NavigableSet<?>> type) {
return Sets.unmodifiableNavigableSet((NavigableSet<?>) kryo.readClassAndObject(input));
}

@Override
public NavigableSet<?> copy(Kryo kryo, NavigableSet<?> original) {
return Sets.unmodifiableNavigableSet((NavigableSet<?>) kryo.copy(getDelegateFromUnmodifiableNavigableSet(original)));
}

/**
* Creates a new {@link UnmodifiableNavigableSetSerializer} and registers its serializer
* for the UnmodifiableNavigableSetSerializer related class.
*
* @param kryo the {@link Kryo} instance to set the serializer on
*/
public static void registerSerializers(final Kryo kryo) {

// UnmodifiableNavigableSetSerializer (private class)

final UnmodifiableNavigableSetSerializer serializer = new UnmodifiableNavigableSetSerializer();

kryo.register(Sets.unmodifiableNavigableSet(new TreeSet<Object>()).getClass(), serializer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package de.javakaffee.kryoserializers.guava;

import static de.javakaffee.kryoserializers.KryoTest.deserialize;
import static de.javakaffee.kryoserializers.KryoTest.serialize;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.fail;

import java.util.NavigableSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListSet;

import com.esotericsoftware.kryo.Kryo;
import com.google.common.collect.Sets;

import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

/**
* Test for {@link ImmutableSortedSetSerializer}.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class UnmodifiableNavigableSetSerializerTest {

private Kryo _kryo;

@BeforeTest
public void setUp() {
_kryo = new Kryo();

UnmodifiableNavigableSetSerializer.registerSerializers(_kryo);
}

Class<NavigableSet> unmodifiableClass;
{
unmodifiableClass = (Class<NavigableSet>) Sets.unmodifiableNavigableSet(new TreeSet()).getClass();
}

UnmodifiableNavigableSetSerializer forUnwrapping = new UnmodifiableNavigableSetSerializer();

private void assertUnderlyingSet(NavigableSet<String> deserialized, Class<?> class1) {
assertEquals(
forUnwrapping.getDelegateFromUnmodifiableNavigableSet(deserialized).getClass(),
class1,
"Expected underlying class to match");
}

@Test
public void testEmptyTreeSet() {
final TreeSet<String> coreSet = Sets.newTreeSet();
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final byte[] serialized = serialize(_kryo, obj);
final NavigableSet<String> deserialized = deserialize(_kryo, serialized, unmodifiableClass);
assertTrue(deserialized.isEmpty());
assertEquals(deserialized.size(), obj.size());
// And ensure what we get is truly unmodifiable
try {
deserialized.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(deserialized, coreSet.getClass());
}

@Test
public void testEmptySkipList() {
final ConcurrentSkipListSet<String> coreSet = new ConcurrentSkipListSet();
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final byte[] serialized = serialize(_kryo, obj);
final NavigableSet<String> deserialized = deserialize(_kryo, serialized, unmodifiableClass);
assertTrue(deserialized.isEmpty());
assertEquals(deserialized.size(), obj.size());
// And ensure what we get is truly unmodifiable
try {
deserialized.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(deserialized, coreSet.getClass());
}

@Test
public void testPopulatedTreeSet() {
final TreeSet<String> coreSet = Sets.newTreeSet();
coreSet.add("k");
coreSet.add("r");
coreSet.add("y");
coreSet.add("o");
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final byte[] serialized = serialize(_kryo, obj);
final NavigableSet<String> deserialized = deserialize(_kryo, serialized, unmodifiableClass);
assertFalse(deserialized.isEmpty());
assertEquals(deserialized.size(), obj.size());
assertEquals(deserialized, obj);
// And ensure what we get is truly unmodifiable
try {
deserialized.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(deserialized, coreSet.getClass());
}

@Test
public void testPopulatedSkipList() {
final ConcurrentSkipListSet<String> coreSet = new ConcurrentSkipListSet();
coreSet.add("k");
coreSet.add("r");
coreSet.add("y");
coreSet.add("o");
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final byte[] serialized = serialize(_kryo, obj);
final NavigableSet<String> deserialized = deserialize(_kryo, serialized, unmodifiableClass);
assertFalse(deserialized.isEmpty());
assertEquals(deserialized.size(), obj.size());
assertEquals(deserialized, obj);
// And ensure what we get is truly unmodifiable
try {
deserialized.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(deserialized, coreSet.getClass());
}

// Kryo#copy tests

@Test
public void testCopyEmptyTreeSet() {
final TreeSet<String> coreSet = Sets.newTreeSet();
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final NavigableSet<String> copied = _kryo.copy(obj);
assertTrue(copied.isEmpty());
assertEquals(copied.size(), obj.size());

// And ensure what we get is truly unmodifiable
try {
copied.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(copied, coreSet.getClass());
}

@Test
public void testCopyEmptySkipList() {
final ConcurrentSkipListSet<String> coreSet = new ConcurrentSkipListSet();
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final NavigableSet<String> copied = _kryo.copy(obj);
assertTrue(copied.isEmpty());
assertEquals(copied.size(), obj.size());

// And ensure what we get is truly unmodifiable
try {
copied.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(copied, coreSet.getClass());
}

@Test
public void testCopyPopulatedTreeSet() {
final TreeSet<String> coreSet = Sets.newTreeSet();
coreSet.add("k");
coreSet.add("r");
coreSet.add("y");
coreSet.add("o");
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final NavigableSet<String> copied = _kryo.copy(obj);
assertFalse(copied.isEmpty());
assertEquals(copied.size(), obj.size());
assertEquals(copied, obj);

// And ensure what we get is truly unmodifiable
try {
copied.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(copied, coreSet.getClass());
}

@Test
public void testCopyPopulatedSkipList() {
final ConcurrentSkipListSet<String> coreSet = new ConcurrentSkipListSet();
coreSet.add("k");
coreSet.add("r");
coreSet.add("y");
coreSet.add("o");
final NavigableSet<String> obj = Sets.unmodifiableNavigableSet(coreSet);
final NavigableSet<String> copied = _kryo.copy(obj);
assertFalse(copied.isEmpty());
assertEquals(copied.size(), obj.size());
assertEquals(copied, obj);

// And ensure what we get is truly unmodifiable
try {
copied.add("a");
fail("Should have been unable to add a field to an unmodifiable collection post deserialization");
} catch (UnsupportedOperationException expected) {}
assertUnderlyingSet(copied, coreSet.getClass());
}
}