From f7ead2f7c50e477c89a83c1812348c3b43967693 Mon Sep 17 00:00:00 2001 From: John Blum Date: Thu, 7 Nov 2013 12:48:04 -0800 Subject: [PATCH] Fixes JIRA issues SGF-217 involving the use of a Disk Store for overflow on a client subscription queue with a GemFire Cache Server, which was not properly referenced by the Disk Store's 'name'. In addition, fixes JIRA issues SGF-218 involving the improper configuration of the Eviction Policy for a Cache Server's Client Subscription Queue when overflow occurs. --- .../gemfire/config/CacheServerParser.java | 42 ++++---- .../server/CacheServerFactoryBean.java | 72 ++++++++------ .../gemfire/CacheServerIntegrationTest.java | 96 +++++++++++++++++++ ...he-server-with-subscription-disk-store.xml | 29 ++++++ 4 files changed, 189 insertions(+), 50 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/CacheServerIntegrationTest.java create mode 100644 src/test/resources/org/springframework/data/gemfire/cache-server-with-subscription-disk-store.xml diff --git a/src/main/java/org/springframework/data/gemfire/config/CacheServerParser.java b/src/main/java/org/springframework/data/gemfire/config/CacheServerParser.java index d415fa03..423cf8e6 100644 --- a/src/main/java/org/springframework/data/gemfire/config/CacheServerParser.java +++ b/src/main/java/org/springframework/data/gemfire/config/CacheServerParser.java @@ -29,8 +29,9 @@ import org.w3c.dom.Element; /** * Namespace parser for "cache-server" element. - * + *

* @author Costin Leau + * @author John Blum */ class CacheServerParser extends AbstractSimpleBeanDefinitionParser { @@ -41,7 +42,7 @@ class CacheServerParser extends AbstractSimpleBeanDefinitionParser { @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) - throws BeanDefinitionStoreException { + throws BeanDefinitionStoreException { String name = super.resolveId(element, definition, parserContext); if (!StringUtils.hasText(name)) { name = "gemfireServer"; @@ -52,35 +53,38 @@ class CacheServerParser extends AbstractSimpleBeanDefinitionParser { @Override protected boolean isEligibleAttribute(Attr attribute, ParserContext parserContext) { return super.isEligibleAttribute(attribute, parserContext) && !"groups".equals(attribute.getName()) - && !"cache-ref".equals(attribute.getName()); + && !"cache-ref".equals(attribute.getName()); } @Override protected void postProcess(BeanDefinitionBuilder builder, Element element) { + String cacheRefAttribute = element.getAttribute("cache-ref"); - String attr = element.getAttribute("cache-ref"); - // add cache reference (fallback to default if nothing is specified) - builder.addPropertyReference("cache", (StringUtils.hasText(attr) ? attr : GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME)); + builder.addPropertyReference("cache", (StringUtils.hasText(cacheRefAttribute) ? cacheRefAttribute + : GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME)); - attr = element.getAttribute("groups"); - if (StringUtils.hasText(attr)) { - builder.addPropertyValue("serverGroups", StringUtils.commaDelimitedListToStringArray(attr)); + String groupsAttribute = element.getAttribute("groups"); + + if (StringUtils.hasText(groupsAttribute)) { + builder.addPropertyValue("serverGroups", StringUtils.commaDelimitedListToStringArray(groupsAttribute)); } parseSubscription(builder, element); } private void parseSubscription(BeanDefinitionBuilder builder, Element element) { - Element subConfig = DomUtils.getChildElementByTagName(element, "subscription-config"); - if (subConfig == null) { - return; - } + Element subscriptionConfigElement = DomUtils.getChildElementByTagName(element, "subscription-config"); - ParsingUtils.setPropertyValue(subConfig, builder, "capacity", "subscriptionCapacity"); - ParsingUtils.setPropertyValue(subConfig, builder, "disk-store", "subscriptionDiskStore"); - String attr = element.getAttribute("eviction-type"); - if (StringUtils.hasText(attr)) { - builder.addPropertyValue("subscriptionEvictionPolicy", attr.toUpperCase()); + if (subscriptionConfigElement != null) { + ParsingUtils.setPropertyValue(subscriptionConfigElement, builder, "capacity", "subscriptionCapacity"); + ParsingUtils.setPropertyValue(subscriptionConfigElement, builder, "disk-store", "subscriptionDiskStore"); + + String evictionTypeAttribute = subscriptionConfigElement.getAttribute("eviction-type"); + + if (StringUtils.hasText(evictionTypeAttribute)) { + builder.addPropertyValue("subscriptionEvictionPolicy", evictionTypeAttribute.toUpperCase()); + } } } -} \ No newline at end of file + +} diff --git a/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java b/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java index 1b7f3cdf..243e0da2 100644 --- a/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/server/CacheServerFactoryBean.java @@ -24,8 +24,8 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.SmartLifecycle; -import org.springframework.core.io.Resource; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.InterestRegistrationListener; @@ -35,43 +35,50 @@ import com.gemstone.gemfire.cache.server.ServerLoadProbe; /** * FactoryBean for easy creation and configuration of GemFire {@link CacheServer} instances. - * + *

* @author Costin Leau + * @author John Blum */ public class CacheServerFactoryBean implements FactoryBean, InitializingBean, DisposableBean, - SmartLifecycle { + SmartLifecycle { private boolean autoStartup = true; - - private int port = CacheServer.DEFAULT_PORT; - private int maxConnections = CacheServer.DEFAULT_MAX_CONNECTIONS; - private int maxThreads = CacheServer.DEFAULT_MAX_THREADS; private boolean notifyBySubscription = CacheServer.DEFAULT_NOTIFY_BY_SUBSCRIPTION; - private int socketBufferSize = CacheServer.DEFAULT_SOCKET_BUFFER_SIZE; - private int maxTimeBetweenPings = CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS; - private int maxMessageCount = CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT; - private int messageTimeToLive = CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE; - private String[] serverGroups = CacheServer.DEFAULT_GROUPS; - private ServerLoadProbe serverLoadProbe = CacheServer.DEFAULT_LOAD_PROBE; - private long loadPollInterval = CacheServer.DEFAULT_LOAD_POLL_INTERVAL; - private String bindAddress = CacheServer.DEFAULT_BIND_ADDRESS; - private String hostNameForClients = CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS; - private Set listeners = Collections.emptySet(); - private SubscriptionEvictionPolicy evictionPolicy = SubscriptionEvictionPolicy.valueOf(ClientSubscriptionConfig.DEFAULT_EVICTION_POLICY.toUpperCase()); + private int maxConnections = CacheServer.DEFAULT_MAX_CONNECTIONS; + private int maxMessageCount = CacheServer.DEFAULT_MAXIMUM_MESSAGE_COUNT; + private int maxThreads = CacheServer.DEFAULT_MAX_THREADS; + private int maxTimeBetweenPings = CacheServer.DEFAULT_MAXIMUM_TIME_BETWEEN_PINGS; + private int messageTimeToLive = CacheServer.DEFAULT_MESSAGE_TIME_TO_LIVE; + private int port = CacheServer.DEFAULT_PORT; + private int socketBufferSize = CacheServer.DEFAULT_SOCKET_BUFFER_SIZE; private int subscriptionCapacity = ClientSubscriptionConfig.DEFAULT_CAPACITY; - private Resource subscriptionDiskStore; + + private long loadPollInterval = CacheServer.DEFAULT_LOAD_POLL_INTERVAL; private Cache cache; + private CacheServer cacheServer; + private ServerLoadProbe serverLoadProbe = CacheServer.DEFAULT_LOAD_PROBE; + + private Set listeners = Collections.emptySet(); + + private String bindAddress = CacheServer.DEFAULT_BIND_ADDRESS; + private String hostNameForClients = CacheServer.DEFAULT_HOSTNAME_FOR_CLIENTS; + private String subscriptionDiskStore; + + private String[] serverGroups = CacheServer.DEFAULT_GROUPS; + + private SubscriptionEvictionPolicy subscriptionEvictionPolicy = SubscriptionEvictionPolicy.valueOf( + ClientSubscriptionConfig.DEFAULT_EVICTION_POLICY.toUpperCase()); public CacheServer getObject() { return cacheServer; } public Class getObjectType() { - return ((this.cacheServer != null) ? cacheServer.getClass() : CacheServer.class); + return (this.cacheServer != null ? cacheServer.getClass() : CacheServer.class); } public boolean isSingleton() { @@ -79,7 +86,7 @@ public class CacheServerFactoryBean implements FactoryBean, Initial } public void afterPropertiesSet() throws IOException { - Assert.notNull(cache, "cache is required"); + Assert.notNull(cache, "A GemFire Cache is required."); cacheServer = cache.addCacheServer(); cacheServer.setBindAddress(bindAddress); @@ -100,12 +107,14 @@ public class CacheServerFactoryBean implements FactoryBean, Initial cacheServer.registerInterestRegistrationListener(listener); } - // client config ClientSubscriptionConfig config = cacheServer.getClientSubscriptionConfig(); + config.setCapacity(subscriptionCapacity); - config.setEvictionPolicy(evictionPolicy.name().toLowerCase()); - if (subscriptionDiskStore != null) - config.setDiskStoreName(subscriptionDiskStore.getFile().getCanonicalPath()); + config.setEvictionPolicy(subscriptionEvictionPolicy.name().toLowerCase()); + + if (StringUtils.hasText(subscriptionDiskStore)) { + config.setDiskStoreName(subscriptionDiskStore); + } } public void destroy() { @@ -128,7 +137,7 @@ public class CacheServerFactoryBean implements FactoryBean, Initial } public boolean isRunning() { - return (cacheServer != null ? cacheServer.isRunning() : false); + return (cacheServer != null && cacheServer.isRunning()); } public void start() { @@ -210,10 +219,10 @@ public class CacheServerFactoryBean implements FactoryBean, Initial } /** - * @param evictionPolicy the evictionPolicy to set + * @param evictionPolicy the subscriptionEvictionPolicy to set */ public void setSubscriptionEvictionPolicy(SubscriptionEvictionPolicy evictionPolicy) { - this.evictionPolicy = evictionPolicy; + this.subscriptionEvictionPolicy = evictionPolicy; } /** @@ -223,7 +232,8 @@ public class CacheServerFactoryBean implements FactoryBean, Initial this.subscriptionCapacity = subscriptionCapacity; } - public void setSubscriptionDiskStore(Resource resource) { - this.subscriptionDiskStore = resource; + public void setSubscriptionDiskStore(String diskStoreName) { + this.subscriptionDiskStore = diskStoreName; } -} \ No newline at end of file + +} diff --git a/src/test/java/org/springframework/data/gemfire/CacheServerIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/CacheServerIntegrationTest.java new file mode 100644 index 00000000..63676a10 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/CacheServerIntegrationTest.java @@ -0,0 +1,96 @@ +/* + * 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.io.File; +import javax.annotation.Resource; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.gemstone.gemfire.cache.server.CacheServer; + +/** + * The CacheServerIntegrationTest class is a test suite of test cases testing the functionality of GemFire Cache Servers + * configured using the Spring Data GemFire XML namespace. + *

+ * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.server.CacheServerFactoryBean + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @since 1.3.3 + */ +@ContextConfiguration("cache-server-with-subscription-disk-store.xml") +@RunWith(SpringJUnit4ClassRunner.class) +public class CacheServerIntegrationTest { + + @Resource(name = "testCacheServer") + private CacheServer cacheServer; + + protected static boolean createDirectory(final File path) { + return (path != null && (path.isDirectory() || path.mkdirs())); + } + + protected static File createFile(final String pathname) { + return new File(pathname); + } + + protected static void deleteRecursive(final File path) { + if (path.isDirectory()) { + for (File file : path.listFiles()) { + deleteRecursive(file); + } + } + + path.delete(); + } + + @BeforeClass + public static void setupBeforeClass() { + assertTrue(createDirectory(createFile("./gemfire/subscription-disk-store"))); + } + + @AfterClass + public static void tearDownAfterClass() { + deleteRecursive(createFile("./gemfire")); + } + + @Test + public void testCacheServerRunningWithSubscription() { + assertNotNull(cacheServer); + assertTrue(cacheServer.isRunning()); + assertEquals("localhost", cacheServer.getBindAddress()); + assertNotNull(cacheServer.getGroups()); + assertEquals(1, cacheServer.getGroups().length); + assertEquals("test-server", cacheServer.getGroups()[0]); + assertEquals(1, cacheServer.getMaxConnections()); + assertNotNull(cacheServer.getClientSubscriptionConfig()); + assertEquals(512, cacheServer.getClientSubscriptionConfig().getCapacity()); + assertEquals("testSubscriptionDiskStore", cacheServer.getClientSubscriptionConfig().getDiskStoreName()); + assertTrue("ENTRY".equalsIgnoreCase(cacheServer.getClientSubscriptionConfig().getEvictionPolicy())); + } + +} diff --git a/src/test/resources/org/springframework/data/gemfire/cache-server-with-subscription-disk-store.xml b/src/test/resources/org/springframework/data/gemfire/cache-server-with-subscription-disk-store.xml new file mode 100644 index 00000000..22b93ed3 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/cache-server-with-subscription-disk-store.xml @@ -0,0 +1,29 @@ + + + + + springGemFireCacheServerWithClientSubscriptionTest + config + + + + + + + + + + + + + +