blob: 1d047eeb000f3eee185f6dc226020aaf3a585ded [file] [log] [blame]
/*
* 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.client.ClientCache;
import org.apache.ignite.client.Config;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.configuration.BinaryConfiguration;
import org.apache.ignite.configuration.ClientConfiguration;
import org.apache.ignite.internal.binary.BinaryMarshallerSelfTest.TestClass1;
import org.apache.ignite.internal.binary.mutabletest.GridBinaryTestClasses.TestObjectContainer;
import org.apache.ignite.internal.processors.cache.CacheEnumOperationsAbstractTest.TestEnum;
import org.apache.ignite.internal.processors.platform.utils.PlatformUtils;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.testframework.GridTestUtils;
import org.junit.Test;
import static org.apache.ignite.internal.processors.cache.CacheEnumOperationsAbstractTest.TestEnum.VAL1;
import static org.apache.ignite.internal.processors.cache.CacheEnumOperationsAbstractTest.TestEnum.VAL2;
import static org.apache.ignite.internal.processors.cache.CacheEnumOperationsAbstractTest.TestEnum.VAL3;
import static org.junit.Assert.assertArrayEquals;
/** */
public class BinaryArraySelfTest extends AbstractBinaryArraysTest {
/** */
private static Ignite server;
/** */
private static Ignite client;
/** */
private static CacheAdapter<Object, Object> srvCache;
/** */
private static CacheAdapter<Object, Object> cliCache;
/** */
private static final Function<Object, Object> TO_TEST_CLS = arr -> arr instanceof TestClass1[][]
? new TestClass2(null, (TestClass1[][])arr)
: new TestClass2((TestClass1[])arr, null);
/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
super.beforeTestsStarted();
server = startGrid(0);
client = startClientGrid(1);
srvCache = new IgniteCacheAdapter<>(server.createCache(DEFAULT_CACHE_NAME));
cliCache = new IgniteCacheAdapter<>(client.getOrCreateCache(DEFAULT_CACHE_NAME));
}
/** */
@Test
public void testArrayKey() {
doTestKeys(srvCache, arr -> arr);
doTestKeys(cliCache, arr -> arr);
try (IgniteClient thinClient = thinClient()) {
// Using other cache because
// 1. Removed entry store in `GridCacheAdapter#map`
// 2. Bytes representation of multidimensional BinaryArray from thin client and Ignite node differ.
// 2a. Thin client reads array from byte stream and preserve pointer equality (look at #dataToTest() -> arr6).
// 2b. Client node invoke `CacheObjectBinaryProcessorImpl#marshallToBinary` before storing
// which breaks link equality of array
// 3. During invocation of `put` node inserts key representation from previous invocation of methods from client node.
// 4. This lead to `ClientCache#containsKey` can't find key because
// it invokes search based equality on `byte[]` key representaion.
//
// This doesn't happen in `useBinaryArrays=false` becuase Ignite node obtain `Object[]`
// from byte stream and invoke marshallToBinary for it which also breaks pointer equality
// therefore during serialization handle will NOT be used.
// In `useBinaryArrays=true` node read `BinaryArray` from stream which mean no need to marshall to binary
// therefore link equality preserved which mean during serialization handle will be used.
doTestKeys(
new ClientCacheAdapter<>(thinClient.getOrCreateCache(DEFAULT_CACHE_NAME + (useBinaryArrays ? "2" : ""))),
arr -> arr
);
}
}
/** */
@Test
public void testArrayFieldInKey() {
doTestKeys(srvCache, TO_TEST_CLS);
doTestKeys(cliCache, TO_TEST_CLS);
try (IgniteClient thinClient = thinClient()) {
doTestKeys(new ClientCacheAdapter<>(thinClient.getOrCreateCache(DEFAULT_CACHE_NAME)), TO_TEST_CLS);
}
}
/** */
@Test
public void testArrayValue() {
doTestValue(srvCache, arr -> arr, false, false);
doTestValue(cliCache, arr -> arr, false, false);
doTestValue(srvCache, arr -> arr, true, false);
doTestValue(cliCache, arr -> arr, true, false);
try (IgniteClient thinClient = thinClient()) {
ClientCacheAdapter<Object, Object> c =
new ClientCacheAdapter<>(thinClient.getOrCreateCache(DEFAULT_CACHE_NAME));
doTestValue(c, arr -> arr, false, false);
doTestValue(c, arr -> arr, true, false);
}
}
/** */
@Test
public void testArrayFieldInValue() {
doTestValue(srvCache, TO_TEST_CLS, false, true);
doTestValue(cliCache, TO_TEST_CLS, false, true);
doTestValue(srvCache, TO_TEST_CLS, true, true);
doTestValue(cliCache, TO_TEST_CLS, true, true);
try (IgniteClient thinClient = thinClient()) {
ClientCacheAdapter<Object, Object> c =
new ClientCacheAdapter<>(thinClient.getOrCreateCache(DEFAULT_CACHE_NAME));
doTestValue(c, TO_TEST_CLS, false, true);
doTestValue(c, TO_TEST_CLS, true, true);
}
}
/** */
@Test
public void testArraySerDe() {
checkArraySerDe(arr -> {
Object obj = server.binary().toBinary(arr);
return useBinaryArrays
? ((BinaryObject)obj).deserialize()
: PlatformUtils.unwrapBinariesInArray((Object[])obj);
}, false);
}
/** */
@Test
public void testArrayFieldSerDe() {
checkArraySerDe(arr -> {
BinaryObject bObj = server.binary().toBinary(new TestObjectContainer(arr));
return ((TestObjectContainer)bObj.deserialize()).foo;
}, true);
}
/** */
@Test
public void testBinaryModeArray() {
putInBinaryGetRegular(srvCache);
putInBinaryGetRegular(cliCache);
try (IgniteClient thinClient = thinClient()) {
putInBinaryGetRegular(new ClientCacheAdapter<>(thinClient.getOrCreateCache(DEFAULT_CACHE_NAME)));
}
}
/** */
@Test
public void testPrimitivesArrays() {
doTestBoxedPrimitivesArrays(srvCache);
doTestBoxedPrimitivesArrays(cliCache);
doTestPrimitivesArrays(srvCache);
doTestPrimitivesArrays(cliCache);
try (IgniteClient thinClient = thinClient()) {
ClientCacheAdapter<Object, Object> c = new
ClientCacheAdapter<>(thinClient.getOrCreateCache(DEFAULT_CACHE_NAME));
doTestBoxedPrimitivesArrays(c);
doTestPrimitivesArrays(c);
}
}
/** */
@Test
public void testSimpleBinaryArrayFieldSerDe() {
TestClass1 src = new TestClass1();
BinaryMarshallerSelfTest.SimpleObject sobj = GridTestUtils.getFieldValue(src, "obj");
GridTestUtils.setFieldValue(sobj, "objArr", new Object[] {"string", 1L, null});
BinaryObject obj = server.binary().toBinary(src);
BinaryObject simpleObj = obj.field("obj");
Object objArr = simpleObj.field("objArr");
assertEquals(useBinaryArrays ? BinaryArray.class : Object[].class, objArr.getClass());
Object deser = obj.deserialize();
assertEquals(TestClass1.class, deser.getClass());
sobj = GridTestUtils.getFieldValue(deser, "obj");
Object[] arr = GridTestUtils.getFieldValue(sobj, "objArr");
assertNotNull(arr);
assertEquals(3, arr.length);
assertEquals("string", arr[0]);
assertEquals(1L, arr[1]);
assertNull(arr[2]);
}
/** */
@Test
public void testArrayOfCollectionSerDe() {
List<TestClass1> l1 = new ArrayList<>(F.asList(new TestClass1(), new TestClass1()));
List<TestClass1> l2 = new ArrayList<>(F.asList(new TestClass1(), new TestClass1()));
List<TestClass1> l3 = new ArrayList<>(F.asList(new TestClass1(), new TestClass1()));
List[] arr = new List[] { l1, l2, l3 };
Object res = server.binary().toBinary(arr);
Object[] res0;
if (useBinaryArrays) {
assertTrue(res instanceof BinaryArray);
res0 = ((BinaryArray)res).deserialize();
}
else {
assertTrue(res instanceof Object[]);
res0 = PlatformUtils.unwrapBinariesInArray((Object[])res);
}
assertEquals(arr.length, res0.length);
for (int i = 0; i < arr.length; i++)
assertEqualsCollections(arr[i], (Collection<?>)res0[i]);
}
/** */
@Test
public void testArrayOfBinariesSerDe() {
BinaryObject[] arr = new BinaryObject[] {
server.binary().toBinary(new TestClass1()),
server.binary().toBinary(new TestClass1())
};
Object obj = server.binary().toBinary(arr);
Object deser;
if (useBinaryArrays) {
assertTrue(obj instanceof BinaryArray);
deser = ((BinaryArray)obj).deserialize();
}
else {
assertTrue(obj instanceof Object[]);
deser = PlatformUtils.unwrapBinariesInArray((Object[])obj);
}
assertEquals(Object[].class, deser.getClass());
Object[] res = ((Object[])deser);
assertEquals(2, res.length);
assertTrue(res[0] instanceof TestClass1);
assertTrue(res[1] instanceof TestClass1);
}
/** */
private void doTestKeys(CacheAdapter<Object, Object> c, Function<Object, Object> wrap) {
List<?> keys = dataToTest();
for (int i = 0; i < keys.size(); i++) {
Object key = wrap.apply(keys.get(i));
assertFalse(c.containsKey(key));
c.put(key, i);
assertTrue(c.containsKey(key));
assertEquals(i, c.get(key));
assertTrue(c.replace(key, i, i + 1));
assertEquals(i + 1, c.get(key));
assertTrue(c.remove(key));
}
}
/** */
private void doTestValue(
CacheAdapter<Object, Object> c,
Function<Object, Object> wrap,
boolean keepBinary,
boolean alwaysSameType
) {
AtomicInteger cntr = new AtomicInteger();
checkArraySerDe(arr -> {
c.put(cntr.getAndIncrement(), wrap.apply(arr));
Object obj;
if (keepBinary) {
obj = c.withKeepBinary().get(cntr.get() - 1);
if (obj instanceof BinaryObject)
obj = ((BinaryObject)obj).deserialize();
else
obj = PlatformUtils.unwrapBinariesInArray((Object[])obj);
}
else
obj = c.get(cntr.get() - 1);
if (obj instanceof Object[])
return obj;
TestClass2 cached = (TestClass2)obj;
return cached.arr != null ? cached.arr : cached.arr2;
}, alwaysSameType);
List<?> vals = dataToTest();
for (int i = 0; i < vals.size(); i++)
assertTrue(c.replace(i, wrap.apply(vals.get(i)), wrap.apply(vals.get((i + 1) % vals.size()))));
for (int i = 0; i < cntr.get(); i++)
assertTrue(c.remove(i));
}
/** */
private List<?> dataToTest() {
TestClass1[][] arr5 = new TestClass1[3][2];
TestClass1[][] arr6 = new TestClass1[2][2];
arr5[0] = new TestClass1[] {new TestClass1()};
arr5[1] = new TestClass1[] {new TestClass1()};
// arr6[0] == arr6[1]
arr6[0] = new TestClass1[] {new TestClass1()};
arr6[1] = arr6[0];
return F.asList(
new TestClass1[0],
new TestClass1[1][2],
new TestClass1[] {new TestClass1(), new TestClass1()},
arr5,
arr6
);
}
/** */
private void putRegularGetInBinary(IgniteCache<Object, Object> c) {
List<?> vals = dataToTest();
for (Object val : vals) {
c.put(1, val);
Object obj = c.withKeepBinary().get(1);
assertEquals(useBinaryArrays ? BinaryArray.class : Object[].class, obj.getClass());
if (useBinaryArrays)
assertEquals(val.getClass(), ((BinaryObject)obj).deserialize().getClass());
assertTrue(c.remove(1));
}
}
/** */
private void putInBinaryGetRegular(CacheAdapter<Object, Object> c) {
Runnable checker = () -> {
Object[] arr = (Object[])c.get(1);
assertTrue(arr[0] instanceof TestClass1);
assertTrue(arr[1] instanceof TestClass1);
assertTrue(c.withKeepBinary().remove(1));
assertNull(c.withKeepBinary().get(1));
assertNull(c.get(1));
};
{
BinaryObject[] src = new BinaryObject[] {
server.binary().toBinary(new TestClass1()),
server.binary().toBinary(new TestClass1())
};
c.withKeepBinary().put(1, src);
checker.run();
}
{
Object src = server.binary().toBinary(
new Object[] {
server.binary().toBinary(new TestClass1()),
server.binary().toBinary(new TestClass1())
}
);
assertEquals(useBinaryArrays ? BinaryArray.class : Object[].class, src.getClass());
c.withKeepBinary().put(1, src);
checker.run();
}
}
/** */
private void doTestPrimitivesArrays(CacheAdapter<Object, Object> c) {
Object[] data = new Object[] {
new byte[] {1, 2, 3},
new short[] {1, 2, 3},
new int[] {1, 2, 3},
new long[] {1L, 2L, 3L},
new float[] {1f, 2f, 3f},
new double[] {1d, 2d, 3d},
new char[] {'a', 'b', 'c'},
new boolean[] {true, false}
};
for (Object item : data) {
c.put(1, item);
Object item0 = c.get(1);
assertTrue(c.replace(1, item, item));
assertTrue(c.remove(1));
assertEquals(item.getClass(), item0.getClass());
assertEqualsArraysAware(item, item0);
c.put(item, 1);
assertTrue(c.containsKey(item));
assertEquals(1, c.get(item));
assertTrue(c.replace(item, 1, 2));
assertTrue(c.remove(item));
}
}
/** */
private void doTestBoxedPrimitivesArrays(CacheAdapter<Object, Object> c) {
Object[] data = new Object[] {
new Byte[] {1, 2, 3},
new Short[] {1, 2, 3},
new Integer[] {1, 2, 3},
new Long[] {1L, 2L, 3L},
new Float[] {1f, 2f, 3f},
new Double[] {1d, 2d, 3d},
new Character[] {'a', 'b', 'c'},
new Boolean[] {true, false},
new TestEnum[] {VAL1, VAL2, VAL3}
};
for (Object item : data) {
c.put(1, item);
Object item0 = c.get(1);
assertTrue(c.replace(1, item, item));
assertTrue(c.remove(1));
if (useBinaryArrays)
assertEquals(item.getClass(), item0.getClass());
assertTrue(Arrays.equals((Object[])item, (Object[])item0));
c.put(item, 1);
assertTrue(c.containsKey(item));
assertEquals(1, c.get(item));
assertTrue(c.replace(item, 1, 2));
assertTrue(c.remove(item));
}
}
/** */
public void checkArraySerDe(Function<Object[], Object> serde, boolean sameArr) {
for (Object[] arr : (List<Object[]>)dataToTest()) {
Object deser = serde.apply(arr);
assertEquals((useBinaryArrays || sameArr) ? arr.getClass() : Object[].class, deser.getClass());
assertEquals(arr.length, ((Object[])deser).length);
assertArrayEquals(arr, ((Object[])deser));
if (arr instanceof TestClass1[][] && arr.length == 2 && sameArr) {
Object[] val = (Object[])deser;
// See dataToTest -> dataToTest -> arr6[0] == arr6[1]
// Check the sanity of test data.
assertSame(((TestClass1[][])arr)[0], ((TestClass1[][])arr)[1]);
assertSame(val[0], val[1]);
}
}
}
/** */
private IgniteClient thinClient() {
return Ignition.startClient(new ClientConfiguration()
.setAddresses(Config.SERVER)
.setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(true))
);
}
/** */
public static class TestClass2 {
/** */
final TestClass1[] arr;
/** */
final TestClass1[][] arr2;
/** */
public TestClass2(TestClass1[] arr, TestClass1[][] arr2) {
this.arr = arr;
this.arr2 = arr2;
}
}
/** */
public interface CacheAdapter<K, V> {
/** */
public V get(K key);
/** */
public void put(K key, V val);
/** */
public boolean replace(K key, V oldVal, V newVal);
/** */
public boolean remove(K key);
/** */
public boolean containsKey(K key);
/** */
public <K1, V1> CacheAdapter<K1, V1> withKeepBinary();
}
/** */
private static class IgniteCacheAdapter<K, V> implements CacheAdapter<K, V> {
/** */
private final IgniteCache<K, V> c;
/** */
public IgniteCacheAdapter(IgniteCache<K, V> c) {
this.c = c;
}
/** {@inheritDoc} */
@Override public V get(K key) {
return c.get(key);
}
/** {@inheritDoc} */
@Override public void put(K key, V val) {
c.put(key, val);
}
/** {@inheritDoc} */
@Override public boolean replace(K key, V oldVal, V newVal) {
return c.replace(key, oldVal, newVal);
}
/** {@inheritDoc} */
@Override public boolean remove(K key) {
return c.remove(key);
}
/** {@inheritDoc} */
@Override public boolean containsKey(K key) {
return c.containsKey(key);
}
/** {@inheritDoc} */
@Override public <K1, V1> CacheAdapter<K1, V1> withKeepBinary() {
return new IgniteCacheAdapter<>(c.withKeepBinary());
}
}
/** */
private static class ClientCacheAdapter<K, V> implements CacheAdapter<K, V> {
/** */
private final ClientCache<K, V> c;
/** */
public ClientCacheAdapter(ClientCache<K, V> c) {
this.c = c;
}
/** {@inheritDoc} */
@Override public V get(K key) {
return c.get(key);
}
/** {@inheritDoc} */
@Override public void put(K key, V val) {
c.put(key, val);
}
/** {@inheritDoc} */
@Override public boolean replace(K key, V oldVal, V newVal) {
return c.replace(key, oldVal, newVal);
}
/** {@inheritDoc} */
@Override public boolean remove(K key) {
return c.remove(key);
}
/** {@inheritDoc} */
@Override public boolean containsKey(K key) {
return c.containsKey(key);
}
/** {@inheritDoc} */
@Override public <K1, V1> CacheAdapter<K1, V1> withKeepBinary() {
return new ClientCacheAdapter<>(c.withKeepBinary());
}
}
}