diff --git a/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java b/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java index b6c3c371d..9ff639120 100644 --- a/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java @@ -32,6 +32,9 @@ import com.esotericsoftware.kryo.util.ObjectMap; import com.esotericsoftware.kryo.util.Util; +import java.lang.reflect.Field; +import java.util.HashSet; + /** Serializes objects using direct field assignment, providing both forward and backward compatibility. This means fields can be * added or removed without invalidating previously serialized bytes. Renaming or changing the type of a field is not supported. * Like {@link FieldSerializer}, it can serialize most classes without needing annotations. @@ -58,6 +61,24 @@ public CompatibleFieldSerializer (Kryo kryo, Class type, CompatibleFieldSerializ this.config = config; } + @Override + protected void initializeCachedFields() { + if (!super.config.extendedFieldNames) { + final HashSet hashSet = new HashSet(); + CachedField[] fields = cachedFields.fields; + for (int i = 0, n = fields.length; i < n; i++) { + final Field field = fields[i].field; + if (!hashSet.add(field.getName())) { + if (WARN) + warn("Detected duplicate field " + field.getName() + " in class hierarchy " + + field.getDeclaringClass() + + ". Consider enabling FieldSerializerConfig.extendedFieldNames"); + break; + } + } + } + } + @Override public void write (Kryo kryo, Output output, T object) { int pop = pushTypeVariables(); diff --git a/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java b/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java index 2352032e5..17cb51794 100644 --- a/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java +++ b/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java @@ -113,7 +113,7 @@ public void write (Kryo kryo, Object object) { output.flush(); } - class LoggerStub extends Logger { + public static class LoggerStub extends Logger { public List levels = new ArrayList(); public List messages = new ArrayList(); diff --git a/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java b/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java index 829382467..274660a8c 100644 --- a/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java +++ b/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java @@ -31,7 +31,10 @@ import java.util.List; import java.util.Objects; +import com.esotericsoftware.kryo.WarnUnregisteredClassesTest; +import com.esotericsoftware.minlog.Log; import org.apache.commons.lang.builder.EqualsBuilder; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -42,6 +45,16 @@ public class CompatibleFieldSerializerTest extends KryoTestCase { supportsCopy = true; } + WarnUnregisteredClassesTest.LoggerStub log; + + @Before + public void setUp() throws Exception { + log = new WarnUnregisteredClassesTest.LoggerStub(); + Log.setLogger(log); + Log.INFO(); + kryo = new Kryo(); + } + @Rule public ExpectedException exceptionRule = ExpectedException.none(); @Test @@ -642,4 +655,45 @@ public int hashCode () { return Objects.hash(value, list, serializable); } } + + @Test + public void testLogWarningOnDuplicateFieldInClassHierarchy() { + kryo.setReferences(true); + CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ClassWithDuplicateField.class); + serializer.getCompatibleFieldSerializerConfig().setChunkedEncoding(true); + serializer.getCompatibleFieldSerializerConfig().setExtendedFieldNames(false); + serializer.updateFields(); + kryo.register(ClassWithDuplicateField.class, serializer); + + final ClassWithDuplicateField duplicateField = new ClassWithDuplicateField(); + roundTrip(31, duplicateField); + assertEquals(2, log.messages.size()); + } + + static class ClassWithDuplicateField extends SuperClassWithDuplicateField { + private Boolean customNote = true; + } + + static class SuperClassWithDuplicateField implements Serializable { + private Boolean customNote = false; + + public SuperClassWithDuplicateField() {} + + public SuperClassWithDuplicateField(Boolean customNote) { + this.customNote = customNote; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SuperClassWithDuplicateField other = (SuperClassWithDuplicateField)obj; + if (customNote != other.customNote) + return false; + return true; + } + } }