From 3a360a37b4ef57e9490098281da3da3953fbe60e Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 28 Aug 2015 00:33:13 -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. 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. --- .../gemfire/ComposableSnapshotFilter.java | 150 ++++++++ .../gemfire/SnapshotServiceFactoryBean.java | 54 ++- .../data/gemfire/config/ParsingUtils.java | 10 + .../gemfire/config/SnapshotServiceParser.java | 3 +- .../config/spring-data-gemfire-1.7.xsd | 1 + .../gemfire/ComposableSnapshotFilterTest.java | 172 +++++++++ .../gemfire/SnapshotApplicationEventTest.java | 11 +- ...riggeredImportsExportsIntegrationTest.java | 263 ++++++++++++++ .../SnapshotServiceFactoryBeanTest.java | 331 +++++++++++------- ...hotServiceImportExportIntegrationTest.java | 15 +- ...dImportsExportsIntegrationTest-context.xml | 70 ++++ 11 files changed, 925 insertions(+), 155 deletions(-) create mode 100644 src/main/java/org/springframework/data/gemfire/ComposableSnapshotFilter.java create mode 100644 src/test/java/org/springframework/data/gemfire/ComposableSnapshotFilterTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest.java create mode 100644 src/test/resources/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest-context.xml diff --git a/src/main/java/org/springframework/data/gemfire/ComposableSnapshotFilter.java b/src/main/java/org/springframework/data/gemfire/ComposableSnapshotFilter.java new file mode 100644 index 00000000..3a39c1a9 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/ComposableSnapshotFilter.java @@ -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 implements SnapshotFilter { + + /** + * 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 leftOperand; + private final SnapshotFilter 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 leftOperand, Operator operator, SnapshotFilter rightOperand) { + this.leftOperand = leftOperand; + this.operator = operator; + this.rightOperand = rightOperand; + } + + /* (non-Javadoc) */ + @SuppressWarnings("unchecked") + static SnapshotFilter[] nullSafeArray(SnapshotFilter... array) { + return (array != null ? array : new SnapshotFilter[0]); + } + + /** + * Composes the array of SnapshotFilters into a logical boolean expression using the specified Operator. + * + * @param the class type of the SnapshotFilter key. + * @param 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 SnapshotFilter compose(Operator operator, SnapshotFilter... snapshotFilters) { + SnapshotFilter composedSnapshotFilter = null; + + for (SnapshotFilter snapshotFilter : nullSafeArray(snapshotFilters)) { + composedSnapshotFilter = (composedSnapshotFilter == null ? snapshotFilter + : new ComposableSnapshotFilter(snapshotFilter, operator, composedSnapshotFilter)); + } + + return composedSnapshotFilter; + } + + /** + * Composes the array of SnapshotFilters into a logical boolean expression using the AND Operator. + * + * @param the class type of the SnapshotFilter key. + * @param 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 SnapshotFilter and(SnapshotFilter... snapshotFilters) { + return compose(Operator.AND, snapshotFilters); + } + + /** + * Composes the array of SnapshotFilters into a logical boolean expression using the OR Operator. + * + * @param the class type of the SnapshotFilter key. + * @param 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 SnapshotFilter or(SnapshotFilter... 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 entry) { + return operator.operate(leftOperand.accept(entry), rightOperand.accept(entry)); + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java b/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java index 95899e14..6707efc3 100644 --- a/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/SnapshotServiceFactoryBean.java @@ -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 implements FactoryBean>, +public class SnapshotServiceFactoryBean implements FactoryBean>, InitializingBean, DisposableBean, ApplicationListener> { protected static final SnapshotMetadata[] EMPTY_ARRAY = new SnapshotMetadata[0]; + private Boolean suppressInitImport; + private Cache cache; private Region region; @@ -185,6 +189,27 @@ public class SnapshotServiceFactoryBean implements FactoryBean implements FactoryBean implements FactoryBean implements FactoryBean createOptions(); - void doExport(SnapshotMetadata[] configurations); + void doExport(SnapshotMetadata... configurations); - void doImport(SnapshotMetadata[] configurations); + void doImport(SnapshotMetadata... configurations); void load(File directory, SnapshotFormat format); @@ -392,6 +417,8 @@ public class SnapshotServiceFactoryBean implements FactoryBean implements SnapshotServiceAdapter { + 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 implements FactoryBean[] configurations) { + public void doExport(SnapshotMetadata... configurations) { for (SnapshotMetadata configuration : nullSafeArray(configurations)) { save(configuration.getLocation(), configuration.getFormat(), createOptions(configuration.getFilter())); } } @Override - public void doImport(SnapshotMetadata[] configurations) { + public void doImport(SnapshotMetadata... configurations) { for (SnapshotMetadata configuration : nullSafeArray(configurations)) { load(configuration.getFormat(), createOptions(configuration.getFilter()), handleLocation(configuration)); } @@ -434,8 +461,7 @@ public class SnapshotServiceFactoryBean implements FactoryBean + diff --git a/src/test/java/org/springframework/data/gemfire/ComposableSnapshotFilterTest.java b/src/test/java/org/springframework/data/gemfire/ComposableSnapshotFilterTest.java new file mode 100644 index 00000000..ea6c1176 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/ComposableSnapshotFilterTest.java @@ -0,0 +1,172 @@ +/* + * 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.is; +import static org.hamcrest.CoreMatchers.isA; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.data.gemfire.ComposableSnapshotFilter.Operator; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import com.gemstone.gemfire.cache.snapshot.SnapshotFilter; + +/** + * The ComposableSnapshotFilterTest class is a test suite of test cases testing the contract and functionality + * of the ComposableSnapshotFilter class. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.ComposableSnapshotFilter + * @see org.springframework.data.gemfire.ComposableSnapshotFilter.Operator + * @see com.gemstone.gemfire.cache.snapshot.SnapshotFilter + * @since 1.7.0 + */ +public class ComposableSnapshotFilterTest { + + private static final AtomicInteger ID_SEQUENCE = new AtomicInteger(0); + + @SuppressWarnings("unchecked") + protected SnapshotFilter mockSnapshotFilter(boolean accept) { + SnapshotFilter mockSnapshotFilter = mock(SnapshotFilter.class, String.format( + "MockSnapshotFilter-%1$d", ID_SEQUENCE.incrementAndGet())); + + when(mockSnapshotFilter.accept(any(Map.Entry.class))).thenReturn(accept); + + return mockSnapshotFilter; + } + + @Test + public void operatorIdentityIsSuccessful() { + assertThat(Operator.AND.isAnd(), is(true)); + assertThat(Operator.AND.isOr(), is(false)); + assertThat(Operator.OR.isAnd(), is(false)); + assertThat(Operator.OR.isOr(), is(true)); + } + + @Test + public void andOperatorOperationIsValid() { + assertThat(Operator.AND.operate(true, true), is(true)); + assertThat(Operator.AND.operate(true, false), is(false)); + assertThat(Operator.AND.operate(false, true), is(false)); + assertThat(Operator.AND.operate(false, false), is(false)); + } + + @Test + public void orOperatorOperationIsValid() { + assertThat(Operator.OR.operate(true, true), is(true)); + assertThat(Operator.OR.operate(true, false), is(true)); + assertThat(Operator.OR.operate(false, true), is(true)); + assertThat(Operator.OR.operate(false, false), is(false)); + } + + @Test + public void nullSafeArrayWithNonNullArray() { + SnapshotFilter[] expectedArray = {}; + + assertThat(ComposableSnapshotFilter.nullSafeArray(expectedArray), is(sameInstance(expectedArray))); + } + + @Test + public void nullSafeArrayWithNullArray() { + SnapshotFilter[] actualArray = ComposableSnapshotFilter.nullSafeArray((SnapshotFilter[]) null); + + assertThat(actualArray, is(notNullValue())); + assertThat(actualArray.length, is(equalTo(0))); + } + + @Test + public void composeSingle() { + SnapshotFilter mockSnapshotFilter = mockSnapshotFilter(false); + SnapshotFilter composedFilter = ComposableSnapshotFilter.compose(Operator.AND, mockSnapshotFilter); + + assertThat(composedFilter, is(sameInstance(mockSnapshotFilter))); + } + + @Test + public void composeMultiple() throws Exception { + SnapshotFilter mockSnapshotFilterOne = mockSnapshotFilter(false); + SnapshotFilter mockSnapshotFilterTwo = mockSnapshotFilter(true); + + SnapshotFilter composedFilter = ComposableSnapshotFilter.compose(Operator.AND, mockSnapshotFilterOne, + mockSnapshotFilterTwo); + + assertThat(composedFilter, is(not(sameInstance(mockSnapshotFilterOne)))); + assertThat(composedFilter, is(not(sameInstance(mockSnapshotFilterTwo)))); + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat((SnapshotFilter) TestUtils.readField("leftOperand", composedFilter), + is(equalTo(mockSnapshotFilterTwo))); + assertThat((Operator) TestUtils.readField("operator", composedFilter), is(equalTo(Operator.AND))); + assertThat((SnapshotFilter) TestUtils.readField("rightOperand", composedFilter), + is(equalTo(mockSnapshotFilterOne))); + } + + @Test + @SuppressWarnings("unchecked") + public void composeAndThenAccept() { + SnapshotFilter falseFilter = mockSnapshotFilter(false); + SnapshotFilter trueFilter = mockSnapshotFilter(true); + + SnapshotFilter composedFilter = ComposableSnapshotFilter.and(trueFilter, trueFilter); + + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat(composedFilter.accept(null), is(true)); + + composedFilter = ComposableSnapshotFilter.and(falseFilter, trueFilter); + + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat(composedFilter.accept(null), is(false)); + + composedFilter = ComposableSnapshotFilter.and(falseFilter, falseFilter); + + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat(composedFilter.accept(null), is(false)); + } + + @Test + @SuppressWarnings("unchecked") + public void composeOrThenAccept() { + SnapshotFilter falseFilter = mockSnapshotFilter(false); + SnapshotFilter trueFilter = mockSnapshotFilter(true); + + SnapshotFilter composedFilter = ComposableSnapshotFilter.or(trueFilter, trueFilter); + + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat(composedFilter.accept(null), is(true)); + + composedFilter = ComposableSnapshotFilter.or(falseFilter, trueFilter); + + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat(composedFilter.accept(null), is(true)); + + composedFilter = ComposableSnapshotFilter.or(falseFilter, falseFilter); + + assertThat((ComposableSnapshotFilter) composedFilter, isA(ComposableSnapshotFilter.class)); + assertThat(composedFilter.accept(null), is(false)); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTest.java b/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTest.java index fb661777..39b94b5d 100644 --- a/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTest.java +++ b/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTest.java @@ -147,14 +147,14 @@ public class SnapshotApplicationEventTest { SnapshotApplicationEvent event = new TestSnapshotApplicationEvent(this, "/Example"); assertThat(event.getRegionPath(), is(equalTo("/Example"))); - assertThat(event.matches("/Prototype"), is(false)); - assertThat(event.matches("/Sample"), is(false)); - assertThat(event.matches("/Xmpl"), is(false)); - assertThat(event.matches("/Ex"), is(false)); - assertThat(event.matches("/E.g."), is(false)); assertThat(event.matches("Example"), is(false)); + assertThat(event.matches("/Sample"), is(false)); + assertThat(event.matches("/Prototype"), is(false)); assertThat(event.matches("/example"), is(false)); assertThat(event.matches("/Exam"), is(false)); + assertThat(event.matches("/Xmpl"), is(false)); + assertThat(event.matches("/Ex."), is(false)); + assertThat(event.matches("/E.g."), is(false)); assertThat(event.matches("/"), is(false)); assertThat(event.matches(" "), is(false)); assertThat(event.matches(""), is(false)); @@ -166,6 +166,7 @@ public class SnapshotApplicationEventTest { assertThat(new TestSnapshotApplicationEvent(this, "/Example").matches("/Example"), is(true)); assertThat(new TestSnapshotApplicationEvent(this, "/").matches("/"), is(true)); assertThat(new TestSnapshotApplicationEvent(this, " ").matches(" "), is(true)); + assertThat(new TestSnapshotApplicationEvent(this, "").matches(" "), is(true)); assertThat(new TestSnapshotApplicationEvent(this, "").matches(""), is(true)); assertThat(new TestSnapshotApplicationEvent(this, (String) null).matches((String) null), is(true)); } diff --git a/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest.java new file mode 100644 index 00000000..57413985 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest.java @@ -0,0 +1,263 @@ +/* + * 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.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Resource; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.gemfire.repository.sample.Person; +import org.springframework.data.gemfire.test.support.FileSystemUtils; +import org.springframework.data.gemfire.test.support.ThreadUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.snapshot.SnapshotFilter; +import com.gemstone.gemfire.management.internal.cli.util.spring.StringUtils; + +/** + * The SnapshotApplicationEventTriggeredImportsExportsIntegrationTest class is a test suite of test cases testing + * the effects of the SnapshotServiceFactoryBean using Spring ApplicationEvents to trigger imports and exports + * of GemFire Cache Region data. + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @see org.springframework.data.gemfire.ExportSnapshotApplicationEvent + * @see org.springframework.data.gemfire.ImportSnapshotApplicationEvent + * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean + * @see org.springframework.data.gemfire.repository.sample.Person + * @see com.gemstone.gemfire.cache.Region + * @since 1.7.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class SnapshotApplicationEventTriggeredImportsExportsIntegrationTest { + + protected static final AtomicLong ID_SEQUENCE = new AtomicLong(0l); + + protected static File snapshotsDirectory; + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @Resource(name = "Doe") + private Region doe; + + @Resource(name = "EveryoneElse") + private Region everyoneElse; + + @Resource(name = "Handy") + private Region handy; + + @Resource(name = "People") + private Region people; + + @BeforeClass + public static void setupBeforeClass() throws Exception { + snapshotsDirectory = new File(new File(FileSystemUtils.WORKING_DIRECTORY, "gemfire"), "snapshots"); + + assertThat(snapshotsDirectory.isDirectory() || snapshotsDirectory.mkdirs(), is(true)); + + File peopleSnapshotFile = new File(snapshotsDirectory, "people.snapshot"); + File nonHandyNonDoeSnapshotFile = new File(snapshotsDirectory, "nonHandyNonDoePeople.snapshot"); + + assertThat(peopleSnapshotFile.isFile() || peopleSnapshotFile.createNewFile(), is(true)); + assertThat(nonHandyNonDoeSnapshotFile.isFile() || nonHandyNonDoeSnapshotFile.createNewFile(), is(true)); + } + + @AfterClass + public static void tearDownAfterClass() { + FileSystemUtils.deleteRecursive(snapshotsDirectory.getParentFile()); + snapshotsDirectory = null; + } + + protected void assertPeople(Region targetRegion, Person... people) { + assertThat(targetRegion.size(), is(equalTo(people.length))); + + for (Person person : people) { + assertPerson(person, targetRegion.get(person.getId())); + } + } + + protected void assertPerson(Person expectedPerson, Person actualPerson) { + assertThat(String.format("Expected (%1$s); but was (%2$s)", expectedPerson, actualPerson), + actualPerson, is(notNullValue())); + assertThat(actualPerson.getId(), is(equalTo(expectedPerson.getId()))); + assertThat(actualPerson.getFirstname(), is(equalTo(expectedPerson.getFirstname()))); + assertThat(actualPerson.getLastname(), is(equalTo(expectedPerson.getLastname()))); + } + + protected Person createPerson(String firstName, String lastName) { + return new Person(ID_SEQUENCE.incrementAndGet(), firstName, lastName); + } + + protected Person put(Region targetRegion, Person person) { + targetRegion.putIfAbsent(person.getId(), person); + return person; + } + + protected void wait(final int seconds, final int expectedDoeSize, final int expectedEveryoneSize, + final int expectedHandySize) { + ThreadUtils.timedWait(TimeUnit.SECONDS.toMillis(seconds), 500, new ThreadUtils.WaitCondition() { + @Override public boolean waiting() { + return (doe.size() < expectedDoeSize && everyoneElse.size() < expectedEveryoneSize + && handy.size() < expectedHandySize); + } + }); + } + + @Test + @SuppressWarnings("unchecked") + public void exportsTriggeringImportsOnSnapshotApplicationEvents() { + Person jonDoe = put(people, createPerson("Jon", "Doe")); + Person janeDoe = put(people, createPerson("Jane", "Doe")); + Person jackBlack = put(people, createPerson("Jack", "Black")); + Person jackHandy = put(people, createPerson("Jack", "Handy")); + Person joeDirt = put(people, createPerson("Joe", "Dirt")); + + SnapshotApplicationEvent event = new ExportSnapshotApplicationEvent(this, people.getFullPath()); + + eventPublisher.publishEvent(event); + wait(5, 2, 2, 1); + + assertPeople(doe, jonDoe, janeDoe); + assertPeople(everyoneElse, jackBlack, joeDirt); + assertPeople(handy, jackHandy); + + Person cookieDoe = put(people, createPerson("Cookie", "Doe")); + Person pieDoe = put(people, createPerson("Pie", "Doe")); + Person sourDoe = put(people, createPerson("Sour", "Doe")); + Person randyHandy = put(people, createPerson("Randy", "Handy")); + Person sandyHandy = put(people, createPerson("Sandy", "Handy")); + Person jackHill = put(people, createPerson("Jack", "Hill")); + Person jillHill = put(people, createPerson("Jill", "Hill")); + + eventPublisher.publishEvent(event); + wait(10, 5, 4, 3); + + assertPeople(doe, jonDoe, janeDoe, cookieDoe, pieDoe, sourDoe); + assertPeople(everyoneElse, jackBlack, joeDirt, jackHill, jillHill); + assertPeople(handy, jackHandy, randyHandy, sandyHandy); + + Person bobDoe = put(people, createPerson("Bob", "Doe")); + Person mandyHandy = put(people, createPerson("Mandy", "Handy")); + Person imaPigg = put(people, createPerson("Ima", "Pigg")); + Person benDover = put(people, createPerson("Ben", "Dover")); + + eventPublisher.publishEvent(event); + wait(15, 6, 6, 4); + + assertPeople(doe, jonDoe, janeDoe, cookieDoe, pieDoe, sourDoe, bobDoe); + assertPeople(everyoneElse, jackBlack, joeDirt, jackHill, jillHill, imaPigg, benDover); + assertPeople(handy, jackHandy, randyHandy, sandyHandy, mandyHandy); + } + + protected static class LastNameSnapshotFilter implements SnapshotFilter { + + private final String lastName; + + public LastNameSnapshotFilter(String lastName) { + Assert.hasText(lastName, "'lastName' must be specified"); + this.lastName = lastName; + } + + protected String getLastName() { + Assert.state(StringUtils.hasText(lastName), "'lastName' was not properly initialized"); + return lastName; + } + + @Override + public boolean accept(Map.Entry entry) { + return accept(entry.getValue()); + } + + public boolean accept(Person person) { + return getLastName().equalsIgnoreCase(person.getLastname()); + } + } + + protected static class NotLastNameSnapshotFilter extends LastNameSnapshotFilter { + + public NotLastNameSnapshotFilter(String lastName) { + super(lastName); + } + + @Override + public boolean accept(final Map.Entry entry) { + return !super.accept(entry); + } + } + + protected static class SnapshotImportsMonitor { + + @Autowired + private ApplicationEventPublisher eventPublisher; + + private static final Map snapshotFileLastModifiedMap = new ConcurrentHashMap(2); + + @Scheduled(fixedDelay = 1000) + @SuppressWarnings("unchecked") + public void processSnapshots() { + boolean triggerEvent = false; + + for (File snapshotFile : nullSafeArray(snapshotsDirectory.listFiles(FileSystemUtils.FileOnlyFilter.INSTANCE))) { + triggerEvent |= isUnprocessedSnapshotFile(snapshotFile); + } + + if (triggerEvent) { + eventPublisher.publishEvent(new ImportSnapshotApplicationEvent(this)); + } + } + + protected File[] nullSafeArray(File... files) { + return (files != null ? files : new File[0]); + } + + protected boolean isUnprocessedSnapshotFile(File snapshotFile) { + Long lastModified = snapshotFile.lastModified(); + Long previousLastModified = snapshotFileLastModifiedMap.get(snapshotFile); + + previousLastModified = (previousLastModified != null ? previousLastModified : lastModified); + + snapshotFileLastModifiedMap.put(snapshotFile, lastModified); + + return !previousLastModified.equals(lastModified); + } + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java index 62465f9d..cf14f16e 100644 --- a/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java +++ b/src/test/java/org/springframework/data/gemfire/SnapshotServiceFactoryBeanTest.java @@ -20,6 +20,7 @@ import static com.gemstone.gemfire.cache.snapshot.SnapshotOptions.SnapshotFormat import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.isA; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @@ -87,6 +88,14 @@ public class SnapshotServiceFactoryBeanTest { private SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean(); + protected static File mockFile(String filename) { + File mockFile = mock(File.class, filename); + when(mockFile.isFile()).thenReturn(true); + when(mockFile.getAbsolutePath()).thenReturn(String.format("/path/to/%1$s", filename)); + when(mockFile.getName()).thenReturn(filename); + return mockFile; + } + protected SnapshotMetadata newSnapshotMetadata() { return newSnapshotMetadata(FileSystemUtils.WORKING_DIRECTORY); } @@ -114,9 +123,7 @@ public class SnapshotServiceFactoryBeanTest { @BeforeClass public static void setupBeforeClass() throws Exception { - snapshotDat = File.createTempFile("snapshot", "dat"); - snapshotDat.deleteOnExit(); - assertThat(snapshotDat.isFile(), is(true)); + snapshotDat = mockFile("snapshot.dat"); } @After @@ -248,6 +255,23 @@ public class SnapshotServiceFactoryBeanTest { assertThat(factoryBean.getRegion(), is(nullValue())); } + @Test + public void setAndGetSuppressInitImportSuccessfully() { + assertThat(factoryBean.isSuppressInitImport(), is(false)); + + factoryBean.setSuppressInitImport(true); + + assertThat(factoryBean.isSuppressInitImport(), is(true)); + + factoryBean.setSuppressInitImport(false); + + assertThat(factoryBean.isSuppressInitImport(), is(false)); + + factoryBean.setSuppressInitImport(null); + + assertThat(factoryBean.isSuppressInitImport(), is(false)); + } + @Test public void isSingletonIsTrue() { assertThat(factoryBean.isSingleton(), is(true)); @@ -255,6 +279,8 @@ public class SnapshotServiceFactoryBeanTest { @Test public void afterPropertiesSetCreatesSnapshotServiceAdapterAndDoesImportWithConfiguredImports() throws Exception { + SnapshotMetadata expectedSnapshotMetadata = newSnapshotMetadata(); + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); @@ -264,14 +290,30 @@ public class SnapshotServiceFactoryBeanTest { } }; - SnapshotMetadata expectedSnapshotMetadata = newSnapshotMetadata(); - factoryBean.setImports(toArray(expectedSnapshotMetadata)); factoryBean.afterPropertiesSet(); assertThat(factoryBean.getImports()[0], is(equalTo(expectedSnapshotMetadata))); - verify(mockSnapshotService, times(1)).doImport(eq(toArray(expectedSnapshotMetadata))); + verify(mockSnapshotService, times(1)).doImport(eq(expectedSnapshotMetadata)); + } + + @Test + public void afterPropertiesSetCreatesSnapshotServiceAdapterButSuppressesImportOnInit() throws Exception { + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); + + SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { + @Override protected SnapshotServiceAdapter create() { + return mockSnapshotService; + } + }; + + factoryBean.setSuppressInitImport(true); + factoryBean.afterPropertiesSet(); + + assertThat(factoryBean.isSuppressInitImport(), is(true)); + + verify(mockSnapshotService, never()).doImport(any(SnapshotMetadata[].class)); } @Test @@ -328,6 +370,8 @@ public class SnapshotServiceFactoryBeanTest { @Test public void destroyPerformsExportWithConfiguredExports() throws Exception { + SnapshotMetadata expectedSnapshotMetadata = newSnapshotMetadata(); + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); @@ -337,232 +381,263 @@ public class SnapshotServiceFactoryBeanTest { } }; - SnapshotMetadata expectedSnapshotMetadata = newSnapshotMetadata(); - factoryBean.setExports(toArray(expectedSnapshotMetadata)); factoryBean.destroy(); assertThat(factoryBean.getExports()[0], is(equalTo(expectedSnapshotMetadata))); - verify(mockSnapshotService, times(1)).doExport(eq(toArray(expectedSnapshotMetadata))); + verify(mockSnapshotService, times(1)).doExport(eq(expectedSnapshotMetadata)); } @Test public void onApplicationEventWhenMatchUsingEventSnapshotMetadataPerformsExport() throws Exception { Region mockRegion = mock(Region.class, "MockRegion"); - SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + SnapshotApplicationEvent mockSnapshotEvent = mock(ExportSnapshotApplicationEvent.class, + "MockExportSnapshotApplicationEvent"); - final RegionSnapshotService mockSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + SnapshotMetadata eventSnapshotMetadata = newSnapshotMetadata(snapshotDat); - when(mockRegion.getFullPath()).thenReturn("/Example"); - when(mockSnapshotService.createOptions()).thenReturn(mockSnapshotOptions); - when(mockSnapshotOptions.setFilter(any(SnapshotFilter.class))).thenReturn(mockSnapshotOptions); + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); + + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(false); + when(mockSnapshotEvent.matches(eq(mockRegion))).thenReturn(true); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(toArray(eventSnapshotMetadata)); SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { @Override public SnapshotServiceAdapter getObject() throws Exception { - return new RegionSnapshotServiceAdapter(mockSnapshotService); + return mockSnapshotService; } }; factoryBean.setExports(toArray(newSnapshotMetadata())); factoryBean.setRegion(mockRegion); - SnapshotMetadata eventSnapshotMetadata = newSnapshotMetadata(snapshotDat); - - SnapshotApplicationEvent event = new ExportSnapshotApplicationEvent(this, "/Example", eventSnapshotMetadata); - assertThat(factoryBean.getExports()[0], is(not(sameInstance(eventSnapshotMetadata)))); assertThat(factoryBean.getRegion(), is(sameInstance(mockRegion))); - assertThat(event.isRegionSnapshotEvent(), is(true)); - assertThat(event.getSnapshotMetadata()[0], is(sameInstance(eventSnapshotMetadata))); - factoryBean.onApplicationEvent(event); + factoryBean.onApplicationEvent(mockSnapshotEvent); - verify(mockRegion, times(1)).getFullPath(); - verify(mockSnapshotOptions, times(1)).setFilter(isNull(SnapshotFilter.class)); - verify(mockSnapshotService, times(1)).createOptions(); - verify(mockSnapshotService, times(1)).save(eq(eventSnapshotMetadata.getLocation()), - eq(eventSnapshotMetadata.getFormat()), eq(mockSnapshotOptions)); + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, times(1)).matches(eq(mockRegion)); + verify(mockSnapshotEvent, times(1)).getSnapshotMetadata(); + verify(mockSnapshotService, times(1)).doExport(eq(eventSnapshotMetadata)); } @Test public void onApplicationEventWhenMatchUsingFactorySnapshotMetadataPerformsImport() throws Exception { - final CacheSnapshotService mockSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + SnapshotApplicationEvent mockSnapshotEvent = mock(ImportSnapshotApplicationEvent.class, + "MockImportSnapshotApplicationEvent"); - SnapshotOptions mockSnapshotOptions = mock(SnapshotOptions.class, "MockSnapshotOptions"); + SnapshotMetadata factorySnapshotMetadata = newSnapshotMetadata(snapshotDat); - when(mockSnapshotService.createOptions()).thenReturn(mockSnapshotOptions); - when(mockSnapshotOptions.setFilter(any(SnapshotFilter.class))).thenReturn(mockSnapshotOptions); + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); + + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(true); + when(mockSnapshotEvent.matches(any(Region.class))).thenReturn(false); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(null); SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { @Override public SnapshotServiceAdapter getObject() throws Exception { - return new CacheSnapshotServiceAdapter(mockSnapshotService); + return mockSnapshotService; } }; - SnapshotMetadata expectedSnapshotMetadata = newSnapshotMetadata(snapshotDat); + factoryBean.setImports(toArray(factorySnapshotMetadata)); - factoryBean.setImports(toArray(expectedSnapshotMetadata)); - - SnapshotApplicationEvent event = new ImportSnapshotApplicationEvent(this); - - assertThat(factoryBean.getImports()[0], is(equalTo(expectedSnapshotMetadata))); + assertThat(factoryBean.getImports()[0], is(equalTo(factorySnapshotMetadata))); assertThat(factoryBean.getRegion(), is(nullValue())); - assertThat(event.isCacheSnapshotEvent(), is(true)); - assertThat(event.getSnapshotMetadata().length, is(equalTo(0))); - factoryBean.onApplicationEvent(event); + factoryBean.onApplicationEvent(mockSnapshotEvent); - verify(mockSnapshotOptions, times(1)).setFilter(isNull(SnapshotFilter.class)); - verify(mockSnapshotService, times(1)).createOptions(); - verify(mockSnapshotService, times(1)).load(eq(new File[] { expectedSnapshotMetadata.getLocation() }), - eq(expectedSnapshotMetadata.getFormat()), eq(mockSnapshotOptions)); + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, never()).matches(any(Region.class)); + verify(mockSnapshotEvent, times(1)).getSnapshotMetadata(); + verify(mockSnapshotService, times(1)).doImport(eq(factorySnapshotMetadata)); } @Test public void onApplicationEventWhenNoMatchDoesNotPerformExport() throws Exception { - final CacheSnapshotService mockSnapshotService = mock(CacheSnapshotService.class, "MockCacheSnapshotService"); + SnapshotApplicationEvent mockSnapshotEvent = mock(ExportSnapshotApplicationEvent.class, + "MockExportSnapshotApplicationEvent"); + + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(false); + when(mockSnapshotEvent.matches(any(Region.class))).thenReturn(false); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(null); + + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { @Override public SnapshotServiceAdapter getObject() throws Exception { - return new CacheSnapshotServiceAdapter(mockSnapshotService); + return mockSnapshotService; } }; factoryBean.setExports(toArray(newSnapshotMetadata())); - SnapshotApplicationEvent event = new ExportSnapshotApplicationEvent(this, "/Example"); - - assertThat(factoryBean.getExports().length, is(equalTo(1))); + assertThat(factoryBean.getExports()[0], isA(SnapshotMetadata.class)); assertThat(factoryBean.getRegion(), is(nullValue())); - assertThat(event.isRegionSnapshotEvent(), is(true)); - factoryBean.onApplicationEvent(event); + factoryBean.onApplicationEvent(mockSnapshotEvent); - verify(mockSnapshotService, never()).createOptions(); - verify(mockSnapshotService, never()).save(any(File.class), any(SnapshotFormat.class)); - verify(mockSnapshotService, never()).save(any(File.class), any(SnapshotFormat.class), - any(SnapshotOptions.class)); + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, times(1)).matches(isNull(Region.class)); + verify(mockSnapshotEvent, never()).getSnapshotMetadata(); + verify(mockSnapshotService, never()).doExport(any(SnapshotMetadata.class)); } @Test public void onApplicationEventWhenNoMatchDoesNotPerformImport() throws Exception { Region mockRegion = mock(Region.class, "MockRegion"); - when(mockRegion.getFullPath()).thenReturn("/Example"); + SnapshotApplicationEvent mockSnapshotEvent = mock(ImportSnapshotApplicationEvent.class, + "MockImportSnapshotApplicationEvent"); - final RegionSnapshotService mockSnapshotService = mock(RegionSnapshotService.class, "MockRegionSnapshotService"); + final SnapshotServiceAdapter mockSnapshotService = mock(SnapshotServiceAdapter.class, "MockSnapshotServiceAdapter"); + + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(false); + when(mockSnapshotEvent.matches(any(Region.class))).thenReturn(false); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(null); SnapshotServiceFactoryBean factoryBean = new SnapshotServiceFactoryBean() { @Override public SnapshotServiceAdapter getObject() throws Exception { - return new RegionSnapshotServiceAdapter(mockSnapshotService); + return mockSnapshotService; } }; factoryBean.setImports(toArray(newSnapshotMetadata())); factoryBean.setRegion(mockRegion); - SnapshotApplicationEvent event = new ImportSnapshotApplicationEvent(this, "/Test"); - - assertThat(factoryBean.getImports().length, is(equalTo(1))); + assertThat(factoryBean.getImports()[0], isA(SnapshotMetadata.class)); assertThat(factoryBean.getRegion(), is(equalTo(mockRegion))); - assertThat(event.isRegionSnapshotEvent(), is(true)); - factoryBean.onApplicationEvent(event); + factoryBean.onApplicationEvent(mockSnapshotEvent); - verify(mockRegion, times(1)).getFullPath(); - verify(mockSnapshotService, never()).createOptions(); - verify(mockSnapshotService, never()).load(any(File.class), any(SnapshotFormat.class)); - verify(mockSnapshotService, never()).load(any(File.class), any(SnapshotFormat.class), - any(SnapshotOptions.class)); + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, times(1)).matches(eq(mockRegion)); + verify(mockSnapshotEvent, never()).getSnapshotMetadata(); + verify(mockSnapshotService, never()).doImport(any(SnapshotMetadata.class)); } @Test public void resolveSnapshotMetadataFromEvent() { SnapshotMetadata eventSnapshotMetadata = newSnapshotMetadata(snapshotDat); - SnapshotMetadata exportSnapshotMetadata = newSnapshotMetadata(); - SnapshotMetadata importSnapshotMetadata = newSnapshotMetadata(FileSystemUtils.USER_HOME); + SnapshotMetadata factoryExportSnapshotMetadata = newSnapshotMetadata(); + SnapshotMetadata factoryImportSnapshotMetadata = newSnapshotMetadata(FileSystemUtils.USER_HOME); - factoryBean.setExports(toArray(exportSnapshotMetadata)); - factoryBean.setImports(toArray(importSnapshotMetadata)); + SnapshotApplicationEvent mockSnapshotEvent = mock(SnapshotApplicationEvent.class, + "MockSnapshotApplicationEvent"); - assertThat(factoryBean.getExports()[0], is(equalTo(exportSnapshotMetadata))); - assertThat(factoryBean.getImports()[0], is(equalTo(importSnapshotMetadata))); - assertThat(factoryBean.resolveSnapshotMetadata(new ExportSnapshotApplicationEvent( - this, eventSnapshotMetadata))[0], is(equalTo(eventSnapshotMetadata))); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(toArray(eventSnapshotMetadata)); + + factoryBean.setExports(toArray(factoryExportSnapshotMetadata)); + factoryBean.setImports(toArray(factoryImportSnapshotMetadata)); + + assertThat(factoryBean.getExports()[0], is(equalTo(factoryExportSnapshotMetadata))); + assertThat(factoryBean.getImports()[0], is(equalTo(factoryImportSnapshotMetadata))); + assertThat(factoryBean.resolveSnapshotMetadata(mockSnapshotEvent)[0], is(equalTo(eventSnapshotMetadata))); + + verify(mockSnapshotEvent, times(1)).getSnapshotMetadata(); } @Test public void resolveExportSnapshotMetadataFromFactory() { - SnapshotMetadata exportSnapshotMetadata = newSnapshotMetadata(); + SnapshotMetadata factoryExportSnapshotMetadata = newSnapshotMetadata(); + SnapshotMetadata factoryImportSnapshotMetadata = newSnapshotMetadata(FileSystemUtils.USER_HOME); - factoryBean.setExports(toArray(exportSnapshotMetadata)); + SnapshotApplicationEvent mockSnapshotEvent = mock(ExportSnapshotApplicationEvent.class, + "MockExportSnapshotApplicationEvent"); - assertThat(factoryBean.getExports()[0], is(equalTo(exportSnapshotMetadata))); - assertThat(factoryBean.resolveSnapshotMetadata(new ExportSnapshotApplicationEvent(this))[0], - is(equalTo(exportSnapshotMetadata))); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(null); + + factoryBean.setExports(toArray(factoryExportSnapshotMetadata)); + factoryBean.setImports(toArray(factoryImportSnapshotMetadata)); + + assertThat(factoryBean.getExports()[0], is(equalTo(factoryExportSnapshotMetadata))); + assertThat(factoryBean.getImports()[0], is(equalTo(factoryImportSnapshotMetadata))); + assertThat(factoryBean.resolveSnapshotMetadata(mockSnapshotEvent)[0], is(equalTo(factoryExportSnapshotMetadata))); + + verify(mockSnapshotEvent, times(1)).getSnapshotMetadata(); } @Test public void resolveImportSnapshotMetadataFromFactory() { - SnapshotMetadata exportSnapshotMetadata = newSnapshotMetadata(); + SnapshotMetadata factoryExportSnapshotMetadata = newSnapshotMetadata(); + SnapshotMetadata factoryImportSnapshotMetadata = newSnapshotMetadata(FileSystemUtils.USER_HOME); - factoryBean.setImports(toArray(exportSnapshotMetadata)); + SnapshotApplicationEvent mockSnapshotEvent = mock(ImportSnapshotApplicationEvent.class, + "MockImportSnapshotApplicationEvent"); - assertThat(factoryBean.getImports()[0], is(equalTo(exportSnapshotMetadata))); - assertThat(factoryBean.resolveSnapshotMetadata(new ImportSnapshotApplicationEvent(this))[0], - is(equalTo(exportSnapshotMetadata))); + when(mockSnapshotEvent.getSnapshotMetadata()).thenReturn(toArray()); + + factoryBean.setExports(toArray(factoryExportSnapshotMetadata)); + factoryBean.setImports(toArray(factoryImportSnapshotMetadata)); + + assertThat(factoryBean.getExports()[0], is(equalTo(factoryExportSnapshotMetadata))); + assertThat(factoryBean.getImports()[0], is(equalTo(factoryImportSnapshotMetadata))); + assertThat(factoryBean.resolveSnapshotMetadata(mockSnapshotEvent)[0], is(equalTo(factoryImportSnapshotMetadata))); + + verify(mockSnapshotEvent, times(1)).getSnapshotMetadata(); } @Test public void withCacheBasedSnapshotServiceOnCacheSnapshotEventIsMatch() { - SnapshotApplicationEvent event = new ExportSnapshotApplicationEvent(this); + SnapshotApplicationEvent mockSnapshotEvent = mock(SnapshotApplicationEvent.class, "MockSnapshotApplicationEvent"); - assertThat(event.isCacheSnapshotEvent(), is(true)); - assertThat(event.isRegionSnapshotEvent(), is(false)); - assertThat(factoryBean.isMatch(event), is(true)); + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(true); + + assertThat(factoryBean.getRegion(), is(nullValue())); + assertThat(factoryBean.isMatch(mockSnapshotEvent), is(true)); + + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, never()).matches(any(Region.class)); } @Test public void withCacheBasedSnapshotServiceOnRegionSnapshotEventIsNotAMatch() { - SnapshotApplicationEvent event = new ImportSnapshotApplicationEvent(this, "/Example"); + SnapshotApplicationEvent mockSnapshotEvent = mock(SnapshotApplicationEvent.class, "MockSnapshotApplicationEvent"); - assertThat(event.isCacheSnapshotEvent(), is(false)); - assertThat(event.isRegionSnapshotEvent(), is(true)); - assertThat(factoryBean.isMatch(event), is(false)); + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(false); + when(mockSnapshotEvent.matches(any(Region.class))).thenReturn(false); + + assertThat(factoryBean.getRegion(), is(nullValue())); + assertThat(factoryBean.isMatch(mockSnapshotEvent), is(false)); + + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, times(1)).matches(isNull(Region.class)); } @Test - public void withRegionBasedSnapshotServiceOnRegionSnapshotEventIsMatch() { - SnapshotApplicationEvent event = new ExportSnapshotApplicationEvent(this, "/Example"); + public void withRegionBasedSnapshotServiceOnCacheSnapshotEventIsMatch() { + SnapshotApplicationEvent mockSnapshotEvent = mock(SnapshotApplicationEvent.class, "MockSnapshotApplicationEvent"); - assertThat(event.isCacheSnapshotEvent(), is(false)); - assertThat(event.isRegionSnapshotEvent(), is(true)); - - Region mockRegion = mock(Region.class, "MockRegion"); - - when(mockRegion.getFullPath()).thenReturn(event.getRegionPath()); - - factoryBean.setRegion(mockRegion); - - assertThat(factoryBean.getRegion(), is(sameInstance(mockRegion))); - assertThat(factoryBean.isMatch(event), is(true)); - - verify(mockRegion, times(1)).getFullPath(); - } - - @Test - public void withRegionBasedSnapshotServiceOnCacheSnapshotEventIsNotAMatch() { - SnapshotApplicationEvent event = new ImportSnapshotApplicationEvent(this); - - assertThat(event.isCacheSnapshotEvent(), is(true)); - assertThat(event.isRegionSnapshotEvent(), is(false)); + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(true); factoryBean.setRegion(mock(Region.class, "MockRegion")); assertThat(factoryBean.getRegion(), is(notNullValue())); - assertThat(factoryBean.isMatch(event), is(false)); + assertThat(factoryBean.isMatch(mockSnapshotEvent), is(true)); + + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, never()).matches(any(Region.class)); + } + + @Test + public void withRegionBasedSnapshotServiceOnRegionSnapshotEventIsMatch() { + Region mockRegion = mock(Region.class, "MockRegion"); + + SnapshotApplicationEvent mockSnapshotEvent = mock(SnapshotApplicationEvent.class, "MockSnapshotApplicationEvent"); + + when(mockSnapshotEvent.isCacheSnapshotEvent()).thenReturn(false); + when(mockSnapshotEvent.matches(eq(mockRegion))).thenReturn(true); + + factoryBean.setRegion(mockRegion); + + assertThat(factoryBean.getRegion(), is(sameInstance(mockRegion))); + assertThat(factoryBean.isMatch(mockSnapshotEvent), is(true)); + + verify(mockSnapshotEvent, times(1)).isCacheSnapshotEvent(); + verify(mockSnapshotEvent, times(1)).matches(eq(mockRegion)); } @Test @@ -636,9 +711,7 @@ public class SnapshotServiceFactoryBeanTest { when(mockSnapshotOptionsOne.setFilter(eq(mockSnapshotFilterOne))).thenReturn(mockSnapshotOptionsOne); when(mockSnapshotOptionsTwo.setFilter(eq(mockSnapshotFilterTwo))).thenReturn(mockSnapshotOptionsTwo); - File snapshotDatTwo = File.createTempFile("snapshot-2", "dat"); - - snapshotDatTwo.deleteOnExit(); + File snapshotDatTwo = mockFile("snapshot-2.dat"); SnapshotMetadata[] expectedImports = toArray(newSnapshotMetadata(snapshotDat, mockSnapshotFilterOne), newSnapshotMetadata(snapshotDatTwo, mockSnapshotFilterTwo)); @@ -1099,14 +1172,14 @@ public class SnapshotServiceFactoryBeanTest { @Test public void createSnapshotMetadataWithFileNullFilterAndGemFireFormat() throws Exception { - SnapshotMetadata metadata = new SnapshotMetadata(snapshotDat, null, SnapshotFormat.GEMFIRE); + SnapshotMetadata snapshotMetadata = new SnapshotMetadata(snapshotDat, null, SnapshotFormat.GEMFIRE); - assertThat(metadata.getLocation(), is(equalTo(snapshotDat))); - assertThat(metadata.isDirectory(), is(false)); - assertThat(metadata.isFile(), is(true)); - assertThat(metadata.getFilter(), is(nullValue())); - assertThat(metadata.isFilterPresent(), is(false)); - assertThat(metadata.getFormat(), is(equalTo(SnapshotFormat.GEMFIRE))); + assertThat(snapshotMetadata.getLocation(), is(equalTo(snapshotDat))); + assertThat(snapshotMetadata.isDirectory(), is(false)); + assertThat(snapshotMetadata.isFile(), is(true)); + assertThat(snapshotMetadata.getFilter(), is(nullValue())); + assertThat(snapshotMetadata.isFilterPresent(), is(false)); + assertThat(snapshotMetadata.getFormat(), is(equalTo(SnapshotFormat.GEMFIRE))); } @Test diff --git a/src/test/java/org/springframework/data/gemfire/SnapshotServiceImportExportIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/SnapshotServiceImportExportIntegrationTest.java index 6fcda363..7a1dd659 100644 --- a/src/test/java/org/springframework/data/gemfire/SnapshotServiceImportExportIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/SnapshotServiceImportExportIntegrationTest.java @@ -49,16 +49,18 @@ import com.gemstone.gemfire.cache.Region; * @see org.springframework.context.support.ClassPathXmlApplicationContext * @see org.springframework.data.gemfire.SnapshotServiceFactoryBean * @see org.springframework.data.gemfire.repository.sample.Person + * @see com.gemstone.gemfire.cache.Region * @since 1.7.0 */ @SuppressWarnings("unused") public class SnapshotServiceImportExportIntegrationTest { - protected static final AtomicLong ID_SEQUENCE = new AtomicLong(0l); + private static final AtomicLong ID_SEQUENCE = new AtomicLong(0l); private static ConfigurableApplicationContext applicationContext; private static File importPeopleSnapshot; + private static File snapshotsDirectory; private static Region people; @@ -87,8 +89,10 @@ public class SnapshotServiceImportExportIntegrationTest { @BeforeClass @SuppressWarnings("unchecked") public static void setupBeforeClass() throws Exception { - File exportDirectory = new File("./gemfire/snapshots/export"); - File importDirectory = new File("./gemfire/snapshots/import"); + snapshotsDirectory = new File(new File(FileSystemUtils.WORKING_DIRECTORY, "gemfire"), "snapshots"); + + File exportDirectory = new File(snapshotsDirectory, "export"); + File importDirectory = new File(snapshotsDirectory, "import"); assertThat(exportDirectory.isDirectory() || exportDirectory.mkdirs(), is(true)); assertThat(importDirectory.isDirectory() || importDirectory.mkdirs(), is(true)); @@ -113,18 +117,17 @@ public class SnapshotServiceImportExportIntegrationTest { public static void tearDownAfterClass() { applicationContext.close(); - File exportPeopleSnapshot = new File("gemfire/snapshots/export/people.snapshot"); + File exportPeopleSnapshot = new File(new File(snapshotsDirectory, "export"), "people.snapshot"); assertThat(exportPeopleSnapshot.isFile(), is(true)); assertThat(exportPeopleSnapshot.length(), is(equalTo(importPeopleSnapshot.length()))); - FileSystemUtils.deleteRecursive(new File(FileSystemUtils.WORKING_DIRECTORY, "gemfire")); + FileSystemUtils.deleteRecursive(snapshotsDirectory.getParentFile()); } @Before public void setup() { //setupPeople(); - ThreadUtils.timedWait(TimeUnit.SECONDS.toMillis(5), 500, new ThreadUtils.WaitCondition() { @Override public boolean waiting() { return !(people.size() > 0); diff --git a/src/test/resources/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest-context.xml b/src/test/resources/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest-context.xml new file mode 100644 index 00000000..a807e309 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/SnapshotApplicationEventTriggeredImportsExportsIntegrationTest-context.xml @@ -0,0 +1,70 @@ + + + + + SnapshotApplicationEventTriggeredImportsExportsIntegrationTest + 0 + warning + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +