DATAGEODE-231 - The 'gemfireDataSourcePostProcessor' bean should be a BeanPostProcessor.

Also, under no circumstances should the BeanPostProcessor have a dependency on the GemFireCache!
This commit is contained in:
John Blum
2019-09-19 20:40:03 -07:00
parent ae944c3893
commit 71dd7b5d2f
13 changed files with 422 additions and 202 deletions

View File

@@ -29,11 +29,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.data.gemfire.client.function.ListRegionsOnServerFunction;
import org.springframework.data.gemfire.function.execution.GemfireOnServersFunctionTemplate;
import org.springframework.util.Assert;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
@@ -56,40 +60,43 @@ import org.springframework.util.ObjectUtils;
* @see ListRegionsOnServerFunction
* @since 1.2.0
*/
public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor {
public class GemfireDataSourcePostProcessor implements BeanFactoryAware, BeanPostProcessor {
private static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT = ClientRegionShortcut.PROXY;
private final ClientCache clientCache;
private ClientRegionShortcut clientRegionShortcut;
private ClientRegionShortcut clientRegionShortcut = DEFAULT_CLIENT_REGION_SHORTCUT;
private ConfigurableBeanFactory beanFactory;
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* Constructs an instance of the {@link GemfireDataSourcePostProcessor} {@link BeanFactoryPostProcessor} class
* initialized * with the specified {@link ClientCache} instance for creating client {@link Region Regions}
* for all data {@link Region Regions} configured in the cluster.
* Set a reference to the {@link BeanFactory}.
*
* @param clientCache reference to the {@link ClientCache} instance.
* @throws IllegalArgumentException if {@link ClientCache} is {@literal null}.
* @see org.apache.geode.cache.client.ClientCache
* @param beanFactory reference to the {@link BeanFactory}.
* @throws BeansException if the {@link BeanFactory} is not a {@link ConfigurableBeanFactory}.
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
* @see org.springframework.beans.factory.BeanFactory
*/
public GemfireDataSourcePostProcessor(ClientCache clientCache) {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.notNull(clientCache, "ClientCache must not be null");
this.clientCache = clientCache;
if (beanFactory instanceof ConfigurableBeanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
else {
throw new TypeMismatchException(beanFactory, ConfigurableBeanFactory.class);
}
}
/**
* Returns a reference to the {@link ClientCache}.
* Returns a reference to the configured {@link ConfigurableBeanFactory}.
*
* @return a reference to the {@link ClientCache}.
* @see org.apache.geode.cache.client.ClientCache
* @return a reference to the configured {@link ConfigurableBeanFactory}.
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
*/
protected ClientCache getClientCache() {
return this.clientCache;
public Optional<ConfigurableBeanFactory> getBeanFactory() {
return Optional.ofNullable(this.beanFactory);
}
/**
@@ -115,6 +122,17 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
return Optional.ofNullable(this.clientRegionShortcut);
}
/**
* Resolves the {@link ClientRegionShortcut} used to configure and create client {@link Region Regions}.
*
* @return the resolved {@link ClientRegionShortcut}.
* @see org.apache.geode.cache.client.ClientRegionShortcut
* @see #getClientRegionShortcut()
*/
protected ClientRegionShortcut resolveClientRegionShortcut() {
return getClientRegionShortcut().orElse(DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
* Returns a reference to the configured {@link Logger} used to log messages.
*
@@ -125,27 +143,31 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
return this.logger;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* #postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
createClientProxyRegions(beanFactory, regionNames());
@Nullable @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ClientCache) {
ClientCache clientCache = (ClientCache) bean;
getBeanFactory().ifPresent(it -> createClientProxyRegions(it, clientCache, regionNames(clientCache)));
}
return bean;
}
// TODO: remove this logic and delegate to o.s.d.g.config.remote.GemfireAdminOperations
Iterable<String> regionNames() {
Iterable<String> regionNames(ClientCache clientCache) {
try {
return execute(new ListRegionsOnServerFunction());
return execute(clientCache, new ListRegionsOnServerFunction());
}
catch (Exception ignore) {
try {
Object results = execute(new GetRegionsFunction());
Object results = execute(clientCache, new GetRegionsFunction());
List<String> regionNames = Collections.emptyList();
@@ -163,15 +185,14 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
return regionNames;
}
catch (Exception cause) {
logDebug("Failed to determine the Regions available on the Server: %n%1$s", cause);
logDebug("Failed to determine the Regions available on the Server: %n%s", cause);
return Collections.emptyList();
}
}
}
@SuppressWarnings("unchecked")
<T> T execute(Function gemfireFunction, Object... arguments) {
return new GemfireOnServersFunctionTemplate(getClientCache()).executeAndExtract(gemfireFunction, arguments);
<T> T execute(ClientCache clientCache, Function gemfireFunction, Object... arguments) {
return new GemfireOnServersFunctionTemplate(clientCache).executeAndExtract(gemfireFunction, arguments);
}
boolean containsRegionInformation(Object results) {
@@ -180,15 +201,15 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
&& ((Object[]) results)[0] instanceof RegionInformation;
}
void createClientProxyRegions(ConfigurableListableBeanFactory beanFactory, Iterable<String> regionNames) {
void createClientProxyRegions(ConfigurableBeanFactory beanFactory, ClientCache clientCache,
Iterable<String> regionNames) {
if (regionNames.iterator().hasNext()) {
ClientRegionShortcut resolvedClientRegionShortcut = getClientRegionShortcut()
.orElse(DEFAULT_CLIENT_REGION_SHORTCUT);
ClientRegionShortcut resolvedClientRegionShortcut = resolveClientRegionShortcut();
ClientRegionFactory<?, ?> clientRegionFactory =
this.clientCache.createClientRegionFactory(resolvedClientRegionShortcut);
clientCache.createClientRegionFactory(resolvedClientRegionShortcut);
for (String regionName : regionNames) {
@@ -235,7 +256,16 @@ public class GemfireDataSourcePostProcessor implements BeanFactoryPostProcessor
}
public GemfireDataSourcePostProcessor using(ClientRegionShortcut clientRegionShortcut) {
setClientRegionShortcut(clientRegionShortcut);
return this;
}
public GemfireDataSourcePostProcessor using(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
return this;
}
}

View File

@@ -45,8 +45,10 @@ public class ListRegionsOnServerFunction implements Function {
* @see org.apache.geode.cache.execute.Function#execute(org.apache.geode.cache.execute.FunctionContext)
*/
@Override
@SuppressWarnings("unchecked")
public void execute(FunctionContext functionContext) {
List<String> regionNames = new ArrayList<String>();
List<String> regionNames = new ArrayList<>();
for (Region<?, ?> region : getCache().rootRegions()) {
regionNames.add(region.getName());
@@ -55,7 +57,6 @@ public class ListRegionsOnServerFunction implements Function {
functionContext.getResultSender().lastResult(regionNames);
}
/* (non-Javadoc) */
Cache getCache() {
return CacheFactory.getAnyInstance();
}

View File

@@ -23,15 +23,19 @@ import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.shiro.util.Assert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.client.GemfireDataSourcePostProcessor;
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.data.gemfire.util.CacheUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* The {@link ClusterDefinedRegionsConfiguration} class configures client Proxy-based {@link Region Regions}
@@ -83,10 +87,17 @@ public class ClusterDefinedRegionsConfiguration extends AbstractAnnotationConfig
}
@Bean
public GemfireDataSourcePostProcessor gemfireDataSourcePostProcessor(GemFireCache gemfireCache) {
@Order(Ordered.HIGHEST_PRECEDENCE + 1000000)
public GemfireDataSourcePostProcessor gemfireDataSourcePostProcessor() {
return new GemfireDataSourcePostProcessor().using(getBeanFactory()).using(resolveClientRegionShortcut());
}
Assert.isTrue(CacheUtils.isClient(gemfireCache), "GemFireCache must be an instance of ClientCache");
@Bean
Object nullCacheDependentBean(GemFireCache cache) {
return new GemfireDataSourcePostProcessor((ClientCache) gemfireCache).using(resolveClientRegionShortcut());
Assert.isTrue(CacheUtils.isClient(cache), String.format("GemFireCache [%s] must be a %s",
ObjectUtils.nullSafeClassName(cache), ClientCache.class.getName()));
return null;
}
}

View File

@@ -10,7 +10,6 @@
* 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.xml;
import org.slf4j.Logger;
@@ -18,23 +17,33 @@ import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanFactory;
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.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.gemfire.client.GemfireDataSourcePostProcessor;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
/**
* Bean definition parser for the &lt;gfe-data:datasource&gt; SDG XML namespace (XSD) element.
*
* @author David Turanski
* @author John Blum
* @see org.w3c.dom.Element
* @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.AbstractBeanDefinitionParser
* @see org.springframework.beans.factory.xml.ParserContext
* @see org.springframework.data.gemfire.client.GemfireDataSourcePostProcessor
* @see ClientCacheParser
* @see PoolParser
* @see org.springframework.data.gemfire.config.xml.ClientCacheParser
* @see org.springframework.data.gemfire.config.xml.PoolParser
*/
class GemfireDataSourceParser extends AbstractBeanDefinitionParser {
@@ -51,11 +60,12 @@ class GemfireDataSourceParser extends AbstractBeanDefinitionParser {
parseAndRegisterClientCache(element, parserContext);
parseAndRegisterPool(element, parserContext);
registerGemFireDataSourcePostProcessor(parserContext);
registerGemFireDataSourceBeanPostProcessor(parserContext);
return null;
}
@SuppressWarnings("all")
private void parseAndRegisterClientCache(Element element, ParserContext parserContext) {
BeanDefinition clientCacheDefinition = new ClientCacheParser().parse(element, parserContext);
@@ -69,6 +79,7 @@ class GemfireDataSourceParser extends AbstractBeanDefinitionParser {
}
}
@SuppressWarnings("all")
private void parseAndRegisterPool(Element element, ParserContext parserContext) {
BeanDefinition poolDefinition = new PoolParser().parse(element, parserContext);
@@ -82,13 +93,29 @@ class GemfireDataSourceParser extends AbstractBeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME, poolDefinition);
}
private void registerGemFireDataSourcePostProcessor(ParserContext parserContext) {
private void registerGemFireDataSourceBeanPostProcessor(@NonNull ParserContext parserContext) {
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(GemfireDataSourcePostProcessor.class);
BeanFactory beanFactory = resolveBeanFactory(parserContext);
builder.addConstructorArgReference(GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
if (beanFactory != null) {
BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(GemfireDataSourcePostProcessor.class);
builder.addPropertyValue("beanFactory", beanFactory);
BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
}
}
private @Nullable BeanFactory resolveBeanFactory(@NonNull ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
return registry instanceof ConfigurableApplicationContext
? ((ConfigurableApplicationContext) registry).getBeanFactory()
: registry instanceof BeanFactory
? (BeanFactory) registry
: null;
}
}

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.repository.config;
import java.lang.annotation.Annotation;
@@ -23,6 +22,7 @@ import java.util.Collections;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.data.gemfire.config.xml.GemfireConstants;
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
import org.springframework.data.gemfire.mapping.annotation.Region;
import org.springframework.data.gemfire.repository.GemfireRepository;
@@ -89,6 +89,18 @@ public class GemfireRepositoryConfigurationExtension extends RepositoryConfigura
return GemfireRepositoryFactoryBean.class.getName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(BeanDefinitionBuilder, RepositoryConfigurationSource)
*/
@Override
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
super.postProcess(builder, source);
builder.addPropertyReference("cache", GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource)

View File

@@ -17,9 +17,11 @@ package org.springframework.data.gemfire.repository.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -28,6 +30,7 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.beans.BeansException;
@@ -73,6 +76,8 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
private ApplicationContext applicationContext;
private GemFireCache cache;
private Iterable<Region<?, ?>> regions;
private MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> mappingContext;
@@ -96,14 +101,19 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
* @see org.springframework.context.ApplicationContext
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.regions = Collections.unmodifiableSet(applicationContext.getBeansOfType(Region.class).entrySet().stream()
Map<String, Region> regionBeans = applicationContext.getBeansOfType(Region.class);
Set<Region<?, ?>> regions = new HashSet<>(Collections.unmodifiableSet(regionBeans.entrySet().stream()
.<Region<?, ?>>map(Map.Entry::getValue)
.collect(Collectors.toSet()));
.collect(Collectors.toSet())));
getCache().map(GemFireCache::rootRegions).ifPresent(regions::addAll);
this.regions = regions;
}
/**
@@ -117,6 +127,26 @@ public class GemfireRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
return Optional.ofNullable(this.applicationContext);
}
/**
* Set a reference to the {@link GemFireCache}.
*
* @param cache reference to the {@link GemFireCache}.
* @see org.apache.geode.cache.GemFireCache
*/
public void setCache(GemFireCache cache) {
this.cache = cache;
}
/**
* Returns an {@link Optional} reference to the configured {@link GemFireCache}.
*
* @return an {@link Optional} reference to the configured {@link GemFireCache}.
* @see org.apache.geode.cache.GemFireCache
*/
protected Optional<GemFireCache> getCache() {
return Optional.ofNullable(this.cache);
}
/**
* Configures the {@link MappingContext} used to perform application domain object type to data store mappings.
*