Initial implementation of JIRA improvement SGF-191 enabling Spring Data GemFire to lookup and auto register all GemFire Cache Regions not explicitly defined in the Spring context as Spring beans.

This commit is contained in:
John Blum
2014-08-14 22:39:59 -07:00
committed by John Blum
parent d9b69e25db
commit 920e53794e
6 changed files with 227 additions and 35 deletions

View File

@@ -28,15 +28,19 @@ import com.gemstone.gemfire.cache.client.ClientRegionFactory;
import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
/**
* @author David Turanski
* a {@link BeanFactoryPostProcessor} to register a Client Region bean, if necessary, for each Region accessible
* A {@link BeanFactoryPostProcessor} to register a Client Region bean, if necessary, for each Region accessible
* to a Gemfire data source. If the Region is already defined, the definition will not be overridden.
*
* @author David Turanski
* @author John Blum
*/
public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor {
private static Log logger = LogFactory.getLog(GemfireDataSourcePostProcessor.class);
private final ClientCache cache;
public GemfireDataSourcePostProcessor(ClientCache cache) {
public GemfireDataSourcePostProcessor(final ClientCache cache) {
this.cache = cache;
}
@@ -46,37 +50,41 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
createClientRegions(beanFactory);
}
private void createClientRegions(ConfigurableListableBeanFactory beanFactory) {
GemfireFunctionOperations template = new GemfireOnServersFunctionTemplate(cache);
Iterable<String> regionNames = template.executeAndExtract(new ListRegionsOnServerFunction());
GemfireFunctionOperations functionTemplate = new GemfireOnServersFunctionTemplate(cache);
Iterable<String> regionNames = functionTemplate.executeAndExtract(new ListRegionsOnServerFunction());
ClientRegionFactory<?, ?> clientRegionFactory = null;
if (regionNames != null && regionNames.iterator().hasNext()) {
clientRegionFactory = cache.createClientRegionFactory(ClientRegionShortcut.PROXY);
}
for (String regionName : regionNames) {
boolean createRegion = true;
if (beanFactory.containsBean(regionName)) {
Object existingBean = beanFactory.getBean(regionName);
Assert.isTrue(beanFactory.getBean(regionName) instanceof Region, String.format(
"cannot create a ClientRegion bean named %s. A bean with this name of type %s already exists.",
Assert.isTrue(existingBean instanceof Region, String.format(
"Cannot create a ClientRegion bean named '%1$s'. A bean with this name of type '%2$s' already exists.",
regionName, existingBean.getClass().getName()));
createRegion = false;
}
if (createRegion) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("creating client region for %s", regionName));
logger.debug(String.format("Creating Client Region bean with name '%s'...", regionName));
}
beanFactory.registerSingleton(regionName, clientRegionFactory.create(regionName));
} else {
if (logger.isDebugEnabled()) {
logger.debug(String.format("a region named %s is already defined",regionName));
logger.debug(String.format("A Region named '%s' is already defined.", regionName));
}
}
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* The AutoRegionLookupBeanDefinitionParser class...
*
* @author John Blum
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.support.AbstractBeanDefinition
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
* @see org.springframework.beans.factory.xml.BeanDefinitionParser
* @see org.springframework.beans.factory.xml.ParserContext
* @see org.w3c.dom.Element
* @since 1.5.0
*/
public class AutoRegionLookupBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(final Element element, final ParserContext parserContext) {
registerAutoRegionLookupBeanPostProcessor(element, parserContext);
return null;
}
private void registerAutoRegionLookupBeanPostProcessor(final Element element, final ParserContext parserContext) {
AbstractBeanDefinition autoRegionLookupBeanPostProcessor = BeanDefinitionBuilder
.genericBeanDefinition(AutoRegionLookupBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition();
autoRegionLookupBeanPostProcessor.setSource(parserContext.extractSource(element));
BeanDefinitionReaderUtils.registerWithGeneratedName(autoRegionLookupBeanPostProcessor,
parserContext.getRegistry());
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.config;
import java.util.Collections;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.cache.Region;
/**
* The AutoRegionLookupBeanPostProcessor class is a Spring BeanPostProcessor that post processes a GemFireCache by
* registering all Cache Regions that have not been explicitly defined in the Spring application context. This is
* usually the case for Regions that have been defined in GemFire native cache.xml or defined use GemFire 8's new
* cluster-based configuration service.
*
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.Region
* @since 1.5.0
*/
public class AutoRegionLookupBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public final void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
String.format("The BeanFactory reference (%1$s) must be an instance of ConfigurableListableBeanFactory!",
ObjectUtils.nullSafeClassName(beanFactory)));
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
protected ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "A reference to the BeanFactory was not properly configured and initialized!");
return this.beanFactory;
}
@Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
if (bean instanceof GemFireCache) {
registerCacheRegionsAsBeans((GemFireCache) bean);
}
return bean;
}
private void registerCacheRegionsAsBeans(final GemFireCache cache) {
for (Region region : cache.rootRegions()) {
registerRegionAsBean(region);
}
}
private void registerRegionAsBean(final Region<?, ?> region) {
if (region != null) {
String regionBeanName = getBeanName(region);
if (!getBeanFactory().containsBean(regionBeanName)) {
getBeanFactory().registerSingleton(regionBeanName, region);
}
for (Region<?, ?> subregion : nullSafeSubregions(region)) {
registerRegionAsBean(subregion);
}
}
}
private Set<Region<?, ?>> nullSafeSubregions(final Region<?, ?> parentRegion) {
Set<Region<?, ?>> subregions = parentRegion.subregions(false);
return (subregions != null ? subregions : Collections.<Region<?, ?>>emptySet());
}
private String getBeanName(final Region region) {
String regionFullPath = region.getFullPath();
return (regionFullPath.lastIndexOf(Region.SEPARATOR) > 0 ? regionFullPath : region.getName());
}
}

