IGNITE-2213: Fixed serialization of duplicate fields.

Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/627134b2
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/627134b2
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/627134b2

Branch: refs/heads/master
Commit: 627134b25ca730d7a7dbcec9facde158c5d15dc3
Parents: d8576b8
Author: vozerov-gridgain <voze...@gridgain.com>
Authored: Tue Dec 22 11:28:44 2015 +0300
Committer: vozerov-gridgain <voze...@gridgain.com>
Committed: Tue Dec 22 11:28:44 2015 +0300

----------------------------------------------------------------------
 .../internal/binary/BinaryClassDescriptor.java  |  56 ++++++++--
 .../ignite/internal/binary/BinaryUtils.java     |  11 ++
 .../binary/BinaryMarshallerSelfTest.java        | 110 +++++++++++++++++++
 .../IgniteBinaryCacheQueryTestSuite.java        |   3 +-
 4 files changed, 169 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/627134b2/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
index 1eb3882..1105809 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryClassDescriptor.java
@@ -35,6 +35,7 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.math.BigDecimal;
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -44,11 +45,9 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
-import static java.lang.reflect.Modifier.isStatic;
-import static java.lang.reflect.Modifier.isTransient;
-
 /**
  * Binary class descriptor.
  */
@@ -250,21 +249,24 @@ public class BinaryClassDescriptor {
 
                 BinarySchema.Builder schemaBuilder = 
BinarySchema.Builder.newBuilder();
 
+                Set<String> duplicates = duplicateFields(cls);
+
                 Collection<String> names = new HashSet<>();
                 Collection<Integer> ids = new HashSet<>();
 
                 for (Class<?> c = cls; c != null && !c.equals(Object.class); c 
= c.getSuperclass()) {
                     for (Field f : c.getDeclaredFields()) {
-                        int mod = f.getModifiers();
-
-                        if (!isStatic(mod) && !isTransient(mod)) {
+                        if (serializeField(f)) {
                             f.setAccessible(true);
 
                             String name = f.getName();
 
-                            if (!names.add(name))
-                                throw new BinaryObjectException("Duplicate 
field name [fieldName=" + name +
-                                    ", cls=" + cls.getName() + ']');
+                            if (duplicates.contains(name))
+                                name = BinaryUtils.qualifiedFieldName(c, name);
+
+                            boolean added = names.add(name);
+
+                            assert added : name;
 
                             int fieldId = idMapper.fieldId(typeId, name);
 
@@ -308,6 +310,42 @@ public class BinaryClassDescriptor {
     }
 
     /**
+     * Find all fields with duplicate names in the class.
+     *
+     * @param cls Class.
+     * @return Fields with duplicate names.
+     */
+    private static Set<String> duplicateFields(Class cls) {
+        Set<String> all = new HashSet<>();
+        Set<String> duplicates = new HashSet<>();
+
+        for (Class<?> c = cls; c != null && !c.equals(Object.class); c = 
c.getSuperclass()) {
+            for (Field f : c.getDeclaredFields()) {
+                if (serializeField(f)) {
+                    String name = f.getName();
+
+                    if (!all.add(name))
+                        duplicates.add(name);
+                }
+            }
+        }
+
+        return duplicates;
+    }
+
+    /**
+     * Whether the field must be serialized.
+     *
+     * @param f Field.
+     * @return {@code True} if must be serialized.
+     */
+    private static boolean serializeField(Field f) {
+        int mod = f.getModifiers();
+
+        return !Modifier.isStatic(mod) && !Modifier.isTransient(mod);
+    }
+
+    /**
      * @return {@code True} if enum.
      */
     boolean isEnum() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/627134b2/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java 
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
index 8cb4b38..62a9d26 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
@@ -1883,6 +1883,17 @@ public class BinaryUtils {
     }
 
     /**
+     * Create qualified field name.
+     *
+     * @param cls Class.
+     * @param fieldName Field name.
+     * @return Qualified field name.
+     */
+    public static String qualifiedFieldName(Class cls, String fieldName) {
+        return cls.getName() + "." + fieldName;
+    }
+
+    /**
      * Enum type.
      */
     private static class EnumType {

http://git-wip-us.apache.org/repos/asf/ignite/blob/627134b2/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
index fcd511b..ac9771f 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
@@ -54,6 +54,7 @@ import java.util.concurrent.ConcurrentSkipListSet;
 import junit.framework.Assert;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.binary.BinaryCollectionFactory;
+import org.apache.ignite.binary.BinaryField;
 import org.apache.ignite.binary.BinaryIdMapper;
 import org.apache.ignite.binary.BinaryMapFactory;
 import org.apache.ignite.binary.BinaryObject;
@@ -63,6 +64,7 @@ import org.apache.ignite.binary.BinaryRawReader;
 import org.apache.ignite.binary.BinaryRawWriter;
 import org.apache.ignite.binary.BinaryReader;
 import org.apache.ignite.binary.BinarySerializer;
+import org.apache.ignite.binary.BinaryType;
 import org.apache.ignite.binary.BinaryTypeConfiguration;
 import org.apache.ignite.binary.BinaryWriter;
 import org.apache.ignite.binary.Binarylizable;
@@ -2315,6 +2317,63 @@ public class BinaryMarshallerSelfTest extends 
GridCommonAbstractTest {
     }
 
     /**
+     * Test duplicate fields.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDuplicateFields() throws Exception {
+        BinaryMarshaller marsh = binaryMarshaller();
+
+        DuplicateFieldsB obj = new DuplicateFieldsB(1, 2);
+
+        BinaryObjectImpl objBin = marshal(obj, marsh);
+
+        String fieldName = "x";
+        String fieldNameA = DuplicateFieldsA.class.getName() + "." + fieldName;
+        String fieldNameB = DuplicateFieldsB.class.getName() + "." + fieldName;
+
+        // Check "hasField".
+        assert !objBin.hasField(fieldName);
+        assert objBin.hasField(fieldNameA);
+        assert objBin.hasField(fieldNameB);
+
+        // Check direct field access.
+        assertNull(objBin.field(fieldName));
+        assertEquals(1, objBin.field(fieldNameA));
+        assertEquals(2, objBin.field(fieldNameB));
+
+        // Check metadata.
+        BinaryType type = objBin.type();
+
+        Collection<String> fieldNames = type.fieldNames();
+
+        assertEquals(2, fieldNames.size());
+
+        assert !fieldNames.contains(fieldName);
+        assert fieldNames.contains(fieldNameA);
+        assert fieldNames.contains(fieldNameB);
+
+        // Check field access through type.
+        BinaryField field = type.field(fieldName);
+        BinaryField fieldA = type.field(fieldNameA);
+        BinaryField fieldB = type.field(fieldNameB);
+
+        assert !field.exists(objBin);
+        assert fieldA.exists(objBin);
+        assert fieldB.exists(objBin);
+
+        assertNull(field.value(objBin));
+        assertEquals(1, fieldA.value(objBin));
+        assertEquals(2, fieldB.value(objBin));
+
+        // Check object deserialization.
+        DuplicateFieldsB deserialized = objBin.deserialize();
+
+        assertEquals(obj.xA(), deserialized.xA());
+        assertEquals(obj.xB(), deserialized.xB());
+    }
+
+    /**
      *
      */
     private static interface SomeItf {
@@ -4120,6 +4179,57 @@ public class BinaryMarshallerSelfTest extends 
GridCommonAbstractTest {
     }
 
     /**
+     * Class B for duplicate fields test.
+     */
+    private static class DuplicateFieldsA {
+        /** Field. */
+        int x;
+
+        /**
+         * Constructor.
+         *
+         * @param x Field.
+         */
+        protected DuplicateFieldsA(int x) {
+            this.x = x;
+        }
+
+        /**
+         * @return A's field.
+         */
+        public int xA() {
+            return x;
+        }
+    }
+
+    /**
+     * Class B for duplicate fields test.
+     */
+    private static class DuplicateFieldsB extends DuplicateFieldsA {
+        /** Field. */
+        int x;
+
+        /**
+         * Constructor.
+         *
+         * @param xA Field for parent class.
+         * @param xB Field for current class.
+         */
+        public DuplicateFieldsB(int xA, int xB) {
+            super(xA);
+
+            this.x = xB;
+        }
+
+        /**
+         * @return B's field.
+         */
+        public int xB() {
+            return x;
+        }
+    }
+
+    /**
      *
      */
     private static class DecimalReflective {

http://git-wip-us.apache.org/repos/asf/ignite/blob/627134b2/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
 
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
index 1f5d6d1..b145a90 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.testsuites;
 
 import junit.framework.TestSuite;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
 import 
org.apache.ignite.internal.processors.cache.BinarySerializationQuerySelfTest;
 import 
org.apache.ignite.internal.processors.cache.BinarySerializationQueryWithReflectiveSerializerSelfTest;
 import 
org.apache.ignite.internal.processors.cache.CacheLocalQueryMetricsSelfTest;
@@ -32,7 +33,6 @@ import 
org.apache.ignite.internal.processors.cache.GridCacheQueryIndexingDisable
 import 
org.apache.ignite.internal.processors.cache.GridCacheQueryInternalKeysSelfTest;
 import 
org.apache.ignite.internal.processors.cache.GridCacheQuerySerializationSelfTest;
 import 
org.apache.ignite.internal.processors.cache.GridCacheReduceQueryMultithreadedSelfTest;
-import 
org.apache.ignite.internal.processors.cache.IgniteBinaryObjectFieldsQuerySelfTest;
 import 
org.apache.ignite.internal.processors.cache.IgniteCacheBinaryObjectsScanSelfTest;
 import 
org.apache.ignite.internal.processors.cache.IgniteCacheCollocatedQuerySelfTest;
 import 
org.apache.ignite.internal.processors.cache.IgniteCacheDuplicateEntityConfigurationSelfTest;
@@ -101,7 +101,6 @@ import 
org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest;
 import 
org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest;
 import org.apache.ignite.internal.processors.query.h2.sql.GridQueryParsingTest;
 import 
org.apache.ignite.internal.processors.query.h2.sql.H2CompareBigQueryTest;
-import org.apache.ignite.internal.binary.BinaryMarshaller;
 import 
org.apache.ignite.spi.communication.tcp.GridOrderedMessageCancelSelfTest;
 import org.apache.ignite.testframework.config.GridTestProperties;
 

Reply via email to