SGF-741 - Add support for enforcing Lucene Index creation before Region creation.

This commit is contained in:
John Blum
2018-05-03 23:07:30 -07:00
parent fe28a49c6e
commit 9f683b7976
10 changed files with 888 additions and 67 deletions

View File

@@ -102,9 +102,6 @@ public class IndexFactoryBean extends AbstractFactoryBeanSupport<Index> implemen
private String indexName;
private String name;
/**
* @inheritDoc
*/
@Override
public void afterPropertiesSet() throws Exception {

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2018 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.support;
import java.util.Optional;
import java.util.function.Predicate;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.query.Index;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.MethodMetadata;
import org.springframework.data.gemfire.GenericRegionFactoryBean;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.util.SpringUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The {@link AbstractDependencyStructuringBeanFactoryPostProcessor} class is a Spring {@link BeanFactoryPostProcessor}
* post processing the Spring {@link BeanFactory} to help ensure that the dependencies between different Apache Geode
* or Pivotal GemFire objects (e.g. {@link Region} and a {@link LuceneIndex} or an OQL {@link Index}) have been
* properly declared in order to the lifecycle of those components are upheld according to Apache Geode
* or Pivotal GemFire requirements/rules.
*
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @since 2.1.0
*/
@SuppressWarnings("unused")
public abstract class AbstractDependencyStructuringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
protected BeanDefinition addDependsOn(BeanDefinition beanDefinition, String... beanNames) {
return SpringUtils.addDependsOn(beanDefinition, beanNames);
}
protected Optional<Object> getPropertyValue(BeanDefinition beanDefinition, String propertyName) {
return SpringUtils.getPropertyValue(beanDefinition, propertyName);
}
protected boolean isBeanDefinitionOfType(BeanDefinition beanDefinition, Class<?> type) {
Assert.notNull(type, "Class type must not be null");
return isBeanDefinitionOfType(beanDefinition, typeName -> type.getName().equals(typeName));
}
protected boolean isBeanDefinitionOfType(BeanDefinition beanDefinition, String typeName) {
return isBeanDefinitionOfType(beanDefinition,
typeNameArgument -> String.valueOf(typeName).equals(typeNameArgument));
}
protected boolean isBeanDefinitionOfType(BeanDefinition beanDefinition, Predicate<String> typeFilter) {
return Optional.of(beanDefinition)
.map(it -> beanDefinition.getBeanClassName())
.filter(StringUtils::hasText)
.map(typeFilter::test)
.orElseGet(() ->
Optional.ofNullable(beanDefinition.getFactoryMethodName())
.filter(StringUtils::hasText)
.filter(it -> beanDefinition instanceof AnnotatedBeanDefinition)
.map(it -> ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata())
.map(MethodMetadata::getReturnTypeName)
.map(typeFilter::test)
.orElse(false)
);
}
protected boolean isClientCacheBean(BeanDefinition beanDefinition) {
return isBeanDefinitionOfType(beanDefinition, ClientCacheFactoryBean.class);
}
protected boolean isClientRegionBean(BeanDefinition beanDefinition) {
return isBeanDefinitionOfType(beanDefinition, ClientRegionFactoryBean.class);
}
protected boolean isPoolBean(BeanDefinition beanDefinition) {
return isBeanDefinitionOfType(beanDefinition, PoolFactoryBean.class);
}
protected Predicate<String> isRegionBeanType() {
Predicate<String> genericRegionBeanType =
typeName -> GenericRegionFactoryBean.class.getName().equals(typeName);
return genericRegionBeanType.or(typeName -> ClientRegionFactoryBean.class.getName().equals(typeName))
.or(typeName -> LocalRegionFactoryBean.class.getName().equals(typeName))
.or(typeName -> PartitionedRegionFactoryBean.class.getName().equals(typeName))
.or(typeName -> ReplicatedRegionFactoryBean.class.getName().equals(typeName));
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2018 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.support;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.data.gemfire.search.lucene.LuceneIndexFactoryBean;
import org.springframework.util.StringUtils;
/**
* The {@link LuceneIndexRegionBeanFactoryPostProcessor} class is a Spring {@link BeanFactoryPostProcessor} ensuring
* that a {@link LuceneIndex} is created before the {@link Region} on which the {@link LuceneIndex} is defined.
*
* @author John Blum
* @see org.apache.geode.cache.Region
* @see org.apache.geode.cache.lucene.LuceneIndex
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see org.springframework.data.gemfire.search.lucene.LuceneIndexFactoryBean
* @see org.springframework.data.gemfire.config.support.AbstractDependencyStructuringBeanFactoryPostProcessor
* @since 2.1.0
*/
@SuppressWarnings("unused")
public class LuceneIndexRegionBeanFactoryPostProcessor extends AbstractDependencyStructuringBeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<String, String> regionNameToLuceneIndexBeanName = new HashMap<>();
Map<String, String> regionNameToRegionBeanName = new HashMap<>();
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(beanName -> {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (isBeanDefinitionOfType(beanDefinition, LuceneIndexFactoryBean.class)) {
resolveRegionNameFromLuceneIndex(beanDefinition)
.ifPresent(regionName -> regionNameToLuceneIndexBeanName.put(regionName, beanName));
}
else if (isBeanDefinitionOfType(beanDefinition, isRegionBeanType())) {
resolveRegionNameFromRegionBean(beanName, beanDefinition)
.ifPresent(regionName -> regionNameToRegionBeanName.put(regionName, beanName));
}
});
regionNameToRegionBeanName.keySet().stream().forEach(regionName -> {
if (regionNameToLuceneIndexBeanName.containsKey(regionName)) {
String regionBeanName = regionNameToRegionBeanName.get(regionName);
String luceneIndexBeanName = regionNameToLuceneIndexBeanName.get(regionName);
addDependsOn(beanFactory.getBeanDefinition(regionBeanName), luceneIndexBeanName);
}
});
}
private Optional<String> resolveRegionNameFromLuceneIndex(BeanDefinition beanDefinition) {
return getPropertyValue(beanDefinition, "regionPath")
.map(this::asFullyQualifiedRegionName);
}
private Optional<String> resolveRegionNameFromRegionBean(String beanName, BeanDefinition beanDefinition) {
String regionName = asString(getPropertyValue(beanDefinition, "regionName"));
String name = asString(getPropertyValue(beanDefinition, "name"));
return Optional.ofNullable(StringUtils.hasText(regionName) ? regionName
: (StringUtils.hasText(name) ? name : beanName)).filter(StringUtils::hasText);
}
private String asFullyQualifiedRegionName(Object regionPath) {
return Optional.ofNullable(regionPath)
.map(String::valueOf)
.map(String::trim)
.map(it -> {
int index = it.lastIndexOf(Region.SEPARATOR);
return index > 0 ? it : it.substring(1);
})
.filter(StringUtils::hasText)
.orElse(null);
}
@SuppressWarnings("all")
private String asString(Optional<Object> value) {
return value.map(String::valueOf)
.map(String::trim)
.filter(StringUtils::hasText)
.orElse(null);
}
}

