SGF-18
+ added basic lookup factory bean + refactored existing schema + added namespace parsers + added integration tests
This commit is contained in:
@@ -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).
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 <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"));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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[
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user