IGNITE-5444: Collections.singletonList is not properly serialized by binary marshaller. This closes #2217.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/038a90a9 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/038a90a9 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/038a90a9 Branch: refs/heads/ignite-5578-locJoin Commit: 038a90a98e3d6dce4514d55710955789ea30d2cb Parents: c0c6c2f Author: Andrey V. Mashenkov <andrey.mashen...@gmail.com> Authored: Fri Jul 14 16:41:41 2017 +0300 Committer: Andrey V. Mashenkov <andrey.mashen...@gmail.com> Committed: Fri Jul 14 16:41:41 2017 +0300 ---------------------------------------------------------------------- .../ignite/internal/binary/BinaryContext.java | 1 + .../ignite/internal/binary/BinaryUtils.java | 19 +++++-- .../internal/binary/GridBinaryMarshaller.java | 3 ++ .../processors/cache/CacheObjectUtils.java | 4 +- .../binary/CacheObjectBinaryProcessorImpl.java | 3 +- .../platform/utils/PlatformUtils.java | 3 +- .../ignite/internal/util/IgniteUtils.java | 11 ++++ .../internal/util/MutableSingletonList.java | 53 ++++++++++++++++++++ .../BinaryObjectBuilderAdditionalSelfTest.java | 2 +- .../GridCacheBinaryObjectsAbstractSelfTest.java | 28 +++++++++++ 10 files changed, 120 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/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 fa051f5..eaac7b8 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 @@ -275,6 +275,7 @@ public class BinaryContext { colTypes.put(ArrayList.class, GridBinaryMarshaller.ARR_LIST); colTypes.put(LinkedList.class, GridBinaryMarshaller.LINKED_LIST); + colTypes.put(BinaryUtils.SINGLETON_LIST_CLS, GridBinaryMarshaller.SINGLETON_LIST); colTypes.put(HashSet.class, GridBinaryMarshaller.HASH_SET); colTypes.put(LinkedHashSet.class, GridBinaryMarshaller.LINKED_HASH_SET); http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/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 969f3e1..74d1730 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 @@ -32,6 +32,7 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -59,6 +60,7 @@ import org.apache.ignite.binary.BinaryType; import org.apache.ignite.binary.Binarylizable; import org.apache.ignite.internal.binary.builder.BinaryLazyValue; import org.apache.ignite.internal.binary.streams.BinaryInputStream; +import org.apache.ignite.internal.util.MutableSingletonList; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiTuple; @@ -92,6 +94,9 @@ public class BinaryUtils { /** Binary classes. */ private static final Collection<Class<?>> BINARY_CLS = new HashSet<>(); + /** Class for SingletonList obtained at runtime. */ + public static final Class<? extends Collection> SINGLETON_LIST_CLS = Collections.singletonList(null).getClass(); + /** Flag: user type. */ public static final short FLAG_USR_TYP = 0x0001; @@ -697,7 +702,8 @@ public class BinaryUtils { (!wrapTrees() && cls == TreeSet.class) || cls == ConcurrentSkipListSet.class || cls == ArrayList.class || - cls == LinkedList.class; + cls == LinkedList.class || + cls == SINGLETON_LIST_CLS; } /** @@ -738,6 +744,8 @@ public class BinaryUtils { return new ArrayList<>(((Collection)col).size()); else if (cls == LinkedList.class) return new LinkedList<>(); + else if (cls == SINGLETON_LIST_CLS) + return new MutableSingletonList<>(); return null; } @@ -1132,7 +1140,7 @@ public class BinaryUtils { * @return {@code True} if this is a special collection class. */ public static boolean isSpecialCollection(Class cls) { - return ArrayList.class.equals(cls) || LinkedList.class.equals(cls) || + return ArrayList.class.equals(cls) || LinkedList.class.equals(cls) || SINGLETON_LIST_CLS.equals(cls) || HashSet.class.equals(cls) || LinkedHashSet.class.equals(cls); } @@ -2023,6 +2031,11 @@ public class BinaryUtils { break; + case GridBinaryMarshaller.SINGLETON_LIST: + col = new MutableSingletonList<>(); + + break; + case GridBinaryMarshaller.HASH_SET: col = U.newHashSet(size); @@ -2053,7 +2066,7 @@ public class BinaryUtils { for (int i = 0; i < size; i++) col.add(deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize)); - return col; + return colType == GridBinaryMarshaller.SINGLETON_LIST ? U.convertToSingletonList(col) : col; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/main/java/org/apache/ignite/internal/binary/GridBinaryMarshaller.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/GridBinaryMarshaller.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/GridBinaryMarshaller.java index 291c638..d6c8abd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/GridBinaryMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/GridBinaryMarshaller.java @@ -184,6 +184,9 @@ public class GridBinaryMarshaller { public static final byte LINKED_HASH_SET = 4; /** */ + public static final byte SINGLETON_LIST = 5; + + /** */ public static final byte HASH_MAP = 1; /** */ http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java index f9c76df..5afa751 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java @@ -18,7 +18,9 @@ package org.apache.ignite.internal.processors.cache; import org.apache.ignite.internal.binary.BinaryUtils; +import org.apache.ignite.internal.util.MutableSingletonList; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.U; import java.util.ArrayList; import java.util.Collection; @@ -66,7 +68,7 @@ public class CacheObjectUtils { for (Object obj : col) col0.add(unwrapBinary(ctx, obj, keepBinary, cpy)); - return col0; + return (col0 instanceof MutableSingletonList) ? U.convertToSingletonList(col0) : col0; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java index 0065e41..c0f3515 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java @@ -66,6 +66,7 @@ import org.apache.ignite.internal.processors.cache.KeyCacheObject; import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessorImpl; import org.apache.ignite.internal.util.GridUnsafe; import org.apache.ignite.internal.util.IgniteUtils; +import org.apache.ignite.internal.util.MutableSingletonList; import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.lang.GridMapEntry; import org.apache.ignite.internal.util.tostring.GridToStringExclude; @@ -349,7 +350,7 @@ public class CacheObjectBinaryProcessorImpl extends IgniteCacheObjectProcessorIm for (Object item : col) pCol.add(marshalToBinary(item)); - return pCol; + return (pCol instanceof MutableSingletonList) ? U.convertToSingletonList(pCol) : pCol; } } http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java index 654a2a1..dbd65ed 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformUtils.java @@ -58,6 +58,7 @@ import org.apache.ignite.internal.processors.platform.memory.PlatformInputStream import org.apache.ignite.internal.processors.platform.memory.PlatformMemory; import org.apache.ignite.internal.processors.platform.memory.PlatformMemoryUtils; import org.apache.ignite.internal.processors.platform.memory.PlatformOutputStream; +import org.apache.ignite.internal.util.MutableSingletonList; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; @@ -941,7 +942,7 @@ public class PlatformUtils { for (Object obj : col) col0.add(unwrapBinary(obj)); - return col0; + return (col0 instanceof MutableSingletonList) ? U.convertToSingletonList(col0) : col0; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java index 6a3be55..54ffe41 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java @@ -9343,6 +9343,17 @@ public abstract class IgniteUtils { } /** + * @param col non-null collection with one element + * @return a SingletonList containing the element in the original collection + */ + public static <T> Collection<T> convertToSingletonList(Collection<T> col) { + if (col.size() != 1) { + throw new IllegalArgumentException("Unexpected collection size for singleton list, expecting 1 but was: " + col.size()); + } + return Collections.singletonList(col.iterator().next()); + } + + /** * Returns comparator that sorts remote node addresses. If remote node resides on the same host, then put * loopback addresses first, last otherwise. * http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/main/java/org/apache/ignite/internal/util/MutableSingletonList.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/MutableSingletonList.java b/modules/core/src/main/java/org/apache/ignite/internal/util/MutableSingletonList.java new file mode 100644 index 0000000..87b96de --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/MutableSingletonList.java @@ -0,0 +1,53 @@ +/* + * 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.util; + + +import java.util.AbstractList; + +/** + * List that can contain maximum of one element. Does not allow null element to be added. + */ +public class MutableSingletonList<E> extends AbstractList<E> { + + /** The only element of collection. */ + private E element; + + /** {@inheritDoc} */ + @Override public E get(int index) { + if (index != 0 || element == null) + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size()); + + return element; + } + + /** {@inheritDoc} */ + @Override public void add(int index, E element) { + if (element == null) + throw new IllegalArgumentException("Cannot add null element to list"); + else if (index != 0) + throw new IllegalStateException("Element already added to singleton list"); + else + this.element = element; + } + + /** {@inheritDoc} */ + @Override public int size() { + return element == null ? 0 : 1; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java index 145dbb4..82ff383 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryObjectBuilderAdditionalSelfTest.java @@ -1511,7 +1511,7 @@ public class BinaryObjectBuilderAdditionalSelfTest extends GridCommonAbstractTes assert MAP.equals(binaryObj.type().fieldTypeName("singletonMap")); - assert OBJ.equals(binaryObj.type().fieldTypeName("asList")); + assert COL.equals(binaryObj.type().fieldTypeName("asList")); assert OBJ.equals(binaryObj.type().fieldTypeName("asSet")); assert OBJ.equals(binaryObj.type().fieldTypeName("asMap")); assert OBJ.equals(binaryObj.type().fieldTypeName("asListHint")); http://git-wip-us.apache.org/repos/asf/ignite/blob/038a90a9/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java index 5dace92..b98615c 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java @@ -21,6 +21,7 @@ import java.math.BigDecimal; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -424,6 +425,33 @@ public abstract class GridCacheBinaryObjectsAbstractSelfTest extends GridCommonA /** * @throws Exception If failed. */ + public void testSingletonList() throws Exception { + IgniteCache<Integer, Collection<TestObject>> c = jcache(0); + + c.put(0, Collections.singletonList(new TestObject(123))); + + Collection<TestObject> cFromCache = c.get(0); + + assertEquals(1, cFromCache.size()); + assertEquals(123, cFromCache.iterator().next().val); + + IgniteCache<Integer, Collection<BinaryObject>> kpc = keepBinaryCache(); + + Collection<?> cBinary = kpc.get(0); + + assertEquals(1, cBinary.size()); + + Object bObj = cBinary.iterator().next(); + + assertTrue(bObj instanceof BinaryObject); + assertEquals(Collections.singletonList(null).getClass(), cBinary.getClass()); + + assertEquals(Integer.valueOf(123), ((BinaryObject) bObj).field("val")); + } + + /** + * @throws Exception If failed. + */ public void testGetAsync() throws Exception { IgniteCache<Integer, TestObject> c = jcache(0);