View File

@@ -24,6 +24,7 @@ import org.apache.geode.cache.lucene.LuceneService;
import org.apache.geode.cache.lucene.LuceneServiceProvider;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.gemfire.support.AbstractFactoryBeanSupport;
import org.springframework.util.Assert;
/**
@@ -38,7 +39,7 @@ import org.springframework.util.Assert;
* @since 1.1.0
*/
@SuppressWarnings("unused")
public class LuceneServiceFactoryBean implements FactoryBean<LuceneService>, InitializingBean {
public class LuceneServiceFactoryBean extends AbstractFactoryBeanSupport<LuceneService> implements InitializingBean {
private GemFireCache gemfireCache;
@@ -49,6 +50,7 @@ public class LuceneServiceFactoryBean implements FactoryBean<LuceneService>, Ini
*/
@Override
public void afterPropertiesSet() throws Exception {
GemFireCache gemfireCache = getCache();
Assert.state(gemfireCache != null,
@@ -83,15 +85,10 @@ public class LuceneServiceFactoryBean implements FactoryBean<LuceneService>, Ini
*/
@Override
public Class<?> getObjectType() {
return Optional.ofNullable(this.luceneService).<Class<?>>map(LuceneService::getClass).orElse(LuceneService.class);
}
/**
* @inheritDoc
*/
@Override
public boolean isSingleton() {
return true;
return Optional.ofNullable(this.luceneService)
.<Class<?>>map(LuceneService::getClass)
.orElse(LuceneService.class);
}
/**

View File

@@ -42,7 +42,6 @@ import org.springframework.util.StringUtils;
// TODO rename this utility class using a more descriptive, intuitive and meaningful name
public abstract class SpringUtils {
/* (non-Javadoc) */
public static BeanDefinition addDependsOn(BeanDefinition bean, String... beanNames) {
List<String> dependsOnList = new ArrayList<>();
@@ -54,7 +53,14 @@ public abstract class SpringUtils {
return bean;
}
/* (non-Javadoc) */
public static Optional<Object> getPropertyValue(BeanDefinition beanDefinition, String propertyName) {
return Optional.ofNullable(beanDefinition)
.map(it -> it.getPropertyValues())
.map(propertyValues -> propertyValues.getPropertyValue(propertyName))
.map(propertyValue -> propertyValue.getValue());
}
public static BeanDefinition setPropertyReference(BeanDefinition beanDefinition,
String propertyName, String beanName) {
@@ -63,7 +69,6 @@ public abstract class SpringUtils {
return beanDefinition;
}
/* (non-Javadoc) */
public static BeanDefinition setPropertyValue(BeanDefinition beanDefinition,
String propertyName, Object propertyValue) {
@@ -72,57 +77,46 @@ public abstract class SpringUtils {
return beanDefinition;
}
/* (non-Javadoc) */
public static String defaultIfEmpty(String value, String defaultValue) {
return (StringUtils.hasText(value) ? value : defaultValue);
}
/* (non-Javadoc) */
public static <T> T defaultIfNull(T value, T defaultValue) {
return Optional.ofNullable(value).orElse(defaultValue);
}
/* (non-Javadoc) */
public static <T> T defaultIfNull(T value, Supplier<T> supplier) {
return Optional.ofNullable(value).orElseGet(supplier);
}
/* (non-Javadoc) */
public static String dereferenceBean(String beanName) {
return String.format("%1$s%2$s", BeanFactory.FACTORY_BEAN_PREFIX, beanName);
}
/* (non-Javadoc) */
public static boolean equalsIgnoreNull(Object obj1, Object obj2) {
return (obj1 == null ? obj2 == null : obj1.equals(obj2));
}
/* (non-Javadoc) */
public static boolean nullOrEquals(Object obj1, Object obj2) {
return (obj1 == null || obj1.equals(obj2));
}
/* (non-Javadoc) */
public static boolean nullSafeEquals(Object obj1, Object obj2) {
return (obj1 != null && obj1.equals(obj2));
}
/* (non-Javadoc) */
public static <T> T safeGetValue(Supplier<T> valueSupplier) {
return safeGetValue(valueSupplier, (T) null);
}
/* (non-Javadoc) */
public static <T> T safeGetValue(Supplier<T> valueSupplier, T defaultValue) {
return safeGetValue(valueSupplier, (Supplier<T>) () -> defaultValue);
}
/* (non-Javadoc) */
public static <T> T safeGetValue(Supplier<T> valueSupplier, Supplier<T> defaultValueSupplier) {
return safeGetValue(valueSupplier, (Function<Throwable, T>) exception -> defaultValueSupplier.get());
}
/* (non-Javadoc) */
public static <T> T safeGetValue(Supplier<T> valueSupplier, Function<Throwable, T> exceptionHandler) {
try {
return valueSupplier.get();