/* * 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.client.cache.impl; import com.hazelcast.cache.HazelcastCacheManager; import com.hazelcast.client.impl.ClientMessageDecoder; import com.hazelcast.client.impl.HazelcastClientInstanceImpl; import com.hazelcast.client.impl.HazelcastClientProxy; import com.hazelcast.client.impl.protocol.ClientMessage; import com.hazelcast.client.impl.protocol.codec.CacheGetCodec; import com.hazelcast.client.spi.impl.ClientInvocation; import com.hazelcast.client.spi.impl.ClientInvocationFuture; import com.hazelcast.client.test.TestHazelcastFactory; import com.hazelcast.config.CacheConfig; import com.hazelcast.config.CacheConfiguration; import com.hazelcast.nio.serialization.Data; import com.hazelcast.spi.serialization.SerializationService; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.configuration.Factory; import javax.cache.integration.CacheLoader; import javax.cache.integration.CacheLoaderException; import javax.cache.spi.CachingProvider; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class CallbackAwareClientDelegatingFutureTest extends HazelcastTestSupport { private static final String CACHE_NAME = "MyCache"; private static final ClientMessageDecoder CACHE_GET_RESPONSE_DECODER = new ClientMessageDecoder() { @Override public <T> T decodeClientMessage(ClientMessage clientMessage) { return (T) CacheGetCodec.decodeResponse(clientMessage).response; } }; private TestHazelcastFactory factory; private HazelcastClientInstanceImpl client; @Before public void setup() { factory = new TestHazelcastFactory(); factory.newHazelcastInstance(); client = ((HazelcastClientProxy) factory.newHazelcastClient()).client; } @After public void tearDown() { factory.shutdownAll(); } @Test public void test_CallbackAwareClientDelegatingFuture_when_noTimeOut_noError() throws ExecutionException, InterruptedException { test_CallbackAwareClientDelegatingFuture(false, false); } @Test public void test_CallbackAwareClientDelegatingFuture_when_timeOut_but_noError() throws ExecutionException, InterruptedException { test_CallbackAwareClientDelegatingFuture(true, false); } @Test public void test_CallbackAwareClientDelegatingFuture_when_noTimeOut_but_error() throws ExecutionException, InterruptedException { test_CallbackAwareClientDelegatingFuture(false, true); } private void test_CallbackAwareClientDelegatingFuture(boolean timeout, boolean error) throws ExecutionException, InterruptedException { if (timeout && error) { throw new IllegalArgumentException( "Only one of the `timeout` and `error` parameters can be enabled at the same time!"); } int timeoutMillis = timeout ? 5000 : -1; createCache(timeoutMillis, error); ClientMessage getRequest = createGetRequest(1); ClientMessageDecoder decoder = CACHE_GET_RESPONSE_DECODER; ClientInvocation invocation = new ClientInvocation(client, getRequest, 0); ClientInvocationFuture invocationFuture = invocation.invoke(); final AtomicBoolean responseCalled = new AtomicBoolean(); final AtomicBoolean failureCalled = new AtomicBoolean(); OneShotExecutionCallback callback = new OneShotExecutionCallback() { @Override protected void onResponseInternal(Object response) { responseCalled.set(true); } @Override protected void onFailureInternal(Throwable t) { failureCalled.set(true); } }; CallbackAwareClientDelegatingFuture callbackAwareInvocationFuture = new CallbackAwareClientDelegatingFuture(invocationFuture, client.getSerializationService(), decoder, callback); if (timeoutMillis > 0) { try { callbackAwareInvocationFuture.get(timeoutMillis / 2, TimeUnit.MILLISECONDS); fail("Timeout expected!"); } catch (TimeoutException e) { // Timeout expected assertTrue(failureCalled.get()); assertFalse(responseCalled.get()); } } else { if (error) { try { callbackAwareInvocationFuture.get(); fail("CacheLoaderException expected!"); } catch (ExecutionException e) { // Exception expected assertTrue(e.getCause() instanceof CacheLoaderException); assertTrue(failureCalled.get()); assertFalse(responseCalled.get()); } } else { try { callbackAwareInvocationFuture.get(); assertTrue(responseCalled.get()); assertFalse(failureCalled.get()); } catch (CacheLoaderException e) { fail("CacheLoaderException not expected!"); } } } } private Cache createCache(int blockMilis, boolean throwError) { CachingProvider cachingProvider = HazelcastClientCachingProvider.createCachingProvider(client); CacheManager cacheManager = cachingProvider.getCacheManager(); CacheConfiguration cacheConfig = new CacheConfig() .setReadThrough(true) .setCacheLoaderFactory(new BlockableCacheLoaderFactory(blockMilis, throwError)); return cacheManager.createCache(CACHE_NAME, cacheConfig); } private ClientMessage createGetRequest(Integer key) { SerializationService serializationService = client.getSerializationService(); Data keyData = serializationService.toData(key); Data expiryPolicyData = serializationService.toData(null); String cacheNameWithPrefix = HazelcastCacheManager.CACHE_MANAGER_PREFIX + CACHE_NAME; return CacheGetCodec.encodeRequest(cacheNameWithPrefix, keyData, expiryPolicyData); } public static class BlockableCacheLoaderFactory implements Factory<BlockableCacheLoader> { private final int blockMillis; private final boolean throwError; public BlockableCacheLoaderFactory(int blockMillis, boolean throwError) { this.blockMillis = blockMillis; this.throwError = throwError; } @Override public BlockableCacheLoader create() { return new BlockableCacheLoader(blockMillis, throwError); } } public static class BlockableCacheLoader implements CacheLoader<Integer, String> { private final int blockMillis; private final boolean throwError; public BlockableCacheLoader(int blockMillis, boolean throwError) { this.blockMillis = blockMillis; this.throwError = throwError; } @Override public String load(Integer key) throws CacheLoaderException { if (throwError) { throw new CacheLoaderException(); } if (blockMillis > 0) { sleepMillis(blockMillis); } return "Value-" + key; } @Override public Map<Integer, String> loadAll(Iterable<? extends Integer> keys) throws CacheLoaderException { throw new UnsupportedOperationException(); } } }