/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.internal.nearcache; import com.hazelcast.config.NearCacheConfig; import com.hazelcast.config.SerializationConfig; import com.hazelcast.internal.adapter.DataStructureAdapter; import com.hazelcast.nio.serialization.ClassDefinition; import com.hazelcast.nio.serialization.ClassDefinitionBuilder; import com.hazelcast.nio.serialization.HazelcastSerializationException; import com.hazelcast.nio.serialization.Portable; import com.hazelcast.nio.serialization.PortableFactory; import com.hazelcast.nio.serialization.PortableReader; import com.hazelcast.nio.serialization.PortableWriter; import com.hazelcast.test.HazelcastTestSupport; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static com.hazelcast.internal.nearcache.NearCacheTestUtils.assertNearCacheSizeEventually; import static com.hazelcast.internal.nearcache.NearCacheTestUtils.isCacheOnUpdate; import static java.lang.String.format; import static org.junit.Assert.assertTrue; /** * Contains the logic code for unified Near Cache serialization count tests. * * @param <NK> key type of the tested Near Cache * @param <NV> value type of the tested Near Cache */ public abstract class AbstractNearCacheSerializationCountTest<NK, NV> extends HazelcastTestSupport { /** * The default name used for the data structures which have a Near Cache. */ protected static final String DEFAULT_NEAR_CACHE_NAME = "defaultNearCache"; private static final AtomicInteger KEY_SERIALIZE_COUNT = new AtomicInteger(); private static final AtomicInteger KEY_DESERIALIZE_COUNT = new AtomicInteger(); private static final AtomicInteger VALUE_SERIALIZE_COUNT = new AtomicInteger(); private static final AtomicInteger VALUE_DESERIALIZE_COUNT = new AtomicInteger(); private static final AtomicReference<List<String>> KEY_SERIALIZE_STACKTRACE = new AtomicReference<List<String>>(); private static final AtomicReference<List<String>> KEY_DESERIALIZE_STACKTRACE = new AtomicReference<List<String>>(); private static final AtomicReference<List<String>> VALUE_SERIALIZE_STACKTRACE = new AtomicReference<List<String>>(); private static final AtomicReference<List<String>> VALUE_DESERIALIZE_STACKTRACE = new AtomicReference<List<String>>(); /** * An array with the expected number of key serializations for a {@link DataStructureAdapter#put} * and two {@link DataStructureAdapter#get(Object)} calls for the given {@link NearCacheConfig}. */ protected int[] expectedKeySerializationCounts; /** * An array with the expected number of key deserializations for a {@link DataStructureAdapter#put} * and two {@link DataStructureAdapter#get(Object)} calls for the given {@link NearCacheConfig}. */ protected int[] expectedKeyDeserializationCounts; /** * An array with the expected number of value serializations for a {@link DataStructureAdapter#put} * and two {@link DataStructureAdapter#get(Object)} calls for the given {@link NearCacheConfig}. */ protected int[] expectedValueSerializationCounts; /** * An array with the expected number of value deserializations for a {@link DataStructureAdapter#put} * and two {@link DataStructureAdapter#get(Object)} calls for the given {@link NearCacheConfig}. */ protected int[] expectedValueDeserializationCounts; /** * The {@link NearCacheConfig} used by the Near Cache tests. * * Needs to be set by the implementations of this class in their {@link org.junit.Before} methods. */ protected NearCacheConfig nearCacheConfig; /** * Adds the test configuration to the given {@link NearCacheSerializationCountConfigBuilder}. * * @param configBuilder the {@link NearCacheSerializationCountConfigBuilder} */ protected abstract void addConfiguration(NearCacheSerializationCountConfigBuilder configBuilder); /** * Creates the {@link NearCacheTestContext} used by the Near Cache tests. * * @param <K> key type of the created {@link DataStructureAdapter} * @param <V> value type of the created {@link DataStructureAdapter} * @return a {@link NearCacheTestContext} used by the Near Cache tests */ protected abstract <K, V> NearCacheTestContext<K, V, NK, NV> createContext(); @Before public final void initStates() { KEY_SERIALIZE_COUNT.set(0); KEY_DESERIALIZE_COUNT.set(0); VALUE_SERIALIZE_COUNT.set(0); VALUE_DESERIALIZE_COUNT.set(0); KEY_SERIALIZE_STACKTRACE.set(new CopyOnWriteArrayList<String>()); KEY_DESERIALIZE_STACKTRACE.set(new CopyOnWriteArrayList<String>()); VALUE_SERIALIZE_STACKTRACE.set(new CopyOnWriteArrayList<String>()); VALUE_DESERIALIZE_STACKTRACE.set(new CopyOnWriteArrayList<String>()); } /** * Tests the serialization and deserialization counts on a {@link DataStructureAdapter} with a {@link NearCache}. */ @Test public void testSerializationCounts() { NearCacheTestContext<KeySerializationCountingData, ValueSerializationCountingData, NK, NV> context = createContext(); KeySerializationCountingData key = new KeySerializationCountingData(); ValueSerializationCountingData value = new ValueSerializationCountingData(); context.nearCacheAdapter.put(key, value); if (isCacheOnUpdate(nearCacheConfig)) { assertNearCacheSizeEventually(context, 1); } assertAndReset("put()", 0); context.nearCacheAdapter.get(key); assertAndReset("first get()", 1); context.nearCacheAdapter.get(key); assertAndReset("second get()", 2); } private void assertAndReset(String label, int index) { int expectedKeySerializeCount = expectedKeySerializationCounts[index]; int expectedKeyDeserializeCount = expectedKeyDeserializationCounts[index]; int expectedValueSerializeCount = expectedValueSerializationCounts[index]; int expectedValueDeserializeCount = expectedValueDeserializationCounts[index]; int actualKeySerializeCount = KEY_SERIALIZE_COUNT.getAndSet(0); int actualKeyDeserializeCount = KEY_DESERIALIZE_COUNT.getAndSet(0); int actualValueSerializeCount = VALUE_SERIALIZE_COUNT.getAndSet(0); int actualValueDeserializeCount = VALUE_DESERIALIZE_COUNT.getAndSet(0); List<String> keySerializeStackTrace = KEY_SERIALIZE_STACKTRACE.getAndSet(new CopyOnWriteArrayList<String>()); List<String> keyDeserializeStackTrace = KEY_DESERIALIZE_STACKTRACE.getAndSet(new CopyOnWriteArrayList<String>()); List<String> valueSerializeStackTrace = VALUE_SERIALIZE_STACKTRACE.getAndSet(new CopyOnWriteArrayList<String>()); List<String> valueDeserializeStackTrace = VALUE_DESERIALIZE_STACKTRACE.getAndSet(new CopyOnWriteArrayList<String>()); NearCacheSerializationCountConfigBuilder configBuilder = new NearCacheSerializationCountConfigBuilder(); configBuilder.append(expectedKeySerializationCounts); configBuilder.append(expectedKeyDeserializationCounts); configBuilder.append(expectedValueSerializationCounts); configBuilder.append(expectedValueDeserializationCounts); addConfiguration(configBuilder); assertTrue(format("key serializeCount on %s: expected %d, but was %d%n%s", label, expectedKeySerializeCount, actualKeySerializeCount, configBuilder.build(true, true, index, keySerializeStackTrace)), expectedKeySerializeCount == actualKeySerializeCount); assertTrue(format("key deserializeCount on %s: expected %d, but was %d%n%s", label, expectedKeyDeserializeCount, actualKeyDeserializeCount, configBuilder.build(true, false, index, keyDeserializeStackTrace)), expectedKeyDeserializeCount == actualKeyDeserializeCount); assertTrue(format("value serializeCount on %s: expected %d, but was %d%n%s", label, expectedValueSerializeCount, actualValueSerializeCount, configBuilder.build(false, true, index, valueSerializeStackTrace)), expectedValueSerializeCount == actualValueSerializeCount); assertTrue(format("value deserializeCount on %s: expected %d, but was %d%n%s", label, expectedValueDeserializeCount, actualValueDeserializeCount, configBuilder.build(false, false, index, valueDeserializeStackTrace)), expectedValueDeserializeCount == actualValueDeserializeCount); } /** * Creates an {@code int[]} for the expected (de)serialization count parameterization. * * @param put expected count for the put() (de)serialization * @param firstGet expected count for the first get() (de)serialization * @param secondGet expected count for the second get() (de)serialization * @return the {@code int[]} with the expected (de)serialization counts */ protected static int[] newInt(int put, int firstGet, int secondGet) { return new int[]{put, firstGet, secondGet}; } /** * Adds the serialization configuration for the used {@link Portable} domain object to the given {@link SerializationConfig}. * * @param serializationConfig the given {@link SerializationConfig} for the {@link DataStructureAdapter} */ protected static void prepareSerializationConfig(SerializationConfig serializationConfig) { ClassDefinition keyClassDefinition = new ClassDefinitionBuilder(KeySerializationCountingData.FACTORY_ID, KeySerializationCountingData.CLASS_ID).build(); serializationConfig.addClassDefinition(keyClassDefinition); ClassDefinition valueClassDefinition = new ClassDefinitionBuilder(ValueSerializationCountingData.FACTORY_ID, ValueSerializationCountingData.CLASS_ID).build(); serializationConfig.addClassDefinition(valueClassDefinition); serializationConfig.addPortableFactory(KeySerializationCountingData.FACTORY_ID, new PortableFactory() { @Override public Portable create(int classId) { return new KeySerializationCountingData(); } }); serializationConfig.addPortableFactory(ValueSerializationCountingData.FACTORY_ID, new PortableFactory() { @Override public Portable create(int classId) { return new ValueSerializationCountingData(); } }); } private static String getStackTrace(String message) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); new HazelcastSerializationException(message).printStackTrace(pw); return sw.toString(); } private static class KeySerializationCountingData implements Portable { private static int FACTORY_ID = 1; private static int CLASS_ID = 1; KeySerializationCountingData() { } @Override public int getFactoryId() { return FACTORY_ID; } @Override public int getClassId() { return CLASS_ID; } @Override public void writePortable(PortableWriter writer) throws IOException { KEY_SERIALIZE_COUNT.incrementAndGet(); KEY_SERIALIZE_STACKTRACE.get().add(getStackTrace("invoked key serialization")); } @Override public void readPortable(PortableReader reader) throws IOException { KEY_DESERIALIZE_COUNT.incrementAndGet(); KEY_DESERIALIZE_STACKTRACE.get().add(getStackTrace("invoked key deserialization")); } } private static class ValueSerializationCountingData implements Portable { private static int FACTORY_ID = 2; private static int CLASS_ID = 2; ValueSerializationCountingData() { } @Override public int getFactoryId() { return FACTORY_ID; } @Override public int getClassId() { return CLASS_ID; } @Override public void writePortable(PortableWriter writer) throws IOException { VALUE_SERIALIZE_COUNT.incrementAndGet(); VALUE_SERIALIZE_STACKTRACE.get().add(getStackTrace("invoked value serialization")); } @Override public void readPortable(PortableReader reader) throws IOException { VALUE_DESERIALIZE_COUNT.incrementAndGet(); VALUE_DESERIALIZE_STACKTRACE.get().add(getStackTrace("invoked value deserialization")); } } }