diff --git a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java index ee0751cd..81bd13e0 100644 --- a/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/CacheFactoryBean.java @@ -38,6 +38,7 @@ import com.gemstone.gemfire.GemFireException; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheClosedException; import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.GemFireCache; import com.gemstone.gemfire.distributed.DistributedMember; import com.gemstone.gemfire.distributed.DistributedSystem; import com.gemstone.gemfire.pdx.PdxSerializable; @@ -57,8 +58,13 @@ import com.gemstone.gemfire.pdx.PdxSerializer; * @author Costin Leau */ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, DisposableBean, - InitializingBean, FactoryBean, PersistenceExceptionTranslator { + InitializingBean, FactoryBean, PersistenceExceptionTranslator { + /** + * Inner class to avoid a hard dependency on the GemFire 6.6 API. + * + * @author Costin Leau + */ private class PdxOptions implements Runnable { private CacheFactory factory; @@ -69,6 +75,7 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl public void run() { Assert.isAssignable(PdxSerializer.class, pdxSerializer.getClass(), "Invalid pdx serializer used"); + factory.setPdxSerializer((PdxSerializer) pdxSerializer); factory.setPdxDiskStore(pdxDiskStoreName); factory.setPdxIgnoreUnreadFields(pdxIgnoreUnreadFields); @@ -77,9 +84,9 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl } } - private static final Log log = LogFactory.getLog(CacheFactoryBean.class); + protected final Log log = LogFactory.getLog(getClass()); - private Cache cache; + private GemFireCache cache; private Resource cacheXml; private Properties properties; private ClassLoader beanClassLoader; @@ -89,11 +96,11 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl private String beanName; private boolean useBeanFactoryLocator = true; // PDX options - private Object pdxSerializer; - private Boolean pdxPersistent; - private Boolean pdxReadSerialized; - private Boolean pdxIgnoreUnreadFields; - private String pdxDiskStoreName; + protected Object pdxSerializer; + protected Boolean pdxPersistent; + protected Boolean pdxReadSerialized; + protected Boolean pdxIgnoreUnreadFields; + protected String pdxDiskStoreName; public void afterPropertiesSet() throws Exception { // initialize locator @@ -104,7 +111,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl factoryLocator.afterPropertiesSet(); } Properties cfgProps = mergeProperties(); - CacheFactory factory = new CacheFactory(cfgProps); // use the bean class loader to load Declarable classes Thread th = Thread.currentThread(); @@ -115,19 +121,21 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl // first look for open caches String msg = null; try { - cache = CacheFactory.getAnyInstance(); + cache = fetchCache(); msg = "Retrieved existing"; } catch (CacheClosedException ex) { + Object factory = createFactory(cfgProps); + // GemFire 6.6 specific options if (pdxSerializer != null || pdxPersistent != null || pdxReadSerialized != null || pdxIgnoreUnreadFields != null || pdxDiskStoreName != null) { Assert.isTrue(ClassUtils.isPresent("com.gemstone.gemfire.pdx.PdxSerializer", beanClassLoader), "Cannot set PDX options since GemFire 6.6 not detected"); - new PdxOptions(factory).run(); + applyPdxOptions(factory); } // fall back to cache creation - cache = factory.create(); + cache = createCache(factory); msg = "Created"; } @@ -136,7 +144,6 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl log.info("Connected to Distributed System [" + system.getName() + "=" + member.getId() + "@" + member.getHost() + "]"); - log.info(msg + " GemFire v." + CacheFactory.getVersion() + " Cache [" + cache.getName() + "]"); // load/init cache.xml @@ -146,10 +153,34 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl if (log.isDebugEnabled()) log.debug("Initialized cache from " + cacheXml); + } finally { th.setContextClassLoader(oldTCCL); } } + /** + * Sets the PDX properties for the given object. Note this is implementation specific as it depends on the type + * of the factory passed in. + * + * @param factory + */ + protected void applyPdxOptions(Object factory) { + if (factory instanceof CacheFactory) { + new PdxOptions((CacheFactory) factory).run(); + } + } + + protected Object createFactory(Properties props) { + return new CacheFactory(props); + } + + protected GemFireCache fetchCache() { + return CacheFactory.getAnyInstance(); + } + + protected GemFireCache createCache(Object factory) { + return ((CacheFactory) factory).create(); + } private Properties mergeProperties() { Properties cfgProps = (properties != null ? (Properties) properties.clone() : new Properties()); @@ -184,11 +215,11 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl return null; } - public Cache getObject() throws Exception { + public GemFireCache getObject() throws Exception { return cache; } - public Class getObjectType() { + public Class getObjectType() { return (cache != null ? cache.getClass() : Cache.class); } @@ -282,4 +313,11 @@ public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanCl public void setPdxDiskStoreName(String pdxDiskStoreName) { this.pdxDiskStoreName = pdxDiskStoreName; } + + /** + * @return the beanFactory + */ + protected BeanFactory getBeanFactory() { + return beanFactory; + } } \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java new file mode 100644 index 00000000..2f739c52 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/client/ClientCacheFactoryBean.java @@ -0,0 +1,164 @@ +/* + * Copyright 2011 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.client; + +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Properties; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; +import com.gemstone.gemfire.cache.client.Pool; +import com.gemstone.gemfire.pdx.PdxSerializer; + +/** + * FactoryBean dedicated to creating client caches (caches for client JVMs). + * Acts an utility class (as client caches are a subset with a particular configuration of the generic cache). + * + * @author Costin Leau + */ +public class ClientCacheFactoryBean extends CacheFactoryBean { + + /** + * Inner class to avoid a hard dependency on the GemFire 6.6 API. + * + * @author Costin Leau + */ + private class PdxOptions implements Runnable { + + private ClientCacheFactory factory; + + PdxOptions(ClientCacheFactory factory) { + this.factory = factory; + } + + public void run() { + Assert.isAssignable(PdxSerializer.class, pdxSerializer.getClass(), "Invalid pdx serializer used"); + + factory.setPdxSerializer((PdxSerializer) pdxSerializer); + factory.setPdxDiskStore(pdxDiskStoreName); + factory.setPdxIgnoreUnreadFields(pdxIgnoreUnreadFields); + factory.setPdxPersistent(pdxPersistent); + factory.setPdxReadSerialized(pdxReadSerialized); + } + } + + private String poolName; + private Pool pool; + + @Override + protected GemFireCache createCache(Object factory) { + ClientCacheFactory ccf = (ClientCacheFactory) factory; + initializePool(ccf); + return ccf.create(); + } + + @Override + protected Object createFactory(Properties props) { + return new ClientCacheFactory(props); + } + + @Override + protected GemFireCache fetchCache() { + return ClientCacheFactory.getAnyInstance(); + } + + private void initializePool(ClientCacheFactory ccf) { + Pool p = pool; + + if (p == null && StringUtils.hasText(poolName)) { + BeanFactory beanFactory = getBeanFactory(); + + // try to eagerly initialize the pool name, if defined as a bean + if (beanFactory.isTypeMatch(poolName, Pool.class)) { + if (log.isDebugEnabled()) { + log.debug("Found bean definition for pool '" + poolName + "'. Eagerly initializing it..."); + } + p = beanFactory.getBean(poolName, Pool.class); + } + } + + if (p != null) { + // copy the pool settings - this way if the pool is not found, at least the cache will have a similar config + ccf.setPoolFreeConnectionTimeout(p.getFreeConnectionTimeout()); + ccf.setPoolIdleTimeout(p.getIdleTimeout()); + ccf.setPoolLoadConditioningInterval(p.getLoadConditioningInterval()); + ccf.setPoolMaxConnections(p.getMaxConnections()); + ccf.setPoolMinConnections(p.getMinConnections()); + ccf.setPoolMultiuserAuthentication(p.getMultiuserAuthentication()); + ccf.setPoolPingInterval(p.getPingInterval()); + ccf.setPoolPRSingleHopEnabled(p.getPRSingleHopEnabled()); + ccf.setPoolReadTimeout(p.getReadTimeout()); + ccf.setPoolRetryAttempts(p.getRetryAttempts()); + ccf.setPoolServerGroup(p.getServerGroup()); + ccf.setPoolSocketBufferSize(p.getSocketBufferSize()); + ccf.setPoolStatisticInterval(p.getStatisticInterval()); + ccf.setPoolSubscriptionAckInterval(p.getSubscriptionAckInterval()); + ccf.setPoolSubscriptionEnabled(p.getSubscriptionEnabled()); + ccf.setPoolSubscriptionMessageTrackingTimeout(p.getSubscriptionMessageTrackingTimeout()); + ccf.setPoolSubscriptionRedundancy(p.getSubscriptionRedundancy()); + ccf.setPoolThreadLocalConnections(p.getThreadLocalConnections()); + + List locators = p.getLocators(); + if (locators != null) { + for (InetSocketAddress inet : locators) { + ccf.addPoolLocator(inet.getHostName(), inet.getPort()); + } + } + + + List servers = p.getServers(); + if (locators != null) { + for (InetSocketAddress inet : servers) { + ccf.addPoolLocator(inet.getHostName(), inet.getPort()); + } + } + } + } + + /** + * Sets the pool name used by this client. + * + * @param poolName + */ + public void setPoolName(String poolName) { + Assert.hasText(poolName, "pool name is required"); + this.poolName = poolName; + } + + /** + * Sets the pool used by this client. + * + * @param pool + */ + public void setPool(Pool pool) { + Assert.notNull(pool, "pool cannot be null"); + this.pool = pool; + } + + @Override + protected void applyPdxOptions(Object factory) { + if (factory instanceof ClientCacheFactory) { + new PdxOptions((ClientCacheFactory) factory).run(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java index 9bab6b96..fcbdfc94 100644 --- a/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/client/PoolFactoryBean.java @@ -55,7 +55,7 @@ public class PoolFactoryBean implements FactoryBean, InitializingBean, Dis private static final Log log = LogFactory.getLog(PoolFactoryBean.class); - // whether the pool has been created internaly or not + // whether the pool has been created internally or not private boolean internalPool = true; private Pool pool; diff --git a/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java b/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java new file mode 100644 index 00000000..01683e71 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/ClientCacheParser.java @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2011 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.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.data.gemfire.client.ClientCacheFactoryBean; +import org.w3c.dom.Element; + +/** + * Parser for <client-cache;gt; definitions. + * + * @author Costin Leau + */ +class ClientCacheParser extends CacheParser { + + @Override + protected Class getBeanClass(Element element) { + return ClientCacheFactoryBean.class; + } + + @Override + protected void doParse(Element element, BeanDefinitionBuilder builder) { + super.doParse(element, builder); + + ParsingUtils.setPropertyValue(element, builder, "pool-name", "poolName"); + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java index 1d4101dd..7882ab01 100644 --- a/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java +++ b/src/main/java/org/springframework/data/gemfire/config/GemfireNamespaceHandler.java @@ -27,6 +27,8 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("cache", new CacheParser()); + registerBeanDefinitionParser("client-cache", new ClientCacheParser()); + registerBeanDefinitionParser("lookup-region", new LookupRegionParser()); registerBeanDefinitionParser("replicated-region", new ReplicatedRegionParser()); registerBeanDefinitionParser("partitioned-region", new PartitionedRegionParser()); diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd index 507f0c0f..45f183e7 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd @@ -17,7 +17,42 @@ ]]> - + + + + + + + + + + + + + + + namespace and its 'properties' element. + ]]> + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + - - - - - namespace and its 'properties' element. - ]]> - - - - - - - + + + + @@ -805,10 +833,10 @@ cache to differ from other caches. - + diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java new file mode 100644 index 00000000..42270477 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheTest.java @@ -0,0 +1,15 @@ +package org.springframework.data.gemfire.client; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("client-cache.xml") +public class ClientCacheTest { + @Test + public void test() { + + } +} diff --git a/src/test/java/org/springframework/data/gemfire/config/CacheNamespaceTest.java b/src/test/java/org/springframework/data/gemfire/config/CacheNamespaceTest.java index be07e62c..41086663 100644 --- a/src/test/java/org/springframework/data/gemfire/config/CacheNamespaceTest.java +++ b/src/test/java/org/springframework/data/gemfire/config/CacheNamespaceTest.java @@ -27,6 +27,7 @@ import org.springframework.data.gemfire.CacheFactoryBean; import org.springframework.data.gemfire.GemfireBeanFactoryLocator; import org.springframework.data.gemfire.RecreatingContextTest; import org.springframework.data.gemfire.TestUtils; +import org.springframework.data.gemfire.client.ClientCacheFactoryBean; /** * @author Costin Leau @@ -76,4 +77,19 @@ public class CacheNamespaceTest extends RecreatingContextTest { } } + @Test + public void testBasicClientCache() throws Exception { + assertTrue(ctx.containsBean("client-cache")); + ClientCacheFactoryBean cfb = (ClientCacheFactoryBean) ctx.getBean("&client-cache"); + assertNull(TestUtils.readField("cacheXml", cfb)); + assertNull(TestUtils.readField("properties", cfb)); + } + + @Test + public void testBasicClientCacheWithXml() throws Exception { + assertTrue(ctx.containsBean("client-cache-with-xml")); + ClientCacheFactoryBean cfb = (ClientCacheFactoryBean) ctx.getBean("&client-cache-with-xml"); + Resource res = TestUtils.readField("cacheXml", cfb); + assertEquals("gemfire-client-cache.xml", res.getFilename()); + } } \ No newline at end of file diff --git a/src/test/resources/gemfire-client-cache.xml b/src/test/resources/gemfire-client-cache.xml new file mode 100644 index 00000000..11c3a381 --- /dev/null +++ b/src/test/resources/gemfire-client-cache.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml b/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml new file mode 100644 index 00000000..748ce520 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/client/client-cache.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/test/resources/org/springframework/data/gemfire/config/cache-ns.xml b/src/test/resources/org/springframework/data/gemfire/config/cache-ns.xml index 263d6e9f..de546d8a 100644 --- a/src/test/resources/org/springframework/data/gemfire/config/cache-ns.xml +++ b/src/test/resources/org/springframework/data/gemfire/config/cache-ns.xml @@ -23,4 +23,8 @@ + + + + \ No newline at end of file