You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2016/08/22 07:36:59 UTC

[03/32] ignite git commit: IGNITE-2852: Fixed TreeMap and TreeSet serialization.

IGNITE-2852: Fixed TreeMap and TreeSet serialization.


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

Branch: refs/heads/ignite-3220-1
Commit: 0b4ffdbcce63e5ce53572f71af967cff300d5670
Parents: 1139a9f
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Sun Aug 14 18:18:40 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Sun Aug 14 18:18:40 2016 +0300

----------------------------------------------------------------------
 .../apache/ignite/IgniteSystemProperties.java   |  10 +
 .../internal/binary/BinaryClassDescriptor.java  |  43 ++-
 .../ignite/internal/binary/BinaryContext.java   |  14 +-
 .../binary/BinaryMethodWriteReplacer.java       |  59 ++++
 .../ignite/internal/binary/BinaryTreeMap.java   |  96 ++++++
 .../binary/BinaryTreeMapWriteReplacer.java      |  34 ++
 .../ignite/internal/binary/BinaryTreeSet.java   |  93 +++++
 .../binary/BinaryTreeSetWriteReplacer.java      |  34 ++
 .../ignite/internal/binary/BinaryUtils.java     |  37 +-
 .../internal/binary/BinaryWriteReplacer.java    |  33 ++
 .../internal/binary/BinaryWriterExImpl.java     |  35 +-
 .../internal/binary/BinaryTreeSelfTest.java     | 341 +++++++++++++++++++
 .../IgniteBinaryObjectsTestSuite.java           |   2 +
 13 files changed, 790 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 0c22c9d..7c428a6 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -452,6 +452,16 @@ public final class IgniteSystemProperties {
     public static final String IGNITE_SERVICES_COMPATIBILITY_MODE = "IGNITE_SERVICES_COMPATIBILITY_MODE";
 
     /**
+     * When set to {@code true} tree-based data structures - {@code TreeMap} and {@code TreeSet} - will not be
+     * wrapped into special holders introduced to overcome serialization issue caused by missing {@code Comparable}
+     * interface on {@code BinaryObject}.
+     * <p>
+     * @deprecated Should be removed in Apache Ignite 2.0.
+     */
+    @Deprecated
+    public static final String IGNITE_BINARY_DONT_WRAP_TREE_STRUCTURES = "IGNITE_BINARY_DONT_WRAP_TREE_STRUCTURES";
+
+    /**
      * Enforces singleton.
      */
     private IgniteSystemProperties() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/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 d2d715b..083057d 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
@@ -89,8 +89,8 @@ public class BinaryClassDescriptor {
     /** */
     private final BinaryFieldAccessor[] fields;
 
-    /** */
-    private final Method writeReplaceMtd;
+    /** Write replacer. */
+    private final BinaryWriteReplacer writeReplacer;
 
     /** */
     private final Method readResolveMtd;
@@ -147,7 +147,7 @@ public class BinaryClassDescriptor {
 
         initialSerializer = serializer;
 
-        // If serializer is not defined at this point, then we have to user OptimizedMarshaller.
+        // If serializer is not defined at this point, then we have to use OptimizedMarshaller.
         useOptMarshaller = serializer == null;
 
         // Reset reflective serializer so that we rely on existing reflection-based serialization.
@@ -298,11 +298,8 @@ public class BinaryClassDescriptor {
 
                             schemaBuilder.addField(fieldId);
 
-                            if (metaDataEnabled) {
-                                assert stableFieldsMeta != null;
-
+                            if (metaDataEnabled)
                                 stableFieldsMeta.put(name, fieldInfo.mode().typeId());
-                            }
                         }
                     }
                 }
@@ -320,14 +317,24 @@ public class BinaryClassDescriptor {
                 throw new BinaryObjectException("Invalid mode: " + mode);
         }
 
+        BinaryWriteReplacer writeReplacer0 = BinaryUtils.writeReplacer(cls);
+
+        Method writeReplaceMthd;
+
         if (mode == BinaryWriteMode.BINARY || mode == BinaryWriteMode.OBJECT) {
             readResolveMtd = U.findNonPublicMethod(cls, "readResolve");
-            writeReplaceMtd = U.findNonPublicMethod(cls, "writeReplace");
+
+            writeReplaceMthd = U.findNonPublicMethod(cls, "writeReplace");
         }
         else {
             readResolveMtd = null;
-            writeReplaceMtd = null;
+            writeReplaceMthd = null;
         }
+
+        if (writeReplaceMthd != null && writeReplacer0 == null)
+            writeReplacer0 = new BinaryMethodWriteReplacer(writeReplaceMthd);
+
+        writeReplacer = writeReplacer0;
     }
 
     /**
@@ -469,10 +476,22 @@ public class BinaryClassDescriptor {
     }
 
     /**
-     * @return binaryWriteReplace() method
+     * @return {@code True} if write-replace should be performed for class.
      */
-    @Nullable Method getWriteReplaceMethod() {
-        return writeReplaceMtd;
+    public boolean isWriteReplace() {
+        return writeReplacer != null;
+    }
+
+    /**
+     * Perform write replace.
+     *
+     * @param obj Original object.
+     * @return Replaced object.
+     */
+    public Object writeReplace(Object obj) {
+        assert isWriteReplace();
+
+        return writeReplacer.replace(obj);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
index a603894..8517acf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
@@ -105,6 +105,8 @@ import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentMap;
 import java.util.jar.JarEntry;
@@ -182,6 +184,11 @@ public class BinaryContext {
         sysClss.add(GridClosureProcessor.C4V2.class.getName());
         sysClss.add(GridClosureProcessor.C4MLAV2.class.getName());
 
+        if (BinaryUtils.wrapTrees()) {
+            sysClss.add(TreeMap.class.getName());
+            sysClss.add(TreeSet.class.getName());
+        }
+
         BINARYLIZABLE_SYS_CLSS = Collections.unmodifiableSet(sysClss);
     }
 
@@ -332,11 +339,16 @@ public class BinaryContext {
      * @param cls Class.
      * @return {@code True} if must be deserialized.
      */
+    @SuppressWarnings("SimplifiableIfStatement")
     public boolean mustDeserialize(Class cls) {
         BinaryClassDescriptor desc = descByCls.get(cls);
 
-        if (desc == null)
+        if (desc == null) {
+            if (BinaryUtils.wrapTrees() && (cls == TreeMap.class || cls == TreeSet.class))
+                return false;
+
             return marshCtx.isSystemType(cls.getName()) || serializerForClass(cls) == null;
+        }
         else
             return desc.useOptimizedMarshaller();
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryMethodWriteReplacer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryMethodWriteReplacer.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryMethodWriteReplacer.java
new file mode 100644
index 0000000..783048e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryMethodWriteReplacer.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Write replacer based on method invocation.
+ */
+public class BinaryMethodWriteReplacer implements BinaryWriteReplacer {
+    /** Method. */
+    private final Method mthd;
+
+    /**
+     * Constructor.
+     *
+     * @param mthd Method.
+     */
+    public BinaryMethodWriteReplacer(Method mthd) {
+        assert mthd != null;
+
+        this.mthd = mthd;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object replace(Object target) {
+        try {
+            return mthd.invoke(target);
+        }
+        catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        catch (InvocationTargetException e) {
+            if (e.getTargetException() instanceof BinaryObjectException)
+                throw (BinaryObjectException)e.getTargetException();
+
+            throw new BinaryObjectException("Failed to execute writeReplace() method on " + target, e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMap.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMap.java
new file mode 100644
index 0000000..6a7cf9b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMap.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+
+import java.io.ObjectStreamException;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Binary {@link TreeMap} wrapper.
+ */
+public class BinaryTreeMap implements Binarylizable {
+    /** Original map. */
+    private TreeMap map;
+
+    /**
+     * Default constructor.
+     */
+    public BinaryTreeMap() {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param map Original map.
+     */
+    public BinaryTreeMap(TreeMap map) {
+        this.map = map;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        BinaryRawWriter rawWriter = writer.rawWriter();
+
+        rawWriter.writeObject(map.comparator());
+
+        int size = map.size();
+
+        rawWriter.writeInt(size);
+
+        for (Map.Entry<Object, Object> entry : ((TreeMap<Object, Object>)map).entrySet()) {
+            rawWriter.writeObject(entry.getKey());
+            rawWriter.writeObject(entry.getValue());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        BinaryRawReader rawReader = reader.rawReader();
+
+        Comparator comp =  rawReader.readObject();
+
+        map = comp == null ? new TreeMap() : new TreeMap(comp);
+
+        int size = rawReader.readInt();
+
+        for (int i = 0; i < size; i++)
+            map.put(rawReader.readObject(), rawReader.readObject());
+    }
+
+    /**
+     * Reconstructs object on unmarshalling.
+     *
+     * @return Reconstructed object.
+     * @throws ObjectStreamException Thrown in case of unmarshalling error.
+     */
+    protected Object readResolve() throws ObjectStreamException {
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMapWriteReplacer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMapWriteReplacer.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMapWriteReplacer.java
new file mode 100644
index 0000000..049db8e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeMapWriteReplacer.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.TreeMap;
+
+/**
+ * Binary {@link TreeMap} write replacer.
+ */
+public class BinaryTreeMapWriteReplacer implements BinaryWriteReplacer {
+    /** {@inheritDoc} */
+    @Nullable @Override public Object replace(Object target) {
+        assert target instanceof TreeMap;
+
+        return new BinaryTreeMap((TreeMap)target);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSet.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSet.java
new file mode 100644
index 0000000..2b01528
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSet.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+
+import java.io.ObjectStreamException;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+/**
+ * Binary {@link TreeSet} wrapper.
+ */
+public class BinaryTreeSet implements Binarylizable {
+    /** Original set. */
+    private TreeSet set;
+
+    /**
+     * Default constructor.
+     */
+    public BinaryTreeSet() {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param set Original set.
+     */
+    public BinaryTreeSet(TreeSet set) {
+        this.set = set;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public void writeBinary(BinaryWriter writer) throws BinaryObjectException {
+        BinaryRawWriter rawWriter = writer.rawWriter();
+
+        rawWriter.writeObject(set.comparator());
+
+        int size = set.size();
+
+        rawWriter.writeInt(size);
+
+        for (Object val : set)
+            rawWriter.writeObject(val);
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public void readBinary(BinaryReader reader) throws BinaryObjectException {
+        BinaryRawReader rawReader = reader.rawReader();
+
+        Comparator comp =  rawReader.readObject();
+
+        set = comp == null ? new TreeSet() : new TreeSet(comp);
+
+        int size = rawReader.readInt();
+
+        for (int i = 0; i < size; i++)
+            set.add(rawReader.readObject());
+    }
+
+    /**
+     * Reconstructs object on unmarshalling.
+     *
+     * @return Reconstructed object.
+     * @throws ObjectStreamException Thrown in case of unmarshalling error.
+     */
+    protected Object readResolve() throws ObjectStreamException {
+        return set;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSetWriteReplacer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSetWriteReplacer.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSetWriteReplacer.java
new file mode 100644
index 0000000..4350777
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTreeSetWriteReplacer.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.TreeSet;
+
+/**
+ * Binary {@link TreeSet} write replacer.
+ */
+public class BinaryTreeSetWriteReplacer implements BinaryWriteReplacer {
+    /** {@inheritDoc} */
+    @Nullable @Override public Object replace(Object target) {
+        assert target instanceof TreeSet;
+
+        return new BinaryTreeSet((TreeSet)target);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/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 76e5b31..b5834a5 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
@@ -82,6 +82,9 @@ public class BinaryUtils {
     public static final boolean USE_STR_SERIALIZATION_VER_2 = IgniteSystemProperties.getBoolean(
         IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2, false);
 
+    /** Map from class to associated write replacer. */
+    public static final Map<Class, BinaryWriteReplacer> CLS_TO_WRITE_REPLACER = new HashMap<>();
+
     /** {@code true} if serialized value of this type cannot contain references to objects. */
     private static final boolean[] PLAIN_TYPE_FLAG = new boolean[102];
 
@@ -118,6 +121,10 @@ public class BinaryUtils {
     /** Field ID length. */
     public static final int FIELD_ID_LEN = 4;
 
+    /** Whether to skip TreeMap/TreeSet wrapping. */
+    public static final boolean WRAP_TREES =
+        !IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_BINARY_DONT_WRAP_TREE_STRUCTURES);
+
     /** Field type names. */
     private static final String[] FIELD_TYPE_NAMES;
 
@@ -244,6 +251,11 @@ public class BinaryUtils {
         FIELD_TYPE_NAMES[GridBinaryMarshaller.TIMESTAMP_ARR] = "Timestamp[]";
         FIELD_TYPE_NAMES[GridBinaryMarshaller.OBJ_ARR] = "Object[]";
         FIELD_TYPE_NAMES[GridBinaryMarshaller.ENUM_ARR] = "Enum[]";
+
+        if (wrapTrees()) {
+            CLS_TO_WRITE_REPLACER.put(TreeMap.class, new BinaryTreeMapWriteReplacer());
+            CLS_TO_WRITE_REPLACER.put(TreeSet.class, new BinaryTreeSetWriteReplacer());
+        }
     }
 
     /**
@@ -584,6 +596,13 @@ public class BinaryUtils {
     }
 
     /**
+     * @return Whether tree structures should be wrapped.
+     */
+    public static boolean wrapTrees() {
+        return WRAP_TREES;
+    }
+
+    /**
      * @param map Map to check.
      * @return {@code True} if this map type is supported.
      */
@@ -592,7 +611,7 @@ public class BinaryUtils {
 
         return cls == HashMap.class ||
             cls == LinkedHashMap.class ||
-            cls == TreeMap.class ||
+            (!wrapTrees() && cls == TreeMap.class) ||
             cls == ConcurrentHashMap8.class ||
             cls == ConcurrentHashMap.class;
     }
@@ -611,7 +630,7 @@ public class BinaryUtils {
             return U.newHashMap(((Map)map).size());
         else if (cls == LinkedHashMap.class)
             return U.newLinkedHashMap(((Map)map).size());
-        else if (cls == TreeMap.class)
+        else if (!wrapTrees() && cls == TreeMap.class)
             return new TreeMap<>(((TreeMap<Object, Object>)map).comparator());
         else if (cls == ConcurrentHashMap8.class)
             return new ConcurrentHashMap8<>(U.capacity(((Map)map).size()));
@@ -650,7 +669,7 @@ public class BinaryUtils {
 
         return cls == HashSet.class ||
             cls == LinkedHashSet.class ||
-            cls == TreeSet.class ||
+            (!wrapTrees() && cls == TreeSet.class) ||
             cls == ConcurrentSkipListSet.class ||
             cls == ArrayList.class ||
             cls == LinkedList.class;
@@ -686,7 +705,7 @@ public class BinaryUtils {
             return U.newHashSet(((Collection)col).size());
         else if (cls == LinkedHashSet.class)
             return U.newLinkedHashSet(((Collection)col).size());
-        else if (cls == TreeSet.class)
+        else if (!wrapTrees() && cls == TreeSet.class)
             return new TreeSet<>(((TreeSet<Object>)col).comparator());
         else if (cls == ConcurrentSkipListSet.class)
             return new ConcurrentSkipListSet<>(((ConcurrentSkipListSet<Object>)col).comparator());
@@ -2214,6 +2233,16 @@ public class BinaryUtils {
     }
 
     /**
+     * Get predefined write-replacer associated with class.
+     *
+     * @param cls Class.
+     * @return Write replacer.
+     */
+    public static BinaryWriteReplacer writeReplacer(Class cls) {
+        return cls != null ? CLS_TO_WRITE_REPLACER.get(cls) : null;
+    }
+
+    /**
      * Enum type.
      */
     private static class EnumType {

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriteReplacer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriteReplacer.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriteReplacer.java
new file mode 100644
index 0000000..9376c37
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriteReplacer.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Interface to perform write replace.
+ */
+public interface BinaryWriteReplacer {
+    /**
+     * Perform replace.
+     *
+     * @param target Original object.
+     * @return Replaced object.
+     */
+    @Nullable public Object replace(Object target);
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java
index 30710f4..9450482 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java
@@ -17,10 +17,18 @@
 
 package org.apache.ignite.internal.binary;
 
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.internal.binary.streams.BinaryHeapOutputStream;
+import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.jetbrains.annotations.Nullable;
+
 import java.io.IOException;
 import java.io.ObjectOutput;
 import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -29,14 +37,6 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.Map;
 import java.util.UUID;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.binary.BinaryObjectException;
-import org.apache.ignite.binary.BinaryRawWriter;
-import org.apache.ignite.binary.BinaryWriter;
-import org.apache.ignite.internal.binary.streams.BinaryHeapOutputStream;
-import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
-import org.apache.ignite.internal.util.typedef.internal.A;
-import org.jetbrains.annotations.Nullable;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -170,21 +170,8 @@ public class BinaryWriterExImpl implements BinaryWriter, BinaryRawWriterEx, Obje
             return;
         }
 
-        if (enableReplace && desc.getWriteReplaceMethod() != null) {
-            Object replacedObj;
-
-            try {
-                replacedObj = desc.getWriteReplaceMethod().invoke(obj);
-            }
-            catch (IllegalAccessException e) {
-                throw new RuntimeException(e);
-            }
-            catch (InvocationTargetException e) {
-                if (e.getTargetException() instanceof BinaryObjectException)
-                    throw (BinaryObjectException)e.getTargetException();
-
-                throw new BinaryObjectException("Failed to execute writeReplace() method on " + obj, e);
-            }
+        if (enableReplace && desc.isWriteReplace()) {
+            Object replacedObj = desc.writeReplace(obj);
 
             if (replacedObj == null) {
                 out.writeByte(GridBinaryMarshaller.NULL);

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryTreeSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryTreeSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryTreeSelfTest.java
new file mode 100644
index 0000000..d57b34d
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryTreeSelfTest.java
@@ -0,0 +1,341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.binary;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Tests for TreeMap and TreeSet structures.
+ */
+public class BinaryTreeSelfTest extends GridCommonAbstractTest {
+    /** Data structure size. */
+    private static final int SIZE = 100;
+
+    /** Node name: server. */
+    private static final String NODE_SRV = "srv";
+
+    /** Node name: client. */
+    private static final String NODE_CLI = "cli";
+
+    /** Key to be used for cache operations. */
+    private static final int KEY = 1;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        Ignition.start(configuration(NODE_SRV, false));
+        Ignition.start(configuration(NODE_CLI, true));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        super.afterTestsStopped();
+
+        G.stop(NODE_CLI, true);
+        G.stop(NODE_SRV, true);
+    }
+
+    /**
+     * Test {@code TreeMap} data structure.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeMapRegularNoComparator() throws Exception {
+        checkTreeMap(false, false);
+    }
+
+    /**
+     * Test {@code TreeMap} data structure with comparator.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeMapRegularComparator() throws Exception {
+        checkTreeMap(false, true);
+    }
+
+    /**
+     * Test {@code TreeMap} data structure with binary mode.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeMapBinaryNoComparator() throws Exception {
+        checkTreeMap(true, false);
+    }
+
+    /**
+     * Test {@code TreeMap} data structure with binary mode and comparator.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeMapBinaryComparator() throws Exception {
+        checkTreeMap(true, true);
+    }
+
+    /**
+     * Check {@code TreeMap} data structure.
+     *
+     * @param useBinary Whether to go through binary mode.
+     * @param useComparator Whether comparator should be used.
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings("unchecked")
+    private void checkTreeMap(boolean useBinary, boolean useComparator) throws Exception {
+        // Populate map.
+        TreeMap<TestKey, Integer> map;
+
+        if (useComparator) {
+            map = new TreeMap<>(new TestKeyComparator());
+
+            for (int i = 0; i < SIZE; i++)
+                map.put(key(false, i), i);
+        }
+        else {
+            map = new TreeMap<>();
+
+            for (int i = 0; i < SIZE; i++)
+                map.put(key(true, i), i);
+        }
+
+        // Put and get value from cache.
+        cache().put(KEY, map);
+
+        TreeMap<TestKey, Integer> resMap;
+
+        if (useBinary) {
+            BinaryObject resMapBinary = (BinaryObject)cache().withKeepBinary().get(KEY);
+
+            resMap = resMapBinary.deserialize();
+        }
+        else
+            resMap = (TreeMap<TestKey, Integer>)cache().get(KEY);
+
+        // Ensure content is correct.
+        if (useComparator)
+            assert resMap.comparator() instanceof TestKeyComparator;
+        else
+            assertNull(resMap.comparator());
+
+        assertEquals(map, resMap);
+    }
+
+    /**
+     * Test {@code TreeSet} data structure.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeSetRegularNoComparator() throws Exception {
+        checkTreeSet(false, false);
+    }
+
+    /**
+     * Test {@code TreeSet} data structure with comparator.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeSetRegularComparator() throws Exception {
+        checkTreeSet(false, true);
+    }
+
+    /**
+     * Test {@code TreeSet} data structure with binary mode.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeSetBinaryNoComparator() throws Exception {
+        checkTreeSet(true, false);
+    }
+
+    /**
+     * Test {@code TreeSet} data structure with binary mode and comparator.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTreeSetBinaryComparator() throws Exception {
+        checkTreeSet(true, true);
+    }
+
+    /**
+     * Check {@code TreeSet} data structure.
+     *
+     * @param useBinary Whether to go through binary mode.
+     * @param useComparator Whether comparator should be used.
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings("unchecked")
+    private void checkTreeSet(boolean useBinary, boolean useComparator) throws Exception {
+        // Populate set.
+        TreeSet<TestKey> set;
+
+        if (useComparator) {
+            set = new TreeSet<>(new TestKeyComparator());
+
+            for (int i = 0; i < SIZE; i++)
+                set.add(key(false, i));
+        }
+        else {
+            set = new TreeSet<>();
+
+            for (int i = 0; i < SIZE; i++)
+                set.add(key(true, i));
+        }
+
+        // Put and get value from cache.
+        cache().put(KEY, set);
+
+        TreeSet<TestKey> resSet;
+
+        if (useBinary) {
+            BinaryObject resMapBinary = (BinaryObject)cache().withKeepBinary().get(KEY);
+
+            resSet = resMapBinary.deserialize();
+        }
+        else
+            resSet = (TreeSet<TestKey>)cache().get(KEY);
+
+        // Ensure content is correct.
+        if (useComparator)
+            assert resSet.comparator() instanceof TestKeyComparator;
+        else
+            assertNull(resSet.comparator());
+
+        assertEquals(set, resSet);
+    }
+
+    /**
+     * @return Cache.
+     */
+    private IgniteCache cache() {
+        return G.ignite(NODE_CLI).cache(null);
+    }
+
+    /**
+     * Get key.
+     *
+     * @param comparable Whether it should be comparable.
+     * @param id ID.
+     * @return Key.
+     */
+    private static TestKey key(boolean comparable, int id) {
+        return comparable ? new TestKeyComparable(id) : new TestKey(id);
+    }
+
+    /**
+     * Create configuration.
+     *
+     * @param name Node name.
+     * @param client Client mode flag.
+     * @return Configuration.
+     */
+    private static IgniteConfiguration configuration(String name, boolean client) {
+        IgniteConfiguration cfg = new IgniteConfiguration();
+
+        cfg.setGridName(name);
+
+        cfg.setClientMode(client);
+
+        cfg.setLocalHost("127.0.0.1");
+        cfg.setPeerClassLoadingEnabled(false);
+
+        TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
+
+        TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder();
+
+        ipFinder.setAddresses(Collections.singletonList("127.0.0.1:47500"));
+
+        discoSpi.setIpFinder(ipFinder);
+
+        cfg.setDiscoverySpi(discoSpi);
+
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setCacheMode(CacheMode.PARTITIONED);
+
+        cfg.setCacheConfiguration(ccfg);
+
+        return cfg;
+    }
+
+    /**
+     * Test key.
+     */
+    private static class TestKey {
+        /** ID. */
+        private int id;
+
+        /**
+         * Constructor.
+         *
+         * @param id ID.
+         */
+        public TestKey(int id) {
+            this.id = id;
+        }
+
+        /**
+         * @return ID.
+         */
+        public int id() {
+            return id;
+        }
+    }
+
+    /**
+     * Test key implementing comparable interface.
+     */
+    private static class TestKeyComparable extends TestKey implements Comparable<TestKey> {
+        /**
+         * Constructor.
+         *
+         * @param id ID.
+         */
+        private TestKeyComparable(int id) {
+            super(id);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int compareTo(@NotNull TestKey o) {
+            return id() - o.id();
+        }
+    }
+
+    /**
+     * Test key comparator.
+     */
+    private static class TestKeyComparator implements Comparator<TestKey> {
+        /** {@inheritDoc} */
+        @Override public int compare(TestKey o1, TestKey o2) {
+            return o1.id() - o2.id();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b4ffdbc/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
index cedf9a7..dc0540d 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java
@@ -31,6 +31,7 @@ import org.apache.ignite.internal.binary.BinaryObjectBuilderAdditionalSelfTest;
 import org.apache.ignite.internal.binary.BinaryObjectBuilderDefaultMappersSelfTest;
 import org.apache.ignite.internal.binary.BinaryObjectBuilderSimpleNameLowerCaseMappersSelfTest;
 import org.apache.ignite.internal.binary.BinarySimpleNameTestPropertySelfTest;
+import org.apache.ignite.internal.binary.BinaryTreeSelfTest;
 import org.apache.ignite.internal.binary.GridBinaryAffinityKeySelfTest;
 import org.apache.ignite.internal.binary.GridBinaryMarshallerCtxDisabledSelfTest;
 import org.apache.ignite.internal.binary.GridBinaryWildcardsSelfTest;
@@ -85,6 +86,7 @@ public class IgniteBinaryObjectsTestSuite extends TestSuite {
         suite.addTestSuite(BinaryBasicIdMapperSelfTest.class);
         suite.addTestSuite(BinaryBasicNameMapperSelfTest.class);
 
+        suite.addTestSuite(BinaryTreeSelfTest.class);
         suite.addTestSuite(BinaryMarshallerSelfTest.class);
         suite.addTestSuite(BinaryConfigurationConsistencySelfTest.class);
         suite.addTestSuite(GridBinaryMarshallerCtxDisabledSelfTest.class);