View File

@@ -23,7 +23,7 @@ import org.w3c.dom.Element;
/**
* @author David Turanski
*
* @author John Blum
*/
public class GemfireDataSourceParser extends AbstractBeanDefinitionParser {
@@ -32,37 +32,38 @@ public class GemfireDataSourceParser extends AbstractBeanDefinitionParser {
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
AbstractBeanDefinition poolDefinition = (AbstractBeanDefinition) new PoolParser().parse(element, parserContext);
MutablePropertyValues poolProps = poolDefinition.getPropertyValues();
poolProps.add("name", GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
MutablePropertyValues poolProperties = poolDefinition.getPropertyValues();
poolProperties.add("name", GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
if (!element.hasAttribute("subscription-enabled")) {
poolProps.add("subscriptionEnabled", true);
poolProperties.add("subscriptionEnabled", true);
}
parserContext.getRegistry().registerBeanDefinition(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, poolDefinition);
AbstractBeanDefinition clientCacheDefinition = (AbstractBeanDefinition) new ClientCacheParser().parse(element,
parserContext);
AbstractBeanDefinition clientCacheDefinition = (AbstractBeanDefinition) new ClientCacheParser()
.parse(element, parserContext);
MutablePropertyValues props = clientCacheDefinition.getPropertyValues();
MutablePropertyValues clientCacheProperties = clientCacheDefinition.getPropertyValues();
props.add("pool", poolDefinition);
clientCacheDefinition.setPropertyValues(props);
clientCacheProperties.add("pool", poolDefinition);
parserContext.getRegistry().registerBeanDefinition(GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME,
clientCacheDefinition);
clientCacheDefinition);
System.out.println("registered " + GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME + ":"
+ clientCacheDefinition.getBeanClassName());
System.out.printf("Registered GemFire Client Cache bean '%1$s' of type '%2$s'%n",
GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME, clientCacheDefinition.getBeanClassName());
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
GemfireDataSourcePostProcessor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(GemfireDataSourcePostProcessor.class);
builder.addConstructorArgReference(GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
return null;
}

View File

@@ -25,11 +25,14 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* Namespace handler for GemFire definitions.
* Spring NamespaceHandler for GemFire XML namespace (XSD) bean definitions.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
* @see org.springframework.beans.factory.xml.NamespaceHandlerSupport
*/
@SuppressWarnings("unused")
class GemfireNamespaceHandler extends NamespaceHandlerSupport {
protected static final List<String> GEMFIRE7_ELEMENTS = Arrays.asList("async-event-queue", "gateway-sender",
@@ -48,11 +51,10 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("cache", new CacheParser());
registerBeanDefinitionParser("cache-server", new CacheServerParser());
registerBeanDefinitionParser("client-cache", new ClientCacheParser());
registerBeanDefinitionParser("client-region", new ClientRegionParser());
registerBeanDefinitionParser("client-region-template", new ClientRegionParser());
registerBeanDefinitionParser("auto-region-lookup", new AutoRegionLookupBeanDefinitionParser());
registerBeanDefinitionParser("lookup-region", new LookupRegionParser());
registerBeanDefinitionParser("region-template", new TemplateRegionParser());
registerBeanDefinitionParser("local-region", new LocalRegionParser());
@@ -63,14 +65,17 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("replicated-region-template", new ReplicatedRegionParser());
registerBeanDefinitionParser("async-event-queue", new AsyncEventQueueParser());
registerBeanDefinitionParser("disk-store", new DiskStoreParser());
registerBeanDefinitionParser("function-service", new FunctionServiceParser());
registerBeanDefinitionParser("gateway-hub", new GatewayHubParser());
registerBeanDefinitionParser("gateway-receiver", new GatewayReceiverParser());
registerBeanDefinitionParser("gateway-sender", new GatewaySenderParser());
registerBeanDefinitionParser("index", new IndexParser());
registerBeanDefinitionParser("pool", new PoolParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("cq-listener-container", new GemfireListenerContainerParser());
registerBeanDefinitionParser("function-service", new FunctionServiceParser());
registerBeanDefinitionParser("transaction-manager", new TransactionManagerParser());
registerBeanDefinitionParser("client-cache", new ClientCacheParser());
registerBeanDefinitionParser("client-region", new ClientRegionParser());
registerBeanDefinitionParser("client-region-template", new ClientRegionParser());
registerBeanDefinitionParser("cq-listener-container", new GemfireListenerContainerParser());
registerBeanDefinitionParser("pool", new PoolParser());
}
}

View File

@@ -431,6 +431,16 @@ The name of the bean referred by this declaration. If no reference exists, use a
</xsd:attribute>
</xsd:complexType>
<!-- -->
<xsd:element name="auto-region-lookup">
<xsd:annotation>
<xsd:documentation><![CDATA[
GFE namespace element enabling GemFire Cache Regions to be automatically looked up and defined as beans in the Spring
context when those Regions are defined outside of Spring config, such as in GemFire's native cache.xml or with
GemFire 8's new cluster-based configuration service.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="baseLookupRegionType">
<xsd:annotation>
<xsd:documentation><![CDATA[