From 022c68cdd1e41f4ef055ebecbcbd3746fdab0057 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 19 Aug 2015 22:29:31 -0700 Subject: [PATCH] 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. Initial implementation and unit tests. --- .../gemfire/SnapshotServiceFactoryBean.java | 497 ++++++++++++++++++ .../config/GemfireDataNamespaceHandler.java | 1 + .../data/gemfire/config/ParsingUtils.java | 17 +- .../gemfire/config/SnapshotServiceParser.java | 90 ++++ .../config/spring-data-gemfire-1.7.xsd | 61 ++- .../SnapshotServiceFactoryBeanTest.java | 295 +++++++++++ 6 files changed, 957 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java create mode 100644 src/main/java/org/springframework/data/gemfire/config/SnapshotServiceParser.java create mode 100644 src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java diff --git a/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java b/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java new file mode 100644 index 00000000..4ee4dbf3 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java @@ -0,0 +1,497 @@ +/* + * 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 static com.gemstone.gemfire.cache.snapshot.SnapshotOptions.SnapshotFormat; + +import java.io.File; +import java.util.Arrays; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.snapshot.CacheSnapshotService; +import com.gemstone.gemfire.cache.snapshot.RegionSnapshotService; +import com.gemstone.gemfire.cache.snapshot.SnapshotFilter; +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. + * + * @author John Blum + * @see org.springframework.beans.factory.DisposableBean + * @see org.springframework.beans.factory.FactoryBean + * @see org.springframework.beans.factory.InitializingBean + * @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService + * @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService + * @since 1.7.0 + */ +@SuppressWarnings("unused") +public class SnapshotServiceFactoryBean implements FactoryBean>, + InitializingBean, DisposableBean { + + protected static final SnapshotMetadata[] EMPTY_ARRAY = new SnapshotMetadata[0]; + + private Cache cache; + + private Region region; + + private SnapshotServiceAdapter snapshotServiceAdapter; + + private SnapshotMetadata[] exports; + private SnapshotMetadata[] imports; + + /** + * Sets a reference to the GemFire Cache for which the snapshot will be taken. + * + * @param cache the GemFire Cache used to create an instance of CacheSnapshotService. + * @throws IllegalArgumentException if the Cache reference is null. + * @see com.gemstone.gemfire.cache.Cache + * @see #getCache() + */ + public void setCache(Cache cache) { + Assert.notNull(cache, "The GemFire Cache must not be null"); + this.cache = cache; + } + + /** + * Gets a reference to the GemFire Cache for which the snapshot will be taken. + * + * @return the GemFire Cache used to create an instance of CacheSnapshotService. + * @throws IllegalStateException if the Cache argument is null. + * @see com.gemstone.gemfire.cache.Cache + * @see #setCache(Cache) + */ + protected Cache getCache() { + Assert.state(cache != null, "The GemFire Cache was not properly initialized"); + return cache; + } + + public void setExports(SnapshotMetadata[] exports) { + this.exports = exports; + } + + protected SnapshotMetadata[] getExports() { + return nullSafeArray(exports); + } + + public void setImports(SnapshotMetadata[] imports) { + this.imports = imports; + } + + protected SnapshotMetadata[] getImports() { + return nullSafeArray(imports); + } + + /** + * Sets a reference to the GemFire Region for which the snapshot will be taken. + * + * @param region the GemFire Region used to create an instance of the RegionSnapshotService. + * @see com.gemstone.gemfire.cache.Region + * @see #getRegion() + */ + public void setRegion(Region region) { + this.region = region; + } + + /** + * Gets a reference to the GemFire Region for which the snapshot will be taken. + * + * @return the GemFire Region used to create an instance of the RegionSnapshotService. + * @see com.gemstone.gemfire.cache.Region + * @see #getRegion() + */ + protected Region getRegion() { + return region; + } + + /** + * Gets the reference to the GemFire Snapshot Service created by this FactoryBean. + * + * @return the GemFire Snapshot Service created by this FactoryBean. + * @throws Exception if the GemFire Snapshot Service failed to be created. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter + */ + @Override + @SuppressWarnings("unchecked") + public SnapshotServiceAdapter getObject() throws Exception { + return snapshotServiceAdapter; + } + + /** + * Gets the type of Snapshot Service created by this FactoryBean. + * + * @return a Class object representing the type of Snapshot Service created by this FactoryBean. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter + */ + @Override + public Class getObjectType() { + return (snapshotServiceAdapter != null ? snapshotServiceAdapter.getClass() : SnapshotServiceAdapter.class); + } + + /** + * Determines this this FactoryBean creates single GemFire Snapshot Service instances. + * + * @return true. + */ + @Override + public boolean isSingleton() { + return true; + } + + /** + * Constructs and initializes the GemFire Snapshot Service used to take a snapshot of the configured Cache + * or Region if initialized. In addition, this initialization method will perform the actual import. + * + * @throws Exception if the construction and initialization of the GemFire Snapshot Service fails. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter#doImport(SnapshotMetadata[]) + * @see #getImports() + * @see #create() + */ + @Override + @SuppressWarnings("unchecked") + public void afterPropertiesSet() throws Exception { + snapshotServiceAdapter = create(); + snapshotServiceAdapter.doImport(getImports()); + } + + /** + * Constructs an appropriate instance of the SnapshotServiceAdapter based on the FactoryBean configuration. If + * a Region has not been specified, then a GemFire Snapshot Service for the Cache is constructed, otherwise + * the GemFire Snapshot Service for the configured Region is used. + * + * @return a SnapshotServiceAdapter wrapping the appropriate GemFire Snapshot Service (either Cache or Region) + * depending on the FactoryBean configuration. + * @see #wrap(CacheSnapshotService) + * @see #wrap(RegionSnapshotService) + * @see #getRegion() + */ + protected SnapshotServiceAdapter create() { + Region region = getRegion(); + return (region != null ? wrap(region.getSnapshotService()) : wrap(getCache().getSnapshotService())); + } + + /** + * Wraps the GemFire CacheSnapshotService into an appropriate Adapter to ... + * + * @param cacheSnapshotService the GemFire CacheSnapshotService to wrap. + * @return a SnapshotServiceAdapter wrapping the GemFire CacheSnapshotService. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter + * @see com.gemstone.gemfire.cache.snapshot.CacheSnapshotService + */ + protected SnapshotServiceAdapter wrap(CacheSnapshotService cacheSnapshotService) { + return new CacheSnapshotServiceAdapter(cacheSnapshotService); + } + + /** + * Wraps the GemFire RegionSnapshotService into an appropriate Adapter to ... + * + * @param regionSnapshotService the GemFire RegionSnapshotService to wrap. + * @return a SnapshotServiceAdapter wrapping the GemFire RegionSnapshotService. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter + * @see com.gemstone.gemfire.cache.snapshot.RegionSnapshotService + */ + protected SnapshotServiceAdapter wrap(RegionSnapshotService regionSnapshotService) { + return new RegionSnapshotServiceAdapter(regionSnapshotService); + } + + /* (non-Javadoc) */ + @SuppressWarnings("unchecked") + protected SnapshotMetadata[] nullSafeArray(SnapshotMetadata[] configurations) { + return (configurations != null ? configurations : EMPTY_ARRAY); + } + + /** + * Performs an export of the GemFire Cache or Region if configured. + * + * @throws Exception if the Cache/Region data export operation fails. + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean.SnapshotServiceAdapter#doExport(SnapshotMetadata[]) + * @see #getExports() + * @see #getObject() + */ + @Override + public void destroy() throws Exception { + getObject().doExport(getExports()); + } + + public interface SnapshotServiceAdapter { + + SnapshotOptions createOptions(); + + void doExport(SnapshotMetadata[] configurations); + + void doImport(SnapshotMetadata[] configurations); + + void load(File directory, SnapshotFormat format); + + void load(SnapshotFormat format, SnapshotOptions options, File... snapshots); + + void save(File location, SnapshotFormat format); + + void save(File location, SnapshotFormat format, SnapshotOptions options); + + } + + protected class CacheSnapshotServiceAdapter implements SnapshotServiceAdapter { + + private final CacheSnapshotService snapshotService; + + public CacheSnapshotServiceAdapter(CacheSnapshotService snapshotService) { + Assert.notNull(snapshotService, "The backing CacheSnapshotService must not be null"); + this.snapshotService = snapshotService; + } + + protected CacheSnapshotService getSnapshotService() { + return snapshotService; + } + + @Override + public SnapshotOptions createOptions() { + return getSnapshotService().createOptions(); + } + + protected SnapshotOptions createOptions(SnapshotFilter filter) { + return createOptions().setFilter(filter); + } + + @Override + public void doExport(SnapshotMetadata[] configurations) { + if (!ObjectUtils.isEmpty(configurations)) { + for (SnapshotMetadata configuration : configurations) { + save(configuration.getLocation(), configuration.getFormat(), + createOptions(configuration.getFilter())); + } + } + } + + @Override + public void doImport(SnapshotMetadata[] configurations) { + if (!ObjectUtils.isEmpty(configurations)) { + for (SnapshotMetadata configuration : configurations) { + if (configuration.isDirectory()) { + load(configuration.getLocation(), configuration.getFormat()); + } + else { + load(configuration.getFormat(), createOptions(configuration.getFilter()), + configuration.getLocation()); + } + } + } + } + + @Override + public void load(File directory, SnapshotFormat format) { + try { + getSnapshotService().load(directory, format); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to load snapshots from directory (%1$s) in format (%2$s)", directory, format), t); + } + } + + @Override + public void load(SnapshotFormat format, SnapshotOptions options, + File... snapshots) { + try { + getSnapshotService().load(snapshots, format, options); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to load snapshots (%1$s) in format (%2$s) with options (%3$s)", + Arrays.toString(snapshots), format, options), t); + } + } + + @Override + public void save(File directory, SnapshotFormat format) { + try { + getSnapshotService().save(directory, format); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to save snapshots to directory (%1$s) using format (%2$s)", directory, format), t); + } + } + + @Override + public void save(File directory, SnapshotFormat format, + SnapshotOptions options) { + try { + getSnapshotService().save(directory, format, options); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to save snapshots to directory (%1$s) using format (%2$s) with options (%3$s)", + directory, format, options), t); + } + } + } + + protected class RegionSnapshotServiceAdapter implements SnapshotServiceAdapter { + + private final RegionSnapshotService snapshotService; + + public RegionSnapshotServiceAdapter(RegionSnapshotService snapshotService) { + Assert.notNull(snapshotService, "The backing RegionSnapshotService must not be null"); + this.snapshotService = snapshotService; + } + + protected RegionSnapshotService getSnapshotService() { + return snapshotService; + } + + @Override + public SnapshotOptions createOptions() { + return getSnapshotService().createOptions(); + } + + protected SnapshotOptions createOptions(SnapshotFilter filter) { + return createOptions().setFilter(filter); + } + + @Override + public void doExport(final SnapshotMetadata[] configurations) { + if (!ObjectUtils.isEmpty(configurations)) { + for (SnapshotMetadata configuration : configurations) { + Assert.isTrue(configuration.isFile(), String.format("The export location (%1$s) must be a file", + configuration.getLocation())); + save(configuration.getLocation(), configuration.getFormat(), createOptions(configuration.getFilter())); + } + } + } + + @Override + public void doImport(final SnapshotMetadata[] configurations) { + if (!ObjectUtils.isEmpty(configurations)) { + for (SnapshotMetadata configuration : configurations) { + Assert.isTrue(configuration.isFile(), String.format("The import location (%1$s) must be a file", + configuration.getLocation())); + load(configuration.getFormat(), createOptions(configuration.getFilter()), configuration.getLocation()); + } + } + } + + @Override + public void load(File snapshot, SnapshotFormat format) { + try { + getSnapshotService().load(snapshot, format); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to load snapshot from file (%1$s) in format (%2$s)", snapshot, format), t); + } + } + + @Override + public void load(SnapshotFormat format, SnapshotOptions options, File... snapshots) { + try { + for (File snapshot : snapshots) { + getSnapshotService().load(snapshot, format, options); + } + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to load snapshots (%1$s) in format (%2$s) with options (%3$s)", + Arrays.toString(snapshots), format, options), t); + } + } + + @Override + public void save(File snapshot, SnapshotFormat format) { + try { + getSnapshotService().save(snapshot, format); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to save snapshots to directory (%1$s) using format (%2$s)", snapshot, format), t); + } + } + + @Override + public void save(File snapshot, SnapshotFormat format, SnapshotOptions options) { + try { + getSnapshotService().save(snapshot, format, options); + } + catch (Throwable t) { + throw new RuntimeException(String.format( + "Failed to save snapshots to directory (%1$s) using format (%2$s) with options (%3$s)", + snapshot, format, options), t); + } + } + } + + public static class SnapshotMetadata { + + private final File location; + + private final SnapshotFilter filter; + + private final SnapshotFormat format; + + public SnapshotMetadata(File location, SnapshotFilter filter, SnapshotFormat format) { + Assert.isTrue(location != null && location.exists(), String.format( + "The File location (%1$s) must exist", location)); + + this.location = location; + this.filter = filter; + this.format = format; + } + + public boolean isDirectory() { + return getLocation().isDirectory(); + } + + public boolean isFile() { + return getLocation().isFile(); + } + + public File getLocation() { + return location; + } + + public boolean isFilterPresent() { + return (getFilter() != null); + } + + public SnapshotFilter getFilter() { + return filter; + } + + public SnapshotFormat getFormat() { + return (format != null ? format : SnapshotFormat.GEMFIRE); + } + + @Override + public String toString() { + return String.format("{ @type = %1$s, location = %2$s, filter = %2$s, format = %4$s }", + getClass().getName(), getLocation().getAbsolutePath(), getFilter(), getFormat()); + } + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java index 6e175f2c..39dcc5c7 100644 --- a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java +++ b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java @@ -40,5 +40,6 @@ class GemfireDataNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("function-executions", new FunctionExecutionBeanDefinitionParser()); registerBeanDefinitionParser("datasource", new GemfireDataSourceParser()); registerBeanDefinitionParser("json-region-autoproxy", new GemfireRegionAutoProxyParser()); + registerBeanDefinitionParser("snapshot-service", new SnapshotServiceParser()); } } diff --git a/src/main/java/org/springframework/data/gemfire/config/ParsingUtils.java b/src/main/java/org/springframework/data/gemfire/config/ParsingUtils.java index 002f77a0..6bc70d4a 100644 --- a/src/main/java/org/springframework/data/gemfire/config/ParsingUtils.java +++ b/src/main/java/org/springframework/data/gemfire/config/ParsingUtils.java @@ -51,6 +51,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 CACHE_REF_ATTRIBUTE_NAME = "cache-ref"; + static void setPropertyReference(Element element, BeanDefinitionBuilder builder, String attributeName, String propertyName) { @@ -416,7 +419,7 @@ abstract class ParsingUtils { return false; } - public static void parseCompressor(ParserContext parserContext, Element element, + static void parseCompressor(ParserContext parserContext, Element element, BeanDefinitionBuilder regionAttributesBuilder) { Element compressorElement = DomUtils.getChildElementByTagName(element, "compressor"); @@ -427,8 +430,16 @@ abstract class ParsingUtils { } } - static String resolveCacheReference(final String cacheRef) { - return (StringUtils.hasText(cacheRef) ? cacheRef : GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME); + static void setCacheReference(Element element, BeanDefinitionBuilder builder) { + builder.addPropertyReference(CACHE_PROPERTY_NAME, resolveCacheReference(element)); + } + + static String resolveCacheReference(Element element) { + return resolveCacheReference(element.getAttribute(CACHE_REF_ATTRIBUTE_NAME)); + } + + static String resolveCacheReference(String cacheReference) { + return (StringUtils.hasText(cacheReference) ? cacheReference : GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME); } } diff --git a/src/main/java/org/springframework/data/gemfire/config/SnapshotServiceParser.java b/src/main/java/org/springframework/data/gemfire/config/SnapshotServiceParser.java new file mode 100644 index 00000000..86c29199 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/config/SnapshotServiceParser.java @@ -0,0 +1,90 @@ +/* + * 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.List; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.gemfire.SnapshotServiceFactoryBean; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * Parser for <gfe-data:snapshot-service> bean definition elements in Spring XML config. + * + * @author John Blum + * @see org.springframework.beans.factory.config.BeanDefinition + * @see org.springframework.beans.factory.support.BeanDefinitionBuilder + * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser + * @see org.springframework.beans.factory.xml.ParserContext + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean + * @since 1.7.0 + */ +class SnapshotServiceParser extends AbstractSingleBeanDefinitionParser { + + @Override + protected Class getBeanClass(final Element element) { + return SnapshotServiceFactoryBean.class; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + super.doParse(element, parserContext, builder); + + ParsingUtils.setCacheReference(element, builder); + ParsingUtils.setPropertyReference(element, builder, "region-ref", "region"); + builder.addPropertyValue("exports", parseExports(element, parserContext)); + builder.addPropertyValue("imports", parseImports(element, parserContext)); + } + + private ManagedList parseExports(Element element, ParserContext parserContext) { + return parseSnapshots(element, parserContext, "export-snapshot"); + } + + private ManagedList parseImports(Element element, ParserContext parserContext) { + return parseSnapshots(element, parserContext, "import-snapshot"); + } + + private ManagedList parseSnapshots(Element element, ParserContext parserContext, + String childTagName) { + + ManagedList snapshotBeans = new ManagedList(); + + for (Element childElement : DomUtils.getChildElementsByTagName(element, childTagName)) { + snapshotBeans.add(parseSnapshotMetadata(childElement, parserContext)); + } + + return snapshotBeans; + } + + private BeanDefinition parseSnapshotMetadata(Element snapshotMetadataElement, ParserContext parserContext) { + BeanDefinitionBuilder snapshotMetadataBuilder = BeanDefinitionBuilder.genericBeanDefinition( + SnapshotServiceFactoryBean.SnapshotMetadata.class); + + ParsingUtils.setPropertyValue(snapshotMetadataElement, snapshotMetadataBuilder, "location"); + ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, snapshotMetadataElement, snapshotMetadataBuilder, + "filter-ref"); + ParsingUtils.setPropertyValue(snapshotMetadataElement, snapshotMetadataBuilder, "format"); + + return snapshotMetadataBuilder.getBeanDefinition(); + } + +} diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.7.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.7.xsd index b5b3b835..363e2e98 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.7.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.7.xsd @@ -63,7 +63,6 @@ Enables component scanning for annotated function execution interfaces. ]]> - @@ -175,4 +174,64 @@ native GemFire PdxInstance type. True, by default but will incur significant ove + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java new file mode 100644 index 00000000..134eaf2d --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java @@ -0,0 +1,295 @@ +/* + * 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 static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.snapshot.CacheSnapshotService; +import com.gemstone.gemfire.cache.snapshot.RegionSnapshotService; +import com.gemstone.gemfire.cache.snapshot.SnapshotFilter; +import com.gemstone.gemfire.cache.snapshot.SnapshotOptions; + +/** + * The SnapshotServiceFactoryBeanTest class is a test suite of test cases testing the contract and functionality + * of the SnapshotServiceFactoryBean class. + * + * @author John Blum + * @see org.junit.Rule + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean + * @since 1.7.0 + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class SnapshotServiceFactoryBeanTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata() { + return newSnapshotMetadata(new File(System.getProperty("user.dir"))); + } + + protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata(File location) { + return newSnapshotMetadata(location, null); + } + + protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata(File location, + SnapshotFilter filter) { + return newSnapshotMetadata(location, filter, SnapshotOptions.SnapshotFormat.GEMFIRE); + } + + protected SnapshotServiceFactoryBean.SnapshotMetadata newSnapshotMetadata(File location, + SnapshotFilter filter, SnapshotOptions.SnapshotFormat format) { + return new SnapshotServiceFactoryBean.SnapshotMetadata(location, filter, format); + } + + protected SnapshotServiceFactoryBean.SnapshotMetadata[] toArray( + SnapshotServiceFactoryBean.SnapshotMetadata... metadata) { + return metadata; + } + + @After + public void tearDown() { + factoryBean.setExports(null); + factoryBean.setImports(null); + factoryBean.setRegion(null); + } + + @Test + public void setCacheToNull() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The GemFire Cache must not be null"); + factoryBean.setCache(null); + } + + @Test + public void getCacheWhenUninitialized() { + expectedException.expect(IllegalStateException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The GemFire Cache was not properly initialized"); + factoryBean.getCache(); + } + + @Test + public void setAndGetCacheSuccessfully() { + Cache mockCache = mock(Cache.class, "MockCache"); + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setCache(mockCache); + + assertThat(factoryBean.getCache(), is(sameInstance(mockCache))); + } + + @Test + public void setAndGetExports() { + SnapshotServiceFactoryBean.SnapshotMetadata[] actualExports = factoryBean.getExports(); + + assertThat(actualExports, is(notNullValue())); + assertThat(actualExports.length, is(equalTo(0))); + + SnapshotServiceFactoryBean.SnapshotMetadata[] expectedExports = toArray(newSnapshotMetadata()); + + factoryBean.setExports(expectedExports); + actualExports = factoryBean.getExports(); + + assertThat(actualExports, is(sameInstance(expectedExports))); + + factoryBean.setExports(null); + actualExports = factoryBean.getExports(); + + assertThat(actualExports, is(not(sameInstance(expectedExports)))); + assertThat(actualExports, is(notNullValue())); + assertThat(actualExports.length, is(equalTo(0))); + } + + @Test + public void setAndGetImports() { + SnapshotServiceFactoryBean.SnapshotMetadata[] actualImports = factoryBean.getImports(); + + assertThat(actualImports, is(notNullValue())); + assertThat(actualImports.length, is(equalTo(0))); + + SnapshotServiceFactoryBean.SnapshotMetadata[] expectedImports = toArray(newSnapshotMetadata()); + + factoryBean.setImports(expectedImports); + actualImports = factoryBean.getImports(); + + assertThat(actualImports, is(sameInstance(expectedImports))); + + factoryBean.setImports(null); + actualImports = factoryBean.getImports(); + + assertThat(actualImports, is(not(sameInstance(expectedImports)))); + assertThat(actualImports, is(notNullValue())); + assertThat(actualImports.length, is(equalTo(0))); + } + + @Test + public void setAndGetRegionSuccessfully() { + assertThat(factoryBean.getRegion(), is(nullValue())); + + Region mockRegion = mock(Region.class, "MockRegion"); + + factoryBean.setRegion(mockRegion); + + assertThat(factoryBean.getRegion(), is(sameInstance(mockRegion))); + + factoryBean.setRegion(null); + + assertThat(factoryBean.getRegion(), is(nullValue())); + } + + @Test + public void nullSafeArrayWithNonNullArray() { + SnapshotServiceFactoryBean.SnapshotMetadata[] expectedConfigurations = + new SnapshotServiceFactoryBean.SnapshotMetadata[0]; + + assertThat(factoryBean.nullSafeArray(expectedConfigurations), is(sameInstance(expectedConfigurations))); + } + + @Test + public void nullSafeArrayWithNullArray() { + assertThat(factoryBean.nullSafeArray(null), is(equalTo(SnapshotServiceFactoryBean.EMPTY_ARRAY))); + } + + @Test + public void wrapNullCacheSnapshotService() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The backing CacheSnapshotService must not be null"); + factoryBean.wrap((CacheSnapshotService) null); + } + + @Test + public void wrapNullRegionSnapshotService() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage("The backing RegionSnapshotService must not be null"); + factoryBean.wrap((RegionSnapshotService) null); + } + + @Test + public void isSingletonIsTrue() { + assertThat(factoryBean.isSingleton(), is(true)); + } + + @Test + public void createCacheSnapshotServiceAndImportOnInitialization() throws Exception { + Cache mockCache = mock(Cache.class, "MockCache"); + CacheSnapshotService mockCacheSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + + when(mockCache.getSnapshotService()).thenReturn(mockCacheSnapshotService); + + File userHomeDirectory = new File(System.getProperty("user.home")); + + SnapshotServiceFactoryBean.SnapshotMetadata[] expectedImports = toArray( + newSnapshotMetadata(userHomeDirectory)); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setCache(mockCache); + factoryBean.setImports(expectedImports); + factoryBean.setRegion(null); + + assertThat(factoryBean.getObject(), is(nullValue())); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(SnapshotServiceFactoryBean.SnapshotServiceAdapter.class))); + + factoryBean.afterPropertiesSet(); + + assertThat(factoryBean.getObject(), is(instanceOf(SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter.class))); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(SnapshotServiceFactoryBean.CacheSnapshotServiceAdapter.class))); + + verify(mockCache, times(1)).getSnapshotService(); + verify(mockCacheSnapshotService, times(1)).load(eq(userHomeDirectory), eq(SnapshotOptions.SnapshotFormat.GEMFIRE)); + } + + @Test + @SuppressWarnings("unchecked") + public void createRegionSnapshotServiceAndImportOnInitialization() throws Exception { + Cache mockCache = mock(Cache.class, "MockCache"); + Region mockRegion = mock(Region.class, "MockRegion"); + RegionSnapshotService mockRegionSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + SnapshotFilter mockFilter = mock(SnapshotFilter.class, "MockSnapshotFilter"); + SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + + when(mockCache.getSnapshotService()).thenThrow(new UnsupportedOperationException("operation not supported")); + when(mockRegion.getSnapshotService()).thenReturn(mockRegionSnapshotService); + when(mockRegionSnapshotService.createOptions()).thenReturn(mockSnapshotOptions); + when(mockSnapshotOptions.setFilter(any(SnapshotFilter.class))).thenReturn(mockSnapshotOptions); + + File snapshot = File.createTempFile("snapshot", "dat"); + + snapshot.deleteOnExit(); + + SnapshotServiceFactoryBean.SnapshotMetadata[] expectedImports = toArray( + newSnapshotMetadata(snapshot, mockFilter)); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + + factoryBean.setCache(mockCache); + factoryBean.setImports(expectedImports); + factoryBean.setRegion(mockRegion); + + assertThat(factoryBean.getObject(), is(nullValue())); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(SnapshotServiceFactoryBean.SnapshotServiceAdapter.class))); + + factoryBean.afterPropertiesSet(); + + assertThat(factoryBean.getObject(), + is(instanceOf(SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter.class))); + assertThat((Class) factoryBean.getObjectType(), + is(equalTo(SnapshotServiceFactoryBean.RegionSnapshotServiceAdapter.class))); + + verify(mockCache, never()).getSnapshotService(); + verify(mockRegion, times(1)).getSnapshotService(); + verify(mockRegionSnapshotService, times(1)).createOptions(); + verify(mockRegionSnapshotService, times(1)).load(eq(snapshot), + eq(SnapshotOptions.SnapshotFormat.GEMFIRE), eq(mockSnapshotOptions)); + verify(mockSnapshotOptions, times(1)).setFilter(eq(mockFilter)); + } + +}