diff --git a/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java b/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java index 457b95af..c0087a0d 100644 --- a/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java +++ b/spring-geode/src/main/java/org/springframework/geode/data/AbstractCacheDataImporterExporter.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.apache.geode.cache.Region; @@ -38,6 +39,9 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Abstract base class implementing the {@link CacheDataExporter} and {@link CacheDataImporter} interface in order to * simply import/export data operation implementations in a consistent way. @@ -72,6 +76,25 @@ public abstract class AbstractCacheDataImporterExporter private Environment environment; + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private Predicate> regionPredicate = newRegionPredicate(); + + /** + * Constructs a new instance of {@link Predicate} used to filter {@link Region Regions} on data import/export. + * + * The default {@link Predicate} accepts all {@link Region Regions}. Override the {@link #getRegionPredicate()} + * method to change the default behavior. + * + * @return a new instance of {@link Predicate} used to filter {@link Region Regions} on data import/export. + * @see org.apache.geode.cache.Region + * @see java.util.function.Predicate + * @see #getRegionPredicate() + */ + private Predicate> newRegionPredicate() { + return region -> true; + } + /** * Sets a reference to a {@link ApplicationContext} used by this data importer/exporter to perform its function. * @@ -153,6 +176,29 @@ public abstract class AbstractCacheDataImporterExporter .orElseThrow(() -> newIllegalStateException("Environment was not configured")); } + /** + * Return the configured {@link Logger} to log messages. + * + * @return the configured {@link Logger}. + * @see org.slf4j.Logger + */ + protected Logger getLogger() { + return this.logger; + } + + /** + * Returns the configured {@link Predicate} used to filter {@link Region Regions} on data import/export. + * + * @return the configured {@link Predicate} used to filter {@link Region Regions} on data import/export. + * @see org.apache.geode.cache.Region + * @see java.util.function.Predicate + */ + protected @NonNull Predicate> getRegionPredicate() { + + return Optional.ofNullable(this.regionPredicate) + .orElseGet(this::newRegionPredicate); + } + /** * Exports data contained in the given {@link Region}. * @@ -166,6 +212,7 @@ public abstract class AbstractCacheDataImporterExporter Assert.notNull(region, "Region must not be null"); boolean exportEnabled = getEnvironment() + .filter(environment -> getRegionPredicate().test(region)) .filter(environment -> environment.getProperty(CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME, Boolean.class, DEFAULT_CACHE_DATA_EXPORT_ENABLED)) .isPresent(); @@ -195,6 +242,7 @@ public abstract class AbstractCacheDataImporterExporter Assert.notNull(region, "Region must not be null"); boolean importEnabled = getEnvironment() + .filter(environment -> getRegionPredicate().test(region)) .map(Environment::getActiveProfiles) .map(CollectionUtils::asSet) .map(this::getDefaultProfilesIfEmpty) @@ -212,6 +260,15 @@ public abstract class AbstractCacheDataImporterExporter return importEnabled ? doImportInto(region) : region; } + /** + * Imports data into the given {@link Region}. + * + * @param region {@link Region} to import data into. + * @return the given {@link Region}. + * @see org.apache.geode.cache.Region + */ + protected abstract @NonNull Region doImportInto(@NonNull Region region); + @Nullable Set getDefaultProfilesIfEmpty(@Nullable Set activeProfiles) { Set resolvedProfiles = activeProfiles; @@ -259,14 +316,4 @@ public abstract class AbstractCacheDataImporterExporter boolean isNotSet(String value) { return !StringUtils.hasText((value)); } - - /** - * Imports data into the given {@link Region}. - * - * @param region {@link Region} to import data into. - * @return the given {@link Region}. - * @see org.apache.geode.cache.Region - */ - protected abstract @NonNull Region doImportInto(@NonNull Region region); - } diff --git a/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java b/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java index d587c451..0a7dbbaa 100644 --- a/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java +++ b/spring-geode/src/test/java/org/springframework/geode/data/AbstractCacheDataImporterExporterUnitTests.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import org.junit.Test; @@ -55,6 +56,17 @@ import org.springframework.core.env.Environment; */ public class AbstractCacheDataImporterExporterUnitTests { + private AbstractCacheDataImporterExporter mockAbstractCacheDataImporterExporter() { + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).exportFrom(any()); + doCallRealMethod().when(importerExporter).importInto(any()); + doCallRealMethod().when(importerExporter).getLogger(); + doCallRealMethod().when(importerExporter).getRegionPredicate(); + + return importerExporter; + } @Test public void setAndGetApplicationContext() { @@ -62,24 +74,40 @@ public class AbstractCacheDataImporterExporterUnitTests { AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); - doCallRealMethod().when(importerExporter).setApplicationContext(any()); doCallRealMethod().when(importerExporter).getApplicationContext(); doCallRealMethod().when(importerExporter).requireApplicationContext(); + doCallRealMethod().when(importerExporter).setApplicationContext(any()); assertThat(importerExporter.getApplicationContext().orElse(null)).isNull(); importerExporter.setApplicationContext(mockApplicationContext); assertThat(importerExporter.getApplicationContext().orElse(null)).isEqualTo(mockApplicationContext); - assertThat(importerExporter.requireApplicationContext()).isEqualTo(mockApplicationContext); importerExporter.setApplicationContext(null); assertThat(importerExporter.getApplicationContext().orElse(null)).isNull(); } + @Test + public void callRequireApplicationContextWhenPresent() { + + ApplicationContext mockApplicationContext = mock(ApplicationContext.class); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).getApplicationContext(); + doCallRealMethod().when(importerExporter).requireApplicationContext(); + doCallRealMethod().when(importerExporter).setApplicationContext(any()); + + importerExporter.setApplicationContext(mockApplicationContext); + + assertThat(importerExporter.getApplicationContext().orElse(null)).isEqualTo(mockApplicationContext); + assertThat(importerExporter.requireApplicationContext()).isEqualTo(mockApplicationContext); + } + @Test(expected = IllegalStateException.class) - public void requireApplicationContextWhenAnApplicationContextIsNotConfiguredThrowsIllegalStateException() { + public void calRequireApplicationContextWhenNotPresentThrowsIllegalStateException() { AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); @@ -120,8 +148,25 @@ public class AbstractCacheDataImporterExporterUnitTests { assertThat(importerExporter.getEnvironment().orElse(null)).isNull(); } + @Test + public void callRequireEnvironmentWhenPresent() { + + Environment mockEnvironment = mock(Environment.class); + + AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + + doCallRealMethod().when(importerExporter).getEnvironment(); + doCallRealMethod().when(importerExporter).setEnvironment(any()); + doCallRealMethod().when(importerExporter).requireEnvironment(); + + importerExporter.setEnvironment(mockEnvironment); + + assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment); + assertThat(importerExporter.requireEnvironment()).isEqualTo(mockEnvironment); + } + @Test(expected = IllegalStateException.class) - public void requireEnvironmentWhenAnEnvironmentWasNotConfiguredThrowsIllegalStateException() { + public void callRequireEnvironmentWhenNotPresentThrowsIllegalStateException() { AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); @@ -141,7 +186,7 @@ public class AbstractCacheDataImporterExporterUnitTests { @Test @SuppressWarnings("unchecked") - public void exportFromWhenEnvironmentIsPresentAndPropertyIsTrue() { + public void exportFromWhenEnvironmentIsPresentPropertyIsTrueAndPredicateSaysYes() { Environment mockEnvironment = mock(Environment.class); @@ -149,21 +194,26 @@ public class AbstractCacheDataImporterExporterUnitTests { eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED))) .thenReturn(true); + Predicate> mockPredicate = mock(Predicate.class); + + doReturn(true).when(mockPredicate).test(any(Region.class)); + Region mockRegion = mock(Region.class); - AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + AbstractCacheDataImporterExporter importerExporter = mockAbstractCacheDataImporterExporter(); - doCallRealMethod().when(importerExporter).exportFrom(any()); doReturn(mockRegion).when(importerExporter).doExportFrom(eq(mockRegion)); doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockPredicate).when(importerExporter).getRegionPredicate(); assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(1)).doExportFrom(eq(mockRegion)); verify(mockEnvironment, times(1)) .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME), eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED)); - verify(importerExporter, times(1)).getEnvironment(); - verify(importerExporter, times(1)).doExportFrom(eq(mockRegion)); + verify(mockPredicate, times(1)).test(eq(mockRegion)); verifyNoInteractions(mockRegion); } @@ -173,9 +223,8 @@ public class AbstractCacheDataImporterExporterUnitTests { Region mockRegion = mock(Region.class); - AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + AbstractCacheDataImporterExporter importerExporter = mockAbstractCacheDataImporterExporter(); - doCallRealMethod().when(importerExporter).exportFrom(any()); doReturn(Optional.empty()).when(importerExporter).getEnvironment(); assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); @@ -197,27 +246,50 @@ public class AbstractCacheDataImporterExporterUnitTests { eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED))) .thenReturn(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED); - AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); + AbstractCacheDataImporterExporter importerExporter = mockAbstractCacheDataImporterExporter(); - doCallRealMethod().when(importerExporter).exportFrom(any()); doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, never()).doExportFrom(any(Region.class)); verify(mockEnvironment, times(1)) .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_EXPORT_ENABLED_PROPERTY_NAME), eq(Boolean.class), eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_EXPORT_ENABLED)); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void exportFromWhenPredicateSaysNo() { + + Environment mockEnvironment = mock(Environment.class); + + Predicate> mockPredicate = mock(Predicate.class); + + Region mockRegion = mock(Region.class); + + AbstractCacheDataImporterExporter importerExporter = mockAbstractCacheDataImporterExporter(); + + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockPredicate).when(importerExporter).getRegionPredicate(); + doReturn(false).when(mockPredicate).test(any()); + + assertThat(importerExporter.exportFrom(mockRegion)).isEqualTo(mockRegion); + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(1)).getRegionPredicate(); verify(importerExporter, never()).doExportFrom(any(Region.class)); + verify(mockPredicate, times(1)).test(eq(mockRegion)); + verifyNoInteractions(mockEnvironment); verifyNoInteractions(mockRegion); } @Test(expected = IllegalArgumentException.class) public void exportFromNullRegionThrowsIllegalArgumentException() { - AbstractCacheDataImporterExporter importerExporter = mock(AbstractCacheDataImporterExporter.class); - - doCallRealMethod().when(importerExporter).exportFrom(any()); + AbstractCacheDataImporterExporter importerExporter = mockAbstractCacheDataImporterExporter(); try { importerExporter.exportFrom(null); @@ -239,11 +311,12 @@ public class AbstractCacheDataImporterExporterUnitTests { private AbstractCacheDataImporterExporter callRealMethodsFor(AbstractCacheDataImporterExporter importerExporter) { doCallRealMethod().when(importerExporter).importInto(any()); - doCallRealMethod().when(importerExporter).getDefaultProfilesIfEmpty(any(Set.class)); - doCallRealMethod().when(importerExporter).isNonDefaultProfileSet(any(Set.class)); - doCallRealMethod().when(importerExporter).isImportEnabled(any(Set.class), anyString()); doCallRealMethod().when(importerExporter).commaDelimitedListOfStringsToSet(anyString()); doCallRealMethod().when(importerExporter).containsAny(any(Collection.class), any(Collection.class)); + doCallRealMethod().when(importerExporter).getDefaultProfilesIfEmpty(any(Set.class)); + doCallRealMethod().when(importerExporter).getRegionPredicate(); + doCallRealMethod().when(importerExporter).isImportEnabled(any(Set.class), anyString()); + doCallRealMethod().when(importerExporter).isNonDefaultProfileSet(any(Set.class)); doCallRealMethod().when(importerExporter).isNotSet(anyString()); return importerExporter; @@ -251,10 +324,14 @@ public class AbstractCacheDataImporterExporterUnitTests { @Test @SuppressWarnings("unchecked") - public void importIntoWhenEnvironmentIsPresentAndActiveProfilesPropertiesAreSetAndMatch() { + public void importIntoWhenEnvironmentIsPresentActiveProfilesAreSetAndMatchAndPredicateSaysYes() { Region mockRegion = mock(Region.class); + Predicate> mockPredicate = mock(Predicate.class); + + doReturn(true).when(mockPredicate).test(eq(mockRegion)); + Environment mockEnvironment = mock(Environment.class); when(mockEnvironment.getActiveProfiles()).thenReturn(new String[] { "TEST" }); @@ -267,19 +344,21 @@ public class AbstractCacheDataImporterExporterUnitTests { doReturn(mockRegion).when(importerExporter).doImportInto(eq(mockRegion)); doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockPredicate).when(importerExporter).getRegionPredicate(); doReturn(mockEnvironment).when(importerExporter).requireEnvironment(); assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); - verify(mockEnvironment, times(1)).getActiveProfiles(); - verify(mockEnvironment, times(1)) - .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), - eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); verify(importerExporter, times(1)).getEnvironment(); verify(importerExporter, times(1)).requireEnvironment(); verify(importerExporter, times(1)) .isImportEnabled(eq(Collections.singleton("TEST")), eq(" DEV , TEST")); verify(importerExporter, times(1)).doImportInto(eq(mockRegion)); + verify(mockEnvironment, times(1)).getActiveProfiles(); + verify(mockEnvironment, times(1)) + .getProperty(eq(AbstractCacheDataImporterExporter.CACHE_DATA_IMPORT_ACTIVE_PROFILES_PROPERTY_NAME), + eq(AbstractCacheDataImporterExporter.DEFAULT_CACHE_DATA_IMPORT_ACTIVE_PROFILES)); + verify(mockPredicate, times(1)).test(eq(mockRegion)); verifyNoInteractions(mockRegion); } @@ -364,7 +443,35 @@ public class AbstractCacheDataImporterExporterUnitTests { @Test @SuppressWarnings("unchecked") - public void importIntoWhenImportActiveProfilesPropertyIsNotSetCallsDoImportInto() { + public void importIntoWhenPredicateSaysNo() { + + Region mockRegion = mock(Region.class); + + Predicate> mockPredicate = mock(Predicate.class); + + doReturn(false).when(mockPredicate).test(any()); + + Environment mockEnvironment = mock(Environment.class); + + AbstractCacheDataImporterExporter importerExporter = + callRealMethodsFor(mock(AbstractCacheDataImporterExporter.class)); + + doReturn(Optional.of(mockEnvironment)).when(importerExporter).getEnvironment(); + doReturn(mockPredicate).when(importerExporter).getRegionPredicate(); + + assertThat(importerExporter.importInto(mockRegion)).isEqualTo(mockRegion); + + verify(importerExporter, times(1)).getEnvironment(); + verify(importerExporter, times(1)).getRegionPredicate(); + verify(importerExporter, never()).doImportInto(any(Region.class)); + verify(mockPredicate, times(1)).test(eq(mockRegion)); + verifyNoInteractions(mockEnvironment); + verifyNoInteractions(mockRegion); + } + + @Test + @SuppressWarnings("unchecked") + public void importIntoWhenImportActiveProfilesAreNotSetCallsDoImportInto() { Region mockRegion = mock(Region.class); @@ -475,7 +582,7 @@ public class AbstractCacheDataImporterExporterUnitTests { @Test @SuppressWarnings("unchecked") - public void importIntoWhenEnvironmentActiveProfilesNotSetDefaultProfilesContainImportActiveProfilesCallsDoImportInto() { + public void importIntoWhenEnvironmentActiveProfilesAreNotSetAndDefaultProfilesContainImportActiveProfilesCallsDoImportInto() { Region mockRegion = mock(Region.class);