SGF-408 - Provide support in the SDG XML namespace to load a pre-defined data set using GemFire Snapshot Service for development and testing purposes.
Refactored the SnapshotServiceFactoryBean with the addition of a new property (suppressInitImport) to suppress importing on initialization. Refactored the SnapshotServiceFactoryBean SnapshotServiceAdapter interface's doImport(..) and doExport(..) method signatures to varargs. Added the ComposableSnapshotFilter class implemeting the SnapshotFilter interface and Composition Design Pattern for composing multiple SnapshotFilters into one using logical operators. Cleaned up and added addition unit tests. Completed all integration testing including testing with export and import snapshot application event driven GemFire Cache Region snapshots.
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.gemstone.gemfire.cache.snapshot.SnapshotFilter;
|
||||
|
||||
/**
|
||||
* The ComposableSnapshotFilter class is a GemFire SnapshotFilter implementation of the Composition design pattern
|
||||
* allowing 2 or more SnapshotFilters to be combined by logical AND and OR operators acting as a single SnapshotFilter.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ComposableSnapshotFilter<K, V> implements SnapshotFilter<K, V> {
|
||||
|
||||
/**
|
||||
* Operator is an enumeration of logical operators (AND, OR) used to compose SnapshotFilters
|
||||
* into a boolean expression.
|
||||
*/
|
||||
protected enum Operator {
|
||||
AND,
|
||||
OR;
|
||||
|
||||
public boolean isAnd() {
|
||||
return (this == AND);
|
||||
}
|
||||
|
||||
public boolean isOr() {
|
||||
return (this == OR);
|
||||
}
|
||||
|
||||
public boolean operate(boolean leftOperand, boolean rightOperand) {
|
||||
return (isAnd() ? (leftOperand && rightOperand) : (leftOperand || rightOperand));
|
||||
}
|
||||
}
|
||||
|
||||
private final Operator operator;
|
||||
|
||||
private final SnapshotFilter<K, V> leftOperand;
|
||||
private final SnapshotFilter<K, V> rightOperand;
|
||||
|
||||
/**
|
||||
* Constructs an instance of ComposableSnapshotFilter initialized with a logical operator combining the resulting
|
||||
* boolean expressions from the evaluation of the operands.
|
||||
*
|
||||
* @param leftOperand the left operand in the boolean-based expression.
|
||||
* @param operator the right operand in the boolean-based expression.
|
||||
* @param rightOperand the operator used to combine the resulting boolean expressions
|
||||
* from the evaluation of the operands.
|
||||
* @see org.springframework.data.gemfire.ComposableSnapshotFilter.Operator
|
||||
* @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter
|
||||
*/
|
||||
private ComposableSnapshotFilter(SnapshotFilter<K, V> leftOperand, Operator operator, SnapshotFilter<K, V> rightOperand) {
|
||||
this.leftOperand = leftOperand;
|
||||
this.operator = operator;
|
||||
this.rightOperand = rightOperand;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("unchecked")
|
||||
static <K, V> SnapshotFilter<K, V>[] nullSafeArray(SnapshotFilter<K, V>... array) {
|
||||
return (array != null ? array : new SnapshotFilter[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the array of SnapshotFilters into a logical boolean expression using the specified Operator.
|
||||
*
|
||||
* @param <K> the class type of the SnapshotFilter key.
|
||||
* @param <V> the class type of the SnapshotFilter value.
|
||||
* @param operator the logical operator used to compose the SnapshotFilters.
|
||||
* @param snapshotFilters the array of SnapshotFilters to compose into a logical boolean expression
|
||||
* using the Operator.
|
||||
* @return a SnapshotFilter implementation composed of the SnapshotFilters using the specified Operator.
|
||||
* @see org.springframework.data.gemfire.ComposableSnapshotFilter.Operator
|
||||
* @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter
|
||||
*/
|
||||
protected static <K, V> SnapshotFilter<K, V> compose(Operator operator, SnapshotFilter<K, V>... snapshotFilters) {
|
||||
SnapshotFilter<K, V> composedSnapshotFilter = null;
|
||||
|
||||
for (SnapshotFilter<K, V> snapshotFilter : nullSafeArray(snapshotFilters)) {
|
||||
composedSnapshotFilter = (composedSnapshotFilter == null ? snapshotFilter
|
||||
: new ComposableSnapshotFilter<K, V>(snapshotFilter, operator, composedSnapshotFilter));
|
||||
}
|
||||
|
||||
return composedSnapshotFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the array of SnapshotFilters into a logical boolean expression using the AND Operator.
|
||||
*
|
||||
* @param <K> the class type of the SnapshotFilter key.
|
||||
* @param <V> the class type of the SnapshotFilter value.
|
||||
* @param snapshotFilters the array of SnapshotFilters to compose into a logical boolean expression
|
||||
* using the AND Operator.
|
||||
* @return a SnapshotFilter implementation composed of the SnapshotFilters using the AND Operator.
|
||||
* @see org.springframework.data.gemfire.ComposableSnapshotFilter.Operator#AND
|
||||
* @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter
|
||||
*/
|
||||
public static <K, V> SnapshotFilter<K, V> and(SnapshotFilter<K, V>... snapshotFilters) {
|
||||
return compose(Operator.AND, snapshotFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the array of SnapshotFilters into a logical boolean expression using the OR Operator.
|
||||
*
|
||||
* @param <K> the class type of the SnapshotFilter key.
|
||||
* @param <V> the class type of the SnapshotFilter value.
|
||||
* @param snapshotFilters the array of SnapshotFilters to compose into a logical boolean expression
|
||||
* using the OR Operator.
|
||||
* @return a SnapshotFilter implementation composed of the SnapshotFilters using the OR Operator.
|
||||
* @see org.springframework.data.gemfire.ComposableSnapshotFilter.Operator#OR
|
||||
* @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter
|
||||
*/
|
||||
public static <K, V> SnapshotFilter<K, V> or(SnapshotFilter<K, V>... snapshotFilters) {
|
||||
return compose(Operator.OR, snapshotFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the following Map Entry is accepted by this composed SnapshotFilter implementation.
|
||||
*
|
||||
* @param entry the Map.Entry to evaluate.
|
||||
* @return a boolean value indicating whether this composed SnapshotFilter accepts the Map Entry.
|
||||
* @see org.springframework.data.gemfire.ComposableSnapshotFilter.Operator
|
||||
* @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter#accept(Map.Entry)
|
||||
* @see java.util.Map.Entry
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(final Map.Entry<K, V> entry) {
|
||||
return operator.operate(leftOperand.accept(entry), rightOperand.accept(entry));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.data.gemfire;
|
||||
|
||||
import static com.gemstone.gemfire.cache.snapshot.SnapshotOptions.SnapshotFormat;
|
||||
import static org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
@@ -52,24 +53,27 @@ import com.gemstone.gemfire.cache.snapshot.SnapshotOptions;
|
||||
|
||||
/**
|
||||
* The SnapshotServiceFactoryBean class is a Spring FactoryBean used to configure and create an instance
|
||||
* of the appropriate GemFire Snapshot Service. A CacheSnapshotService is created if the Region is not specified,
|
||||
* otherwise a RegionSnapshotService is used based on the configured Region.
|
||||
* of an appropriate GemFire Snapshot Service to perform data import and exports. A CacheSnapshotService is created
|
||||
* if the Region is not specified, otherwise a RegionSnapshotService is used based on the configured Region.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
* @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter
|
||||
* @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService
|
||||
* @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotServiceFactoryBean.SnapshotServiceAdapter<K, V>>,
|
||||
public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotServiceAdapter<K, V>>,
|
||||
InitializingBean, DisposableBean, ApplicationListener<SnapshotApplicationEvent<K, V>> {
|
||||
|
||||
protected static final SnapshotMetadata[] EMPTY_ARRAY = new SnapshotMetadata[0];
|
||||
|
||||
private Boolean suppressInitImport;
|
||||
|
||||
private Cache cache;
|
||||
|
||||
private Region<K, V> region;
|
||||
@@ -185,6 +189,27 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean condition to indicate whether importing on initialization should be suppressed.
|
||||
*
|
||||
* @param suppressInitImport a Boolean value to indicate whether importing on initialization should be suppressed.
|
||||
* @see #isSuppressInitImport()
|
||||
*/
|
||||
public void setSuppressInitImport(Boolean suppressInitImport) {
|
||||
this.suppressInitImport = suppressInitImport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether importing on initialization should be suppressed.
|
||||
*
|
||||
* @return a boolean value indicating whether import on initialization should be suppressed.
|
||||
* @see #setSuppressInitImport(Boolean)
|
||||
* @see #afterPropertiesSet()
|
||||
*/
|
||||
protected boolean isSuppressInitImport() {
|
||||
return Boolean.TRUE.equals(suppressInitImport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reference to the GemFire Snapshot Service created by this FactoryBean.
|
||||
*
|
||||
@@ -233,7 +258,10 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
snapshotServiceAdapter = create();
|
||||
snapshotServiceAdapter.doImport(getImports());
|
||||
|
||||
if (!isSuppressInitImport()) {
|
||||
snapshotServiceAdapter.doImport(getImports());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,10 +361,7 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
* @see org.springframework.data.gemfire.SnapshotApplicationEvent
|
||||
*/
|
||||
protected boolean isMatch(SnapshotApplicationEvent event) {
|
||||
Region region = getRegion();
|
||||
|
||||
return ((event.isRegionSnapshotEvent() && event.matches(region))
|
||||
|| (event.isCacheSnapshotEvent() && region == null));
|
||||
return (event.isCacheSnapshotEvent() || event.matches(getRegion()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,9 +393,9 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
|
||||
SnapshotOptions<K, V> createOptions();
|
||||
|
||||
void doExport(SnapshotMetadata<K, V>[] configurations);
|
||||
void doExport(SnapshotMetadata<K, V>... configurations);
|
||||
|
||||
void doImport(SnapshotMetadata<K, V>[] configurations);
|
||||
void doImport(SnapshotMetadata<K, V>... configurations);
|
||||
|
||||
void load(File directory, SnapshotFormat format);
|
||||
|
||||
@@ -392,6 +417,8 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
*/
|
||||
protected static abstract class SnapshotServiceAdapterSupport<K, V> implements SnapshotServiceAdapter<K, V> {
|
||||
|
||||
protected static final File TEMPORARY_DIRECTORY = new File(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
protected final Log log = createLog();
|
||||
|
||||
Log createLog() {
|
||||
@@ -408,14 +435,14 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doExport(SnapshotMetadata<K, V>[] configurations) {
|
||||
public void doExport(SnapshotMetadata<K, V>... configurations) {
|
||||
for (SnapshotMetadata<K, V> configuration : nullSafeArray(configurations)) {
|
||||
save(configuration.getLocation(), configuration.getFormat(), createOptions(configuration.getFilter()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doImport(SnapshotMetadata<K, V>[] configurations) {
|
||||
public void doImport(SnapshotMetadata<K, V>... configurations) {
|
||||
for (SnapshotMetadata<K, V> configuration : nullSafeArray(configurations)) {
|
||||
load(configuration.getFormat(), createOptions(configuration.getFilter()), handleLocation(configuration));
|
||||
}
|
||||
@@ -434,8 +461,7 @@ public class SnapshotServiceFactoryBean<K, V> implements FactoryBean<SnapshotSer
|
||||
protected File[] handleFileLocation(File file) {
|
||||
if (ArchiveFileFilter.INSTANCE.accept(file)) {
|
||||
try {
|
||||
File extractedArchiveDirectory = new File(System.getProperty("java.io.tmpdir"),
|
||||
file.getName().replaceAll("\\.", "-"));
|
||||
File extractedArchiveDirectory = new File(TEMPORARY_DIRECTORY, file.getName().replaceAll("\\.", "-"));
|
||||
|
||||
Assert.state(extractedArchiveDirectory.isDirectory() || extractedArchiveDirectory.mkdirs(),
|
||||
String.format("Failed create directory (%1$s) in which to extract archive (%2$s)",
|
||||
|
||||
@@ -52,7 +52,9 @@ abstract class ParsingUtils {
|
||||
private static final Log log = LogFactory.getLog(ParsingUtils.class);
|
||||
|
||||
protected static final String CACHE_PROPERTY_NAME = "cache";
|
||||
protected static final String REGION_PROPERTY_NAME = "region";
|
||||
protected static final String CACHE_REF_ATTRIBUTE_NAME = "cache-ref";
|
||||
protected static final String REGION_REF_ATTRIBUTE_NAME = "region-ref";
|
||||
|
||||
static void setPropertyReference(Element element, BeanDefinitionBuilder builder, String attributeName,
|
||||
String propertyName) {
|
||||
@@ -411,7 +413,8 @@ abstract class ParsingUtils {
|
||||
Element expirationElement = DomUtils.getChildElementByTagName(rootElement, elementName);
|
||||
|
||||
if (expirationElement != null) {
|
||||
Object customExpiry = parseRefOrSingleNestedBeanDeclaration(parserContext, expirationElement, regionAttributesBuilder);
|
||||
Object customExpiry = parseRefOrSingleNestedBeanDeclaration(parserContext, expirationElement,
|
||||
regionAttributesBuilder);
|
||||
regionAttributesBuilder.addPropertyValue(propertyName, customExpiry);
|
||||
return true;
|
||||
}
|
||||
@@ -442,4 +445,12 @@ abstract class ParsingUtils {
|
||||
return (StringUtils.hasText(cacheReference) ? cacheReference : GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
|
||||
}
|
||||
|
||||
static void setRegionReference(Element element, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyReference(REGION_PROPERTY_NAME, resolveRegionReference(element));
|
||||
}
|
||||
|
||||
static String resolveRegionReference(Element element) {
|
||||
return element.getAttribute(REGION_REF_ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ class SnapshotServiceParser extends AbstractSingleBeanDefinitionParser {
|
||||
super.doParse(element, parserContext, builder);
|
||||
|
||||
ParsingUtils.setCacheReference(element, builder);
|
||||
ParsingUtils.setPropertyReference(element, builder, "region-ref", "region");
|
||||
ParsingUtils.setRegionReference(element, builder);
|
||||
ParsingUtils.setPropertyValue(element, builder, "suppress-init-import");
|
||||
builder.addPropertyValue("exports", parseExports(element, parserContext));
|
||||
builder.addPropertyValue("imports", parseImports(element, parserContext));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user