Skip to content

Commit

Permalink
Merge pull request #54 from ohshazbot/Issue53
Browse files Browse the repository at this point in the history
Adds support for Guava's Sets.UnmodifiableNavigableSet
  • Loading branch information
magro committed Jul 12, 2016
2 parents 7a43c46 + 4f7abb1 commit 9a4f71e
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.markdown
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
@@ -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);
}
}
@@ -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());
}
}

0 comments on commit 9a4f71e

Please sign in to comment.