/* * Copyright 2010-104 the original author or authors. * * 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 org.springframework.data.gemfire.cache; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; import edu.umd.cs.mtc.MultithreadedTestCase; import edu.umd.cs.mtc.TestFramework; import org.apache.geode.cache.CacheFactory; import org.apache.geode.cache.Region; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.cache.Cache; import org.springframework.data.gemfire.GemfireUtils; import org.springframework.data.gemfire.test.support.AbstractNativeCacheTests; /** * Integration Tests for {@link GemfireCache}. * * @author Costin Leau * @author John Blum * @author Oliver Gierke * @see org.junit.Test * @see edu.umd.cs.mtc.MultithreadedTestCase * @see edu.umd.cs.mtc.TestFramework * @see org.springframework.cache.Cache * @see org.apache.geode.cache.Region */ public class GemfireCacheIntegrationTests extends AbstractNativeCacheTests<Region<Object, Object>> { @Rule public ExpectedException exception = ExpectedException.none(); @Override protected Cache newCache(Region<Object, Object> nativeCache) { return new GemfireCache(nativeCache); } @Override protected Region<Object, Object> newNativeCache() throws Exception { Properties gemfireProperties = new Properties(); gemfireProperties.setProperty("name", GemfireCacheIntegrationTests.class.getName()); gemfireProperties.setProperty("mcast-port", "0"); gemfireProperties.setProperty("locators", ""); gemfireProperties.setProperty("log-level", "warning"); org.apache.geode.cache.Cache cache = GemfireUtils.getCache(); cache = (cache != null ? cache : new CacheFactory(gemfireProperties).create()); Region<Object, Object> region = cache.getRegion(CACHE_NAME); region = (region != null ? region : cache.createRegionFactory().create(CACHE_NAME)); return region; } /** * @see <a href="https://jira.spring.io/browse/SGF-317">Improve GemfireCache implementation to be able to build on Spring 4.1</a> */ @Test public void findsTypedValue() throws Exception { Cache cache = newCache(); cache.put("key", "value"); assertThat(cache.get("key", String.class)).isEqualTo("value"); } /** * @see <a href="https://jira.spring.io/browse/SGF-317">Improve GemfireCache implementation to be able to build on Spring 4.1</a> */ @Test public void skipTypeChecksIfTargetTypeIsNull() throws Exception { Cache cache = newCache(); cache.put("key", 1); assertThat(cache.get("key", (Class<?>) null)).isEqualTo(1); } /** * @see <a href="https://jira.spring.io/browse/SGF-317">Improve GemfireCache implementation to be able to build on Spring 4.1</a> */ @Test public void throwsIllegalStateExceptionIfTypedAccessDoesNotFindMatchingType() throws Exception { Cache cache = newCache(); cache.put("key", "value"); exception.expect(IllegalStateException.class); exception.expectMessage(Integer.class.getName()); exception.expectMessage(String.format("Cached value [value] is not an instance of type [%s]", Integer.class.getName())); cache.get("key", Integer.class); } @Test public void cacheGetWithValueLoaderFindsValue() throws Exception { GemfireCache cache = newCache(); cache.put("key", "value"); assertThat(cache.get("key", TestValueLoader.NULL_VALUE)).isEqualTo("value"); assertThat(TestValueLoader.NULL_VALUE.wasCalled()).isFalse(); } @Test @SuppressWarnings("unchecked") public void cacheGetWithValueLoaderUsesValueLoaderReturnsValue() throws Exception { GemfireCache cache = newCache(); TestValueLoader<String> valueLoader = new TestValueLoader<String>("test"); assertThat(cache.get("key", valueLoader)).isEqualTo("test"); assertThat(valueLoader.wasCalled()).isTrue(); assertThat(((Region<Object, String>) cache.getNativeCache()).get("key")).isEqualTo("test"); } @Test @SuppressWarnings("unchecked") public void cacheGetWithValueLoaderUsesValueLoaderReturnsNull() throws Exception { GemfireCache cache = newCache(); assertThat(cache.get("key", TestValueLoader.NULL_VALUE)).isNull(); assertThat(TestValueLoader.NULL_VALUE.wasCalled()).isTrue(); assertThat(cache.getNativeCache().containsKey("key")).isFalse(); } @Test @SuppressWarnings("all") public void cacheGetWithValueLoaderUsesValueLoaderAndThrowsException() throws Exception { GemfireCache cache = newCache(); try { TestValueLoader<Exception> exceptionThrowingValueLoader = new TestValueLoader<Exception>( new IllegalStateException("test")); exception.expect(Cache.ValueRetrievalException.class); exception.expectCause(is(IllegalStateException.class)); cache.get("key", exceptionThrowingValueLoader); } finally { assertThat(cache.getNativeCache().containsKey("key")).isFalse(); } } @Test public void cacheGetWithValueLoaderIsThreadSafe() throws Throwable { TestFramework.runOnce(new CacheGetWithValueLoaderIsThreadSafe()); } @SuppressWarnings("unused") protected class CacheGetWithValueLoaderIsThreadSafe extends MultithreadedTestCase { private GemfireCache cache; private TestValueLoader<String> cacheLoader; @Override public void initialize(){ super.initialize(); cache = newCacheHandlesCheckedException(); cacheLoader = new TestValueLoader<String>("test") { @Override public String call() throws Exception { waitForTick(2); return super.call(); } }; } <T extends Cache> T newCacheHandlesCheckedException() { try { return newCache(); } catch (Exception e) { throw new RuntimeException("Failed to create Cache", e); } } public void thread1() { assertTick(0); Thread.currentThread().setName("Cache Loader Thread"); String value = cache.get("key", cacheLoader); assertTick(2); assertThat(value).isEqualTo("test"); assertThat(cacheLoader.wasCalled()).isTrue(); } public void thread2() { waitForTick(1); Thread.currentThread().setName("Cache Reader Thread"); TestValueLoader<String> illegalCacheLoader = new TestValueLoader<String>("illegal"); String value = cache.get("key", illegalCacheLoader); assertTick(2); assertThat(value).isEqualTo("test"); assertThat(illegalCacheLoader.wasCalled()).isFalse(); } } protected static class TestValueLoader<T> implements Callable<T> { protected static final TestValueLoader<Object> NULL_VALUE = new TestValueLoader<Object>(); private AtomicBoolean called = new AtomicBoolean(false); private final T value; public TestValueLoader() { this(null); } public TestValueLoader(T value) { this.value = value; } protected boolean wasCalled() { return called.compareAndSet(true, false); } @Override public T call() throws Exception { called.set(true); if (value instanceof Exception) { throw (Exception) value; } return value; } } }