+ added basic lookup factory bean
+ refactored existing schema
+ added namespace parsers
+ added integration tests
This commit is contained in:
costin
2010-09-15 18:39:09 +03:00
parent b6897115c3
commit 1d422f5d8f
8 changed files with 285 additions and 140 deletions

View File

@@ -18,14 +18,9 @@ package org.springframework.data.gemfire;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.AttributesFactory;
import com.gemstone.gemfire.cache.Cache;
@@ -38,19 +33,15 @@ import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.Scope;
/**
* FactoryBean for creating generic Gemfire {@link Region}s. Will try to first locate the region (by name)
* FactoryBean for creating generic GemFire {@link Region}s. Will try to first locate the region (by name)
* and, in case none if found, proceed to creating one using the given settings.
*
* @author Costin Leau
*/
public class RegionFactoryBean<K, V> implements DisposableBean, FactoryBean<Region<K, V>>, InitializingBean,
BeanNameAware {
public class RegionFactoryBean<K, V> extends RegionLookupFactoryBean<K, V> implements DisposableBean {
protected final Log log = LogFactory.getLog(getClass());
private String beanName;
private Cache cache;
private String name;
private boolean destroy = false;
private Resource snapshot;
@@ -61,60 +52,55 @@ public class RegionFactoryBean<K, V> implements DisposableBean, FactoryBean<Regi
private Scope scope;
private DataPolicy dataPolicy;
private Region<K, V> region;
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Cache property must be set");
name = (!StringUtils.hasText(name) ? beanName : name);
Assert.hasText(name, "Name (or beanName) property must be set");
// first get cache
region = cache.getRegion(name);
if (region != null) {
log.info("Retrieved region [" + name + "] from cache");
}
// fall back to cache creation if one is not found
else {
if (attributes != null)
AttributesFactory.validateAttributes(attributes);
AttributesFactory<K, V> attrFactory = (attributes != null ? new AttributesFactory<K, V>(attributes)
: new AttributesFactory<K, V>());
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> listener : cacheListeners) {
attrFactory.addCacheListener(listener);
}
}
if (cacheLoader != null) {
attrFactory.setCacheLoader(cacheLoader);
}
if (cacheWriter != null) {
attrFactory.setCacheWriter(cacheWriter);
}
if (dataPolicy != null) {
attrFactory.setDataPolicy(dataPolicy);
}
if (scope != null) {
attrFactory.setScope(scope);
}
postProcess(attrFactory);
region = cache.createRegion(name, attrFactory.create());
log.info("Created new cache region [" + name + "]");
if (snapshot != null) {
region.loadSnapshot(snapshot.getInputStream());
}
}
super.afterPropertiesSet();
postProcess(region);
}
@Override
protected Region<K, V> lookupFallback(Cache cache, String regionName) throws Exception {
if (attributes != null)
AttributesFactory.validateAttributes(attributes);
AttributesFactory<K, V> attrFactory = (attributes != null ? new AttributesFactory<K, V>(attributes)
: new AttributesFactory<K, V>());
if (!ObjectUtils.isEmpty(cacheListeners)) {
for (CacheListener<K, V> listener : cacheListeners) {
attrFactory.addCacheListener(listener);
}
}
if (cacheLoader != null) {
attrFactory.setCacheLoader(cacheLoader);
}
if (cacheWriter != null) {
attrFactory.setCacheWriter(cacheWriter);
}
if (dataPolicy != null) {
attrFactory.setDataPolicy(dataPolicy);
}
if (scope != null) {
attrFactory.setScope(scope);
}
postProcess(attrFactory);
Region<K, V> reg = cache.createRegion(regionName, attrFactory.create());
log.info("Created new cache region [" + regionName + "]");
if (snapshot != null) {
reg.loadSnapshot(snapshot.getInputStream());
}
return reg;
}
/**
* Post-process the attribute factory object used for configuring the region of this factory bean during the initialization process.
* The object is already initialized and configured by the factory bean before this method
@@ -144,46 +130,6 @@ public class RegionFactoryBean<K, V> implements DisposableBean, FactoryBean<Regi
region = null;
}
public Region<K, V> getObject() throws Exception {
return region;
}
public Class<?> getObjectType() {
return (region != null ? region.getClass() : Region.class);
}
public boolean isSingleton() {
return true;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/**
* Sets the cache used for creating the region.
*
* @see org.springframework.data.gemfire.CacheFactoryBean
* @param cache the cache to set
*/
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* Sets the name of the cache region. If no cache is found under
* the given name, a new one will be created.
* If no name is given, the beanName will be used.
*
* @see com.gemstone.gemfire.cache.Region#getFullPath()
* @see #setBeanName(String)
*
* @param name the region name
*/
public void setName(String name) {
this.name = name;
}
/**
* Indicates whether the region referred by this factory bean,
* will be destroyed on shutdown (default false).

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2010 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.Region;
/**
* Simple FactoryBean for retrieving generic GemFire {@link Region}s. If the region doesn't exist, an exception is thrown.
*
* For declaring and configuring new regions, see {@link RegionFactoryBean}.
*
* @author Costin Leau
*/
public class RegionLookupFactoryBean<K, V> implements FactoryBean<Region<K, V>>, InitializingBean, BeanNameAware {
protected final Log log = LogFactory.getLog(getClass());
private String beanName;
private Cache cache;
private String name;
Region<K, V> region;
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Cache property must be set");
name = (!StringUtils.hasText(name) ? beanName : name);
Assert.hasText(name, "Name (or beanName) property must be set");
region = cache.getRegion(name);
if (region != null) {
log.info("Retrieved region [" + name + "] from cache");
}
else {
region = lookupFallback(cache, name);
}
}
/**
* Fall back method in case the named region does not exist.
* By default, this implementation throws an exception.
*
* @param cache GemFire cache
* @param regionName region name
* @throws Exception
*/
protected Region<K, V> lookupFallback(Cache cache, String regionName) throws Exception {
throw new BeanInitializationException("Cannot find region named " + name + " in cache " + cache);
}
public Region<K, V> getObject() throws Exception {
return region;
}
public Class<?> getObjectType() {
return (region != null ? region.getClass() : Region.class);
}
public boolean isSingleton() {
return true;
}
public void setBeanName(String name) {
this.beanName = name;
}
/**
* Sets the cache used for creating the region.
*
* @see org.springframework.data.gemfire.CacheFactoryBean
* @param cache the cache to set
*/
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* Sets the name of the cache region. If no cache is found under
* the given name, a new one will be created.
* If no name is given, the beanName will be used.
*
* @see com.gemstone.gemfire.cache.Region#getFullPath()
* @see #setBeanName(String)
*
* @param name the region name
*/
public void setName(String name) {
this.name = name;
}
}

View File

@@ -28,7 +28,7 @@ import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.client.Pool;
/**
* Client extension for Gemfire regions.
* Client extension for GemFire regions.
*
* @author Costin Leau
*/

View File

@@ -27,6 +27,7 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("cache", new CacheParser());
registerBeanDefinitionParser("lookup-region", new LookupRegionParser());
registerBeanDefinitionParser("replicated-region", new ReplicatedRegionParser());
registerBeanDefinitionParser("partitioned-region", new PartitionedRegionParser());
registerBeanDefinitionParser("client-region", new ClientRegionParser());

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2010 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.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.gemfire.RegionFactoryBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;lookup-region;gt; definitions.
*
* @author Costin Leau
*/
class LookupRegionParser extends AbstractSingleBeanDefinitionParser {
protected Class<?> getBeanClass(Element element) {
return RegionFactoryBean.class;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
ParsingUtils.setPropertyValue(element, builder, "name", "name");
String attr = element.getAttribute("cache-ref");
// add cache reference (fallback to default if nothing is specified)
builder.addPropertyReference("cache", (StringUtils.hasText(attr) ? attr : "gemfire-cache"));
}
}

View File

@@ -75,45 +75,14 @@ The name of the bean referred by this declaration. If no reference exists, use a
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="readOnlyRegionType" abstract="true">
<xsd:sequence>
<xsd:element name="cache-listener" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation source="com.gemstone.gemfire.cache.CacheListener"><![CDATA[
A cache listener definition for this region. A cache listener handles region or entry related events (that occur after
various operations on the region). Multiple listeners can be declared in a nested manner.
Note: Avoid the risk of deadlock. Since the listener is invoked while holding a lock on the entry generating the event,
it is easy to generate a deadlock by interacting with the region. For this reason, it is highly recommended to use some
other thread for accessing the region and not waiting for it to complete its task.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CacheListener"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="skip">
<xsd:annotation>
<xsd:documentation><![CDATA[
Inner bean definition of the cache listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:any>
</xsd:sequence>
<xsd:attribute name="ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the cache listener bean referred by this declaration. Used as a convenience method. If no reference exists,
use inner bean declarations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:complexType name="basicRegionType">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.Region" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -135,6 +104,52 @@ The name of the bean defining the GemFire cache (by default 'gemfire-cache').
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="readOnlyRegionType" abstract="true">
<xsd:complexContent>
<xsd:extension base="basicRegionType">
<xsd:sequence>
<xsd:element name="cache-listener" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation source="com.gemstone.gemfire.cache.CacheListener"><![CDATA[
A cache listener definition for this region. A cache listener handles region or entry related events (that occur after
various operations on the region). Multiple listeners can be declared in a nested manner.
Note: Avoid the risk of deadlock. Since the listener is invoked while holding a lock on the entry generating the event,
it is easy to generate a deadlock by interacting with the region. For this reason, it is highly recommended to use some
other thread for accessing the region and not waiting for it to complete its task.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.gemstone.gemfire.cache.CacheListener"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="skip">
<xsd:annotation>
<xsd:documentation><![CDATA[
Inner bean definition of the cache listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:any>
</xsd:sequence>
<xsd:attribute name="ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the cache listener bean referred by this declaration. Used as a convenience method. If no reference exists,
use inner bean declarations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="regionType">
@@ -195,6 +210,17 @@ Note: Persistence for partitioned regions is supported only from GemFire 6.5 onw
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="lookup-region" type="basicRegionType">
<xsd:annotation>
<xsd:documentation><![CDATA[[
Looks up an existing, working, GemFire region. Typically regions are defined through GemFire own configuration, the
cache.xml. If the region does not exist, an exception will be thrown.
For defining regions, consider the region elements.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="replicated-region">
<xsd:annotation>
<xsd:documentation source="org.springframework.data.gemfire.RegionFactoryBean"><![CDATA[

View File

@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.RegionFactoryBean;
import org.springframework.data.gemfire.RegionLookupFactoryBean;
import org.springframework.data.gemfire.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -74,4 +75,12 @@ public class ReplicatedRegionNamespaceTest {
assertSame(context.getBean("c-loader"), TestUtils.readField("cacheLoader", fb));
assertSame(context.getBean("c-writer"), TestUtils.readField("cacheWriter", fb));
}
@Test
public void testRegionLookup() throws Exception {
assertTrue(context.containsBean("lookup"));
RegionLookupFactoryBean lfb = context.getBean("&lookup", RegionLookupFactoryBean.class);
assertEquals("simple", TestUtils.readField("name", lfb));
assertEquals(context.getBean("simple"), context.getBean("lookup"));
}
}

View File

@@ -27,4 +27,5 @@
<bean id="c-loader" class="org.springframework.data.gemfire.SimpleCacheLoader"/>
<bean id="c-writer" class="org.springframework.data.gemfire.SimpleCacheWriter"/>
<gfe:lookup-region id="lookup" name="simple"/>
</beans>