SGF-196 - Support adding CacheListeners, CacheLoaders and CacheWriters, along with other mutable Region attributes to an existing Region.

This commit is contained in:
John Blum
2015-04-07 19:27:15 -07:00
parent b9881dfad9
commit b42ca233f0
9 changed files with 959 additions and 35 deletions

View File

@@ -0,0 +1,209 @@
/*
* Copyright 2010-2013 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Test;
import com.gemstone.gemfire.cache.AttributesMutator;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheWriter;
import com.gemstone.gemfire.cache.CustomExpiry;
import com.gemstone.gemfire.cache.EvictionAttributesMutator;
import com.gemstone.gemfire.cache.ExpirationAttributes;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventQueue;
import com.gemstone.gemfire.cache.wan.GatewaySender;
/**
* The LookupRegionFactoryBeanTest class is a test suite of test cases testing the contract and functionality
* of the LookupRegionFactoryBean class.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.LookupRegionFactoryBean
* @see com.gemstone.gemfire.cache.AttributesMutator
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.EvictionAttributesMutator
* @see com.gemstone.gemfire.cache.Region
* @since 1.7.0
*/
public class LookupRegionFactoryBeanTest {
protected AsyncEventQueue mockAsyncEventQueue(final String id) {
AsyncEventQueue mockQueue = mock(AsyncEventQueue.class, String.format("MockAsyncEventQueue.%1$s", id));
when(mockQueue.getId()).thenReturn(id);
return mockQueue;
}
protected GatewaySender mockGatewaySender(final String id) {
GatewaySender mockGatewaySender = mock(GatewaySender.class, String.format("MockGatewaySender.%1$s", id));
when(mockGatewaySender.getId()).thenReturn(id);
return mockGatewaySender;
}
@Test
@SuppressWarnings("unchecked")
public void testAfterPropertiesSet() throws Exception {
Cache mockCache = mock(Cache.class, "testAfterPropertiesSet.MockCache");
Region<Object, Object> mockRegion = mock(Region.class, "testAfterPropertiesSet.MockRegion");
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class,
"testAfterPropertiesSet.MockRegionAttributes");
EvictionAttributesMutator mockEvictionAttributesMutator = mock(EvictionAttributesMutator.class,
"testAfterPropertiesSet.EvictionAttributesMutator");
AttributesMutator<Object, Object> mockAttributesMutator = mock(AttributesMutator.class,
"testAfterPropertiesSet.MockAttributesMutator");
when(mockCache.getRegion(eq("Example"))).thenReturn(mockRegion);
when(mockRegion.getFullPath()).thenReturn("/Example");
when(mockRegion.getName()).thenReturn("Example");
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegionAttributes.getStatisticsEnabled()).thenReturn(true);
when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator);
when(mockAttributesMutator.getEvictionAttributesMutator()).thenReturn(mockEvictionAttributesMutator);
AsyncEventQueue mockAsyncEventQueueOne = mockAsyncEventQueue("AEQ1");
AsyncEventQueue mockAsyncEventQueueTwo = mockAsyncEventQueue("AEQ2");
CacheListener mockCacheListenerZero = mock(CacheListener.class, "testAfterPropertiesSet.MockCacheListener.0");
CacheListener mockCacheListenerOne = mock(CacheListener.class, "testAfterPropertiesSet.MockCacheListener.1");
CacheListener mockCacheListenerTwo = mock(CacheListener.class, "testAfterPropertiesSet.MockCacheListener.2");
CacheLoader mockCacheLoader = mock(CacheLoader.class, "testAfterPropertiesSet.MockCacheLoader");
CacheWriter mockCacheWriter = mock(CacheWriter.class, "testAfterPropertiesSet.MockCacheWriter");
CustomExpiry mockCustomExpiryTti = mock(CustomExpiry.class, "testAfterPropertiesSet.MockCustomExpiry.TTI");
CustomExpiry mockCustomExpiryTtl = mock(CustomExpiry.class, "testAfterPropertiesSet.MockCustomExpiry.TTL");
ExpirationAttributes mockExpirationAttributesEntryTti = mock(ExpirationAttributes.class,
"testAfterPropertiesSet.MockExpirationAttributes.Entry.TTI");
ExpirationAttributes mockExpirationAttributesEntryTtl = mock(ExpirationAttributes.class,
"testAfterPropertiesSet.MockExpirationAttributes.Entry.TTL");
ExpirationAttributes mockExpirationAttributesRegionTti = mock(ExpirationAttributes.class,
"testAfterPropertiesSet.MockExpirationAttributes.Region.TTI");
ExpirationAttributes mockExpirationAttributesRegionTtl = mock(ExpirationAttributes.class,
"testAfterPropertiesSet.MockExpirationAttributes.Region.TTL");
GatewaySender mockGatewaySender = mockGatewaySender("GWS1");
LookupRegionFactoryBean factoryBean = new LookupRegionFactoryBean();
factoryBean.setAsyncEventQueues(new AsyncEventQueue[] { mockAsyncEventQueueOne, mockAsyncEventQueueTwo });
factoryBean.setBeanName("Example");
factoryBean.setCache(mockCache);
factoryBean.setCacheLoader(mockCacheLoader);
factoryBean.setCacheWriter(mockCacheWriter);
factoryBean.setCloningEnabled(true);
factoryBean.setCustomEntryIdleTimeout(mockCustomExpiryTti);
factoryBean.setCustomEntryTimeToLive(mockCustomExpiryTtl);
factoryBean.setEntryIdleTimeout(mockExpirationAttributesEntryTti);
factoryBean.setEntryTimeToLive(mockExpirationAttributesEntryTtl);
factoryBean.setGatewaySenders(new GatewaySender[] { mockGatewaySender });
factoryBean.setEvictionMaximum(1000);
factoryBean.setRegionIdleTimeout(mockExpirationAttributesRegionTti);
factoryBean.setRegionTimeToLive(mockExpirationAttributesRegionTtl);
factoryBean.setStatisticsEnabled(true);
factoryBean.setCacheListeners(new CacheListener[] {
mockCacheListenerZero, mockCacheListenerOne, mockCacheListenerTwo
});
factoryBean.afterPropertiesSet();
verify(mockAttributesMutator, times(1)).addAsyncEventQueueId(eq("AEQ1"));
verify(mockAttributesMutator, times(1)).addAsyncEventQueueId(eq("AEQ2"));
verify(mockAttributesMutator, times(1)).addCacheListener(same(mockCacheListenerZero));
verify(mockAttributesMutator, times(1)).addCacheListener(same(mockCacheListenerOne));
verify(mockAttributesMutator, times(1)).addCacheListener(same(mockCacheListenerTwo));
verify(mockAttributesMutator, times(1)).setCacheLoader(same(mockCacheLoader));
verify(mockAttributesMutator, times(1)).setCacheWriter(same(mockCacheWriter));
verify(mockAttributesMutator, times(1)).setCloningEnabled(eq(true));
verify(mockAttributesMutator, times(1)).setCustomEntryIdleTimeout(same(mockCustomExpiryTti));
verify(mockAttributesMutator, times(1)).setCustomEntryTimeToLive(same(mockCustomExpiryTtl));
verify(mockAttributesMutator, times(1)).setEntryIdleTimeout(same(mockExpirationAttributesEntryTti));
verify(mockAttributesMutator, times(1)).setEntryTimeToLive(same(mockExpirationAttributesEntryTtl));
verify(mockAttributesMutator, times(1)).addGatewaySenderId(eq("GWS1"));
verify(mockEvictionAttributesMutator, times(1)).setMaximum(eq(1000));
verify(mockAttributesMutator, times(1)).setRegionIdleTimeout(same(mockExpirationAttributesRegionTti));
verify(mockAttributesMutator, times(1)).setRegionTimeToLive(same(mockExpirationAttributesRegionTtl));
}
@Test(expected = IllegalStateException.class)
@SuppressWarnings("unchecked")
public void testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified() throws Exception {
Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockCache");
Region<Object, Object> mockRegion = mock(Region.class, "testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockRegion");
RegionAttributes<Object, Object> mockRegionAttributes = mock(RegionAttributes.class,
"testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockRegionAttributes");
AttributesMutator mockAttributesMutator = mock(AttributesMutator.class,
"testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockAttributesMutator");
ExpirationAttributes mockExpirationAttributesEntryTtl = mock(ExpirationAttributes.class,
"testAfterPropertiesSetWhenRegionStatisticsDisabledAndExpirationSpecified.MockExpirationAttributes.Entry.TTL");
when(mockCache.getRegion(eq("Example"))).thenReturn(mockRegion);
when(mockRegion.getFullPath()).thenReturn("/Example");
when(mockRegion.getName()).thenReturn("Example");
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator);
when(mockRegionAttributes.getStatisticsEnabled()).thenReturn(false);
LookupRegionFactoryBean factoryBean = new LookupRegionFactoryBean();
factoryBean.setBeanName("Example");
factoryBean.setCache(mockCache);
factoryBean.setEntryTimeToLive(mockExpirationAttributesEntryTtl);
//factoryBean.setStatisticsEnabled(true);
assertTrue(factoryBean.isStatisticsEnabled());
try {
factoryBean.afterPropertiesSet();
}
catch (IllegalStateException expected) {
assertEquals("Statistics for Region '/Example' must be enabled to change Entry & Region TTL/TTI Expiration settings",
expected.getMessage());
throw expected;
}
finally {
verify(mockAttributesMutator, never()).setEntryTimeToLive(any(ExpirationAttributes.class));
}
}
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright 2010-2013 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheLoaderException;
import com.gemstone.gemfire.cache.CacheWriter;
import com.gemstone.gemfire.cache.CacheWriterException;
import com.gemstone.gemfire.cache.CustomExpiry;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.EntryEvent;
import com.gemstone.gemfire.cache.ExpirationAction;
import com.gemstone.gemfire.cache.ExpirationAttributes;
import com.gemstone.gemfire.cache.LoaderHelper;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionEvent;
import com.gemstone.gemfire.cache.asyncqueue.AsyncEvent;
import com.gemstone.gemfire.cache.asyncqueue.AsyncEventListener;
import com.gemstone.gemfire.cache.util.CacheListenerAdapter;
import com.gemstone.gemfire.management.internal.cli.util.spring.StringUtils;
/**
* The LookupRegionMutationIntegrationTest class is a test suite of test cases testing the contract and integrated
* functionality between natively-defined GemFire Cache Regions and SDG's Region lookup functionality combined with
* Region attribute(s) mutation.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.data.gemfire.LookupRegionFactoryBean
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.7.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class LookupRegionMutationIntegrationTest {
@Resource(name = "Example")
private Region<?, ?> example;
protected void assertGemFireComponent(Object gemfireComponent, String expectedName) {
assertNotNull("The GemFire component must not be null!", gemfireComponent);
assertEquals(expectedName, gemfireComponent.toString());
}
protected void assertExpirationAttributes(ExpirationAttributes expirationAttributes,
String description, int expectedTimeout, ExpirationAction expectedAction) {
assertNotNull(String.format("ExpirationAttributes for '%1$s' must not be null!", description), expirationAttributes);
assertEquals(expectedAction, expirationAttributes.getAction());
assertEquals(expectedTimeout, expirationAttributes.getTimeout());
}
protected void assertCacheListeners(CacheListener[] cacheListeners, Collection<String> expectedCacheListenerNames) {
if (!expectedCacheListenerNames.isEmpty()) {
assertNotNull("CacheListeners must not be null!", cacheListeners);
assertEquals(expectedCacheListenerNames.size(), cacheListeners.length);
assertTrue(toStrings(cacheListeners).containsAll(expectedCacheListenerNames));
}
}
protected Collection<String> toStrings(Object[] objects) {
List<String> cacheListenerNames = new ArrayList<String>(objects.length);
for (Object object : objects) {
cacheListenerNames.add(object.toString());
}
return cacheListenerNames;
}
@Test
public void testRegionConfiguration() {
assertNotNull("'/Example' Region was not properly initialized!", example);
assertEquals("Example", example.getName());
assertEquals("/Example", example.getFullPath());
assertNotNull(example.getAttributes());
assertEquals(DataPolicy.REPLICATE, example.getAttributes().getDataPolicy());
assertEquals(13, example.getAttributes().getInitialCapacity());
assertEquals(0.85f, example.getAttributes().getLoadFactor(), 0.0f);
assertCacheListeners(example.getAttributes().getCacheListeners(), Arrays.asList("A", "B"));
assertGemFireComponent(example.getAttributes().getCacheLoader(), "C");
assertGemFireComponent(example.getAttributes().getCacheWriter(), "D");
assertExpirationAttributes(example.getAttributes().getRegionTimeToLive(), "Region TTL",
120, ExpirationAction.LOCAL_DESTROY);
assertExpirationAttributes(example.getAttributes().getRegionIdleTimeout(), "Region TTI",
60, ExpirationAction.INVALIDATE);
assertExpirationAttributes(example.getAttributes().getEntryTimeToLive(), "Entry TTL",
30, ExpirationAction.DESTROY);
assertGemFireComponent(example.getAttributes().getCustomEntryIdleTimeout(), "E");
assertNotNull(example.getAttributes().getGatewaySenderIds());
assertEquals(1, example.getAttributes().getGatewaySenderIds().size());
assertEquals("GWS", example.getAttributes().getGatewaySenderIds().iterator().next());
assertNotNull(example.getAttributes().getAsyncEventQueueIds());
assertEquals(1, example.getAttributes().getAsyncEventQueueIds().size());
assertEquals("AEQ", example.getAttributes().getAsyncEventQueueIds().iterator().next());
}
protected static interface Nameable extends BeanNameAware {
String getName();
void setName(String name);
}
protected static abstract class AbstractNameable implements Nameable {
private String name;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
@Override
public void setBeanName(final String name) {
if (!StringUtils.hasText(this.name)) {
setName(name);
}
}
@Override
public String toString() {
return getName();
}
}
public static final class TestAsyncEventListener extends AbstractNameable implements AsyncEventListener {
@Override public boolean processEvents(List<AsyncEvent> events) {
throw new UnsupportedOperationException("Not Implemented!");
}
@Override public void close() { }
}
public static final class TestCacheListener<K, V> extends CacheListenerAdapter<K, V> implements Nameable {
private String name;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
@Override
public void setBeanName(final String name) {
if (!StringUtils.hasText(this.name)) {
setName(name);
}
}
@Override
public String toString() {
return getName();
}
}
public static final class TestCacheLoader<K, V> extends AbstractNameable implements CacheLoader<K, V> {
@Override
public V load(LoaderHelper<K, V> helper) throws CacheLoaderException {
throw new UnsupportedOperationException("Not Implemented!");
}
@Override
public void close() { }
}
public static final class TestCacheWriter<K, V> extends AbstractNameable implements CacheWriter<K, V> {
@Override public void beforeUpdate(EntryEvent<K, V> event) throws CacheWriterException { }
@Override public void beforeCreate(EntryEvent<K, V> event) throws CacheWriterException { }
@Override public void beforeDestroy(EntryEvent<K, V> event) throws CacheWriterException { }
@Override public void beforeRegionDestroy(RegionEvent<K, V> event) throws CacheWriterException { }
@Override public void beforeRegionClear(RegionEvent<K, V> event) throws CacheWriterException { }
@Override public void close() { }
}
public static final class TestCustomExpiry<K, V> extends AbstractNameable implements CustomExpiry<K, V> {
@Override public ExpirationAttributes getExpiry(Region.Entry<K, V> entry) {
throw new UnsupportedOperationException("Not Implemented!");
}
@Override public void close() { }
}
}