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 <[email protected]> Authored: Sun Aug 14 18:18:40 2016 +0300 Committer: vozerov-gridgain <[email protected]> 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);
