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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +