SGF-434 - Add a durable GemFire client cache test to assert proper behavior by SDG.

Removed the ClientCacheFactoryBean.readyForEvents() method and moved the logic to onApplicationEvent(..).
Updated the spring-gemfire 1.6 and 1.7 XSD with the new keep-alive attribue on the client-cache element.
Changed the generic signature of the CacheFactoryBean class back to com.gemstone.gemfire.cache.Cache due to the Spring container bean resolution Exception when using JavaConfig in SDG 1.7 with GemFire 8.1 and core Spring Framework 4.1.7.
This commit is contained in:
John Blum
2015-10-13 23:52:28 -07:00
parent b52a18579b
commit a458115c7f
8 changed files with 141 additions and 92 deletions

View File

@@ -76,9 +76,15 @@ import com.gemstone.gemfire.pdx.PdxSerializer;
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.dao.support.PersistenceExceptionTranslator
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.CacheFactory
* @see com.gemstone.gemfire.cache.DynamicRegionFactory
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.distributed.DistributedMember
* @see com.gemstone.gemfire.distributed.DistributedSystem
*/
@SuppressWarnings("unused")
public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean<GemFireCache>,
public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, FactoryBean<Cache>,
InitializingBean, DisposableBean, PersistenceExceptionTranslator {
protected boolean close = true;
@@ -96,7 +102,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
protected Boolean pdxReadSerialized;
protected Boolean useClusterConfiguration;
protected GemFireCache cache;
protected Cache cache;
protected ClassLoader beanClassLoader;
@@ -226,7 +232,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/* (non-Javadoc) */
private GemFireCache init() throws Exception {
private Cache init() throws Exception {
initBeanFactoryLocator();
final ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
@@ -396,6 +402,7 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
* Post processes the GemFire Cache instance by loading any cache.xml, applying settings specified in SDG XML
* configuration meta-data, and registering the appropriate Transaction Listeners, Writer and JNDI settings.
*
* @param <T> parameterized Class type extension of GemFireCache.
* @param cache the GemFire Cache instance to process.
* @return the GemFire Cache instance after processing.
* @throws IOException if the cache.xml Resource could not be loaded and applied to the Cache instance.
@@ -785,35 +792,44 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/**
* @return the beanClassLoader
* Gets a reference to the JRE ClassLoader used to load and create bean classes in the Spring container.
*
* @return the JRE ClassLoader used to load and created beans in the Spring container.
* @see java.lang.ClassLoader
*/
public ClassLoader getBeanClassLoader() {
return beanClassLoader;
}
/**
* @return the beanFactory
* Gets a reference to the Spring BeanFactory that created this GemFire Cache FactoryBean.
*
* @return a reference to the Spring BeanFactory.
* @see org.springframework.beans.factory.BeanFactory
*/
public BeanFactory getBeanFactory() {
return beanFactory;
}
/**
* @return the beanFactoryLocator
*/
/* (non-Javadoc) */
public GemfireBeanFactoryLocator getBeanFactoryLocator() {
return beanFactoryLocator;
}
/**
* @return the beanName
* Gets the Spring bean name for the GemFire Cache.
*
* @return a String value indicating the Spring container bean name for the GemFire Cache object/component.
*/
public String getBeanName() {
return beanName;
}
/**
* @return the cacheXml
* Gets a reference to the GemFire native cache.xml file as a Spring Resource.
*
* @return the a reference to the GemFire native cache.xml as a Spring Resource.
* @see org.springframework.core.io.Resource
*/
public Resource getCacheXml() {
return cacheXml;
@@ -841,14 +857,17 @@ public class CacheFactoryBean implements BeanClassLoaderAware, BeanFactoryAware,
}
/**
* @return the properties
* Gets a reference to the GemFire System Properties.
*
* @return a reference to the GemFire System Properties.
* @see java.util.Properties
*/
public Properties getProperties() {
return (properties != null ? properties : (properties = new Properties()));
}
@Override
public GemFireCache getObject() throws Exception {
public Cache getObject() throws Exception {
return init();
}

View File

@@ -27,6 +27,7 @@ import org.springframework.data.gemfire.config.GemfireConstants;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.client.ClientCacheFactory;
@@ -219,32 +220,25 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
}
/**
* Register for events after Pool and Regions have been created and iff non-durable client...
* Inform the GemFire cluster that this client cache is ready to receive events iff the client is non-durable.
*
* @param event the ApplicationContextEvent fired when the ApplicationContext is refreshed.
* @see org.springframework.context.Lifecycle#start()
* @see com.gemstone.gemfire.cache.client.ClientCache
* @see #readyForEvents(com.gemstone.gemfire.cache.GemFireCache)
* @see com.gemstone.gemfire.cache.client.ClientCache#readyForEvents()
* @see #getReadyForEvents()
* @see #getObject()
*/
@Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
readyForEvents(this.cache);
}
/**
* Inform the GemFire cluster that this client cache is ready to receive events.
*/
private <T extends GemFireCache> T readyForEvents(T clientCache) {
if (Boolean.TRUE.equals(getReadyForEvents()) && !clientCache.isClosed()) {
if (isReadyForEvents()) {
try {
((ClientCache) clientCache).readyForEvents();
((ClientCache) fetchCache()).readyForEvents();
}
catch (IllegalStateException ignore) {
// cannot be called for a non-durable client so exception is thrown
// thrown if clientCache.readyForEvents() is called on a non-durable client
}
catch (CacheClosedException ignore) {
}
}
return clientCache;
}
@Override
@@ -336,6 +330,11 @@ public class ClientCacheFactoryBean extends CacheFactoryBean implements Applicat
return readyForEvents;
}
/* (non-Javadoc) */
public boolean isReadyForEvents() {
return Boolean.TRUE.equals(getReadyForEvents());
}
@Override
public final void setUseClusterConfiguration(Boolean useClusterConfiguration) {
throw new UnsupportedOperationException("Shared, cluster configuration is not applicable to clients.");

View File

@@ -349,6 +349,13 @@ Defines a GemFire Client Cache instance used for creating or retrieving 'regions
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="cacheBaseType">
<xsd:attribute name="keep-alive" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Boolean value indicating whether the server should keep the durable client's queues alive for the timeout period.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pool-name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -349,6 +349,13 @@ Defines a GemFire Client Cache instance used for creating or retrieving 'regions
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="cacheBaseType">
<xsd:attribute name="keep-alive" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Boolean value indicating whether the server should keep the durable client's queues alive for the timeout period.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pool-name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -352,6 +352,7 @@ Defines a GemFire Client Cache instance used for creating or retrieving 'regions
<xsd:attribute name="keep-alive" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Boolean value indicating whether the server should keep the durable client's queues alive for the timeout period.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>

View File

@@ -27,13 +27,13 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
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 java.lang.reflect.Field;
import java.util.Properties;
import org.junit.Test;
@@ -42,8 +42,8 @@ import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.TestUtils;
import org.springframework.data.gemfire.config.GemfireConstants;
import org.springframework.data.util.ReflectionUtils;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.client.ClientCacheFactory;
@@ -60,7 +60,7 @@ import com.gemstone.gemfire.pdx.PdxSerializer;
* @see org.mockito.Mockito
* @see org.junit.Test
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.TestUtils
* @see com.gemstone.gemfire.cache.client.ClientCache
* @since 1.7.0
*/
public class ClientCacheFactoryBeanTest {
@@ -415,78 +415,83 @@ public class ClientCacheFactoryBeanTest {
}
@Test
public void onApplicationEventSignalsReadyForEvents() throws Exception {
ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache");
@SuppressWarnings("unchecked")
public void onApplicationEventCallsClientCacheReadyForEvents() {
final ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache");
when(mockClientCache.isClosed()).thenReturn(false);
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean();
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() {
@Override protected <T extends GemFireCache> T fetchCache() {
return (T) mockClientCache;
}
};
clientCacheFactoryBean.setReadyForEvents(true);
ReflectionUtils.setField(ReflectionUtils.findField(ClientCacheFactoryBean.class,
new org.springframework.util.ReflectionUtils.FieldFilter() {
@Override public boolean matches(final Field field) {
return field.getName().equals("cache");
}
}), clientCacheFactoryBean, mockClientCache);
assertThat(clientCacheFactoryBean.getReadyForEvents(), is(true));
assertThat(clientCacheFactoryBean.isReadyForEvents(), is(true));
clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent"));
verify(mockClientCache, times(1)).isClosed();
verify(mockClientCache, times(1)).readyForEvents();
}
@Test
public void onApplicationEventDoesNotSignalReadyForEventsWhenClientCacheIsClosed() {
ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache");
@SuppressWarnings("unchecked")
public void onApplicationEventDoesNotCallClientCacheReadyForEventsWhenClientCacheFactoryBeanReadyForEventsIsFalse() {
final ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache");
when(mockClientCache.isClosed()).thenReturn(true);
doThrow(new RuntimeException("test")).when(mockClientCache).readyForEvents();
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean();
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() {
@Override protected <T extends GemFireCache> T fetchCache() {
return (T) mockClientCache;
}
};
clientCacheFactoryBean.setReadyForEvents(true);
clientCacheFactoryBean.setReadyForEvents(false);
ReflectionUtils.setField(ReflectionUtils.findField(ClientCacheFactoryBean.class,
new org.springframework.util.ReflectionUtils.FieldFilter() {
@Override public boolean matches(final Field field) {
return field.getName().equals("cache");
}
}), clientCacheFactoryBean, mockClientCache);
assertThat(clientCacheFactoryBean.getReadyForEvents(), is(true));
assertThat(clientCacheFactoryBean.isReadyForEvents(), is(false));
clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent"));
verify(mockClientCache, times(1)).isClosed();
verify(mockClientCache, never()).readyForEvents();
}
@Test
public void onApplicationEventDoesNotSignalReadyForEventsWhenClientCacheFactoryBeanReadyForEventsIsFalse() {
ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache");
@SuppressWarnings("unchecked")
public void onApplicationEventHandlesIllegalStateException() {
final ClientCache mockClientCache = mock(ClientCache.class, "MockClientCache");
when(mockClientCache.isClosed()).thenReturn(false);
doThrow(new IllegalStateException("non-durable client")).when(mockClientCache).readyForEvents();
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean();
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() {
@Override protected <T extends GemFireCache> T fetchCache() {
return (T) mockClientCache;
}
};
clientCacheFactoryBean.setReadyForEvents(false);
clientCacheFactoryBean.setReadyForEvents(true);
ReflectionUtils.setField(ReflectionUtils.findField(ClientCacheFactoryBean.class,
new org.springframework.util.ReflectionUtils.FieldFilter() {
@Override public boolean matches(final Field field) {
return field.getName().equals("cache");
}
}), clientCacheFactoryBean, mockClientCache);
assertThat(clientCacheFactoryBean.getReadyForEvents(), is(false));
assertThat(clientCacheFactoryBean.isReadyForEvents(), is(true));
clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent"));
verify(mockClientCache, never()).isClosed();
verify(mockClientCache, never()).readyForEvents();
verify(mockClientCache, times(1)).readyForEvents();
}
@Test
@SuppressWarnings("unchecked")
public void onApplicationEventHandlesCacheClosedException() {
ClientCacheFactoryBean clientCacheFactoryBean = new ClientCacheFactoryBean() {
@Override protected <T extends GemFireCache> T fetchCache() {
throw new CacheClosedException("test");
}
};
clientCacheFactoryBean.setReadyForEvents(true);
assertThat(clientCacheFactoryBean.isReadyForEvents(), is(true));
clientCacheFactoryBean.onApplicationEvent(mock(ContextRefreshedEvent.class, "MockContextRefreshedEvent"));
}
@Test
@@ -541,7 +546,7 @@ public class ClientCacheFactoryBeanTest {
}
@Test(expected = IllegalArgumentException.class)
public void setPoolNameToInvalidValue() {
public void setPoolNameWithAnIllegalArgument() {
try {
new ClientCacheFactoryBean().setPoolName(" ");
}

View File

@@ -19,6 +19,7 @@ package org.springframework.data.gemfire.client;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
@@ -221,12 +222,16 @@ public class DurableClientCacheIntegrationTest extends AbstractGemFireClientServ
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (RUN_COUNT.get() == 2 && bean instanceof ClientCache) {
// NOTE pending event count is possibly 3 because it includes the 2 puts from the client cache producer
// as well as the "marker"
assertThat(((ClientCache) bean).getDefaultPool().getPendingEventCount(), is(equalTo(
RUN_COUNT.get() == 1 ? -2 : 3)));
pause(TimeUnit.SECONDS.toMillis(3));
if (bean instanceof ClientCache) {
if (RUN_COUNT.get() == 1) {
assertThat(((ClientCache) bean).getDefaultPool().getPendingEventCount(), is(equalTo(-2)));
}
else {
// NOTE pending event count is possibly 3 because it includes the 2 puts from the client cache producer
// as well as the "marker"
assertThat(((ClientCache) bean).getDefaultPool().getPendingEventCount(), is(greaterThanOrEqualTo(2)));
pause(TimeUnit.SECONDS.toMillis(3));
}
}
return bean;

View File

@@ -10,7 +10,9 @@
* 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.test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContextInitializer;
@@ -19,30 +21,34 @@ import org.springframework.util.StringUtils;
/**
* @author David Turanski
*
* @author John Blum
*/
public class GemfireTestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static Log logger = LogFactory.getLog(GemfireTestApplicationContextInitializer.class);
private static final Log LOG = LogFactory.getLog(GemfireTestApplicationContextInitializer.class);
public static final String GEMFIRE_TEST_RUNNER_DISABLED = "org.springframework.data.gemfire.test.GemfireTestRunner.nomock";
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextInitializer#initialize(org.springframework.context.ConfigurableApplicationContext)
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
if (StringUtils.hasText(System.getProperty(GEMFIRE_TEST_RUNNER_DISABLED))) {
String value = System.getProperty(GEMFIRE_TEST_RUNNER_DISABLED);
String gemfireTestRunnerDisabled = System.getProperty(GEMFIRE_TEST_RUNNER_DISABLED, Boolean.FALSE.toString());
if (!("NO".equalsIgnoreCase(value) || "FALSE".equalsIgnoreCase(value))) {
logger.warn(String.format("Mocks disabled. Using real GemFire components: %1$s = %2$s",
GEMFIRE_TEST_RUNNER_DISABLED, value));
return;
}
if (isGemFireTestRunnerDisable(gemfireTestRunnerDisabled)) {
LOG.warn(String.format("WARNING - Mocks disabled; Using real GemFire components (%1$s = %2$s)",
GEMFIRE_TEST_RUNNER_DISABLED, gemfireTestRunnerDisabled));
}
else {
applicationContext.getBeanFactory().addBeanPostProcessor(new GemfireTestBeanPostProcessor());
}
}
applicationContext.getBeanFactory().addBeanPostProcessor(new GemfireTestBeanPostProcessor());
private boolean isGemFireTestRunnerDisable(final String systemPropertyValue) {
return (Boolean.valueOf(StringUtils.trimAllWhitespace(systemPropertyValue))
|| "yes".equalsIgnoreCase(systemPropertyValue));
}
}