Add Spring Lifecycle-aware CacheDataImporterExporter implementation.

Resolves gh-90.
This commit is contained in:
John Blum
2020-06-09 13:19:26 -07:00
parent cb291c844e
commit 895378be70
2 changed files with 692 additions and 0 deletions

View File

@@ -0,0 +1,408 @@
/*
* Copyright 2020 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
*
* https://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.geode.data.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import org.junit.Test;
import org.apache.geode.cache.Region;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.geode.data.CacheDataImporterExporter;
import org.springframework.geode.data.support.LifecycleAwareCacheDataImporterExporter.ImportLifecycle;
/**
* Unit Tests for {@link LifecycleAwareCacheDataImporterExporter}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.apache.geode.cache.Region
* @see org.springframework.context.ApplicationContext
* @see org.springframework.core.env.Environment
* @see org.springframework.geode.data.CacheDataImporterExporter
* @see org.springframework.geode.data.support.LifecycleAwareCacheDataImporterExporter
* @since 1.3.0
*/
public class LifecycleAwareCacheDataImporterExporterUnitTests {
@Test
public void constructLifecycleAwareCacheDataImporterExporter() {
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
assertThat(importerExporter).isNotNull();
assertThat(importerExporter.getCacheDataImporterExporter()).isEqualTo(mockImporterExporter);
assertThat(importerExporter.getEnvironment().orElse(null)).isNull();
verifyNoInteractions(mockImporterExporter);
}
@Test(expected = IllegalArgumentException.class)
public void constructLifecycleAwareCacheDataImporterExporterWithNull() {
try {
new LifecycleAwareCacheDataImporterExporter(null);
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("The CacheDataImporterExporter to decorate must not be null");
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test
public void setApplicationContextOnWrappedCacheDataImporterExporter() {
ApplicationContext mockApplicationContext = mock(ApplicationContext.class);
ApplicationContextAndEnvironmentAwareCacheDataImporterExporter mockImporterExporter =
mock(ApplicationContextAndEnvironmentAwareCacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
assertThat(importerExporter.getCacheDataImporterExporter()).isEqualTo(mockImporterExporter);
importerExporter.setApplicationContext(mockApplicationContext);
importerExporter.setApplicationContext(null);
verify(mockImporterExporter, times(1)).setApplicationContext(eq(mockApplicationContext));
verifyNoMoreInteractions(mockImporterExporter);
}
@Test
public void setAndGetEnvironment() {
ApplicationContextAndEnvironmentAwareCacheDataImporterExporter mockImporterExporter =
mock(ApplicationContextAndEnvironmentAwareCacheDataImporterExporter.class);
Environment mockEnvironment = mock(Environment.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
assertThat(importerExporter.getCacheDataImporterExporter()).isEqualTo(mockImporterExporter);
assertThat(importerExporter.getEnvironment().orElse(null)).isNull();
importerExporter.setEnvironment(mockEnvironment);
assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment);
importerExporter.setEnvironment(null);
assertThat(importerExporter.getEnvironment().orElse(null)).isNull();
verify(mockImporterExporter, times(1)).setEnvironment(eq(mockEnvironment));
verifyNoMoreInteractions(mockImporterExporter);
}
@Test
public void resolveImportLifecycleCachesResultAndReturnsEager() {
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
Environment mockEnvironment = mock(Environment.class);
doReturn(ImportLifecycle.EAGER.name()).when(mockEnvironment)
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_LIFECYCLE_PROPERTY_NAME),
eq(String.class), eq(ImportLifecycle.getDefault().name()));
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
importerExporter.setEnvironment(mockEnvironment);
assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment);
assertThat(importerExporter.resolveImportLifecycle()).isEqualTo(ImportLifecycle.EAGER);
assertThat(importerExporter.resolveImportLifecycle()).isEqualTo(ImportLifecycle.EAGER);
verify(mockEnvironment, times(1))
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_LIFECYCLE_PROPERTY_NAME),
eq(String.class), eq(ImportLifecycle.getDefault().name()));
verifyNoInteractions(mockImporterExporter);
}
@Test
public void resolveImportLifecycleCachesResultAndReturnsLazy() {
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
Environment mockEnvironment = mock(Environment.class);
doReturn("INVALID").when(mockEnvironment)
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_LIFECYCLE_PROPERTY_NAME),
eq(String.class), eq(ImportLifecycle.getDefault().name()));
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
importerExporter.setEnvironment(mockEnvironment);
assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment);
assertThat(importerExporter.resolveImportLifecycle()).isEqualTo(ImportLifecycle.LAZY);
assertThat(importerExporter.resolveImportLifecycle()).isEqualTo(ImportLifecycle.LAZY);
verify(mockEnvironment, times(1))
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_LIFECYCLE_PROPERTY_NAME),
eq(String.class), eq(ImportLifecycle.getDefault().name()));
verifyNoInteractions(mockImporterExporter);
}
@Test
public void resolveImportPhaseCachesResultAndReturnsIntegerMinValuePlusOneMillion() {
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
Environment mockEnvironment = mock(Environment.class);
doReturn(42).when(mockEnvironment)
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_PHASE_PROPERTY_NAME),
eq(Integer.class), eq(LifecycleAwareCacheDataImporterExporter.DEFAULT_IMPORT_PHASE));
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
importerExporter.setEnvironment(mockEnvironment);
assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment);
assertThat(importerExporter.resolveImportPhase()).isEqualTo(42);
assertThat(importerExporter.resolveImportPhase()).isEqualTo(42);
verify(mockEnvironment, times(1))
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_PHASE_PROPERTY_NAME),
eq(Integer.class), eq(LifecycleAwareCacheDataImporterExporter.DEFAULT_IMPORT_PHASE));
verifyNoInteractions(mockImporterExporter);
}
@Test
public void resolveImportPhaseCachesResultAndReturnsDefaultImportPhase() {
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
Environment mockEnvironment = mock(Environment.class);
doReturn(null).when(mockEnvironment)
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_PHASE_PROPERTY_NAME),
eq(Integer.class), eq(LifecycleAwareCacheDataImporterExporter.DEFAULT_IMPORT_PHASE));
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
importerExporter.setEnvironment(mockEnvironment);
assertThat(importerExporter.getEnvironment().orElse(null)).isEqualTo(mockEnvironment);
assertThat(importerExporter.resolveImportPhase()).isEqualTo(LifecycleAwareCacheDataImporterExporter.DEFAULT_IMPORT_PHASE);
assertThat(importerExporter.resolveImportPhase()).isEqualTo(LifecycleAwareCacheDataImporterExporter.DEFAULT_IMPORT_PHASE);
verify(mockEnvironment, times(1))
.getProperty(eq(LifecycleAwareCacheDataImporterExporter.CACHE_DATA_IMPORT_PHASE_PROPERTY_NAME),
eq(Integer.class), eq(LifecycleAwareCacheDataImporterExporter.DEFAULT_IMPORT_PHASE));
verifyNoInteractions(mockImporterExporter);
}
@Test
public void exportFromRegionCallsWrappedCacheDataImporterExporterExportFrom() {
Region<?, ?> mockRegion = mock(Region.class);
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
new LifecycleAwareCacheDataImporterExporter(mockImporterExporter);
assertThat(importerExporter.getCacheDataImporterExporter()).isEqualTo(mockImporterExporter);
importerExporter.exportFrom(mockRegion);
verify(mockImporterExporter, times(1)).exportFrom(eq(mockRegion));
verifyNoMoreInteractions(mockImporterExporter);
}
@Test
public void importIntoRegionCallsWrappedCacheDataImporterExporterImmediatelyWhenImportLifecycleIsEager() {
Region<?, ?> mockRegionOne = mock(Region.class);
Region<?, ?> mockRegionTwo = mock(Region.class);
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
spy(new LifecycleAwareCacheDataImporterExporter(mockImporterExporter));
doReturn(ImportLifecycle.EAGER).when(importerExporter).resolveImportLifecycle();
assertThat(importerExporter.getCacheDataImporterExporter()).isEqualTo(mockImporterExporter);
assertThat(importerExporter.getRegionsForImport()).isEmpty();
importerExporter.importInto(mockRegionOne);
importerExporter.importInto(mockRegionTwo);
assertThat(importerExporter.getRegionsForImport()).isEmpty();
verify(mockImporterExporter, times(1)).importInto(eq(mockRegionOne));
verify(mockImporterExporter, times(1)).importInto(eq(mockRegionTwo));
verifyNoMoreInteractions(mockImporterExporter);
}
@Test
public void importIntoRegionStoresRegionReferenceWhenImportLifecycleIsLazy() {
Region<?, ?> mockRegionOne = mock(Region.class);
Region<?, ?> mockRegionTwo = mock(Region.class);
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
spy(new LifecycleAwareCacheDataImporterExporter(mockImporterExporter));
doReturn(ImportLifecycle.LAZY).when(importerExporter).resolveImportLifecycle();
assertThat(importerExporter.getCacheDataImporterExporter()).isEqualTo(mockImporterExporter);
assertThat(importerExporter.getRegionsForImport()).isEmpty();
importerExporter.importInto(mockRegionOne);
importerExporter.importInto(mockRegionTwo);
assertThat(importerExporter.getRegionsForImport()).containsExactlyInAnyOrder(mockRegionOne, mockRegionTwo);
verify(mockImporterExporter, never()).importInto(any());
verifyNoMoreInteractions(mockImporterExporter);
verifyNoInteractions(mockRegionOne, mockRegionTwo);
}
@Test
public void startImportsIntoRegionsWhenImportLifecycleIsLazy() {
Region<?, ?> mockRegionOne = mock(Region.class);
Region<?, ?> mockRegionTwo = mock(Region.class);
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
spy(new LifecycleAwareCacheDataImporterExporter(mockImporterExporter));
doReturn(ImportLifecycle.LAZY).when(importerExporter).resolveImportLifecycle();
importerExporter.getRegionsForImport().add(mockRegionOne);
importerExporter.getRegionsForImport().add(mockRegionTwo);
assertThat(importerExporter.getRegionsForImport()).containsExactlyInAnyOrder(mockRegionOne, mockRegionTwo);
importerExporter.start();
verify(mockImporterExporter, times(1)).importInto(eq(mockRegionOne));
verify(mockImporterExporter, times(1)).importInto(eq(mockRegionTwo));
verifyNoMoreInteractions(mockImporterExporter);
verifyNoInteractions(mockRegionOne, mockRegionTwo);
}
@Test
public void startDoesNothingWhenImportLifecycleIsEager() {
Region<?, ?> mockRegionOne = mock(Region.class);
Region<?, ?> mockRegionTwo = mock(Region.class);
CacheDataImporterExporter mockImporterExporter = mock(CacheDataImporterExporter.class);
LifecycleAwareCacheDataImporterExporter importerExporter =
spy(new LifecycleAwareCacheDataImporterExporter(mockImporterExporter));
doReturn(ImportLifecycle.EAGER).when(importerExporter).resolveImportLifecycle();
importerExporter.getRegionsForImport().add(mockRegionOne);
importerExporter.getRegionsForImport().add(mockRegionTwo);
assertThat(importerExporter.getRegionsForImport()).containsExactlyInAnyOrder(mockRegionOne, mockRegionTwo);
importerExporter.start();
assertThat(importerExporter.getRegionsForImport()).containsExactlyInAnyOrder(mockRegionOne, mockRegionTwo);
verify(mockImporterExporter, never()).importInto(any());
verifyNoInteractions(mockRegionOne, mockRegionTwo);
}
@Test
public void importLifecycleDefaultIsLazy() {
assertThat(ImportLifecycle.getDefault()).isEqualTo(ImportLifecycle.LAZY);
}
@Test
public void importLifecycleFromIsCaseInsensitive() {
assertThat(ImportLifecycle.from("Eager")).isEqualTo(ImportLifecycle.EAGER);
assertThat(ImportLifecycle.from("lazy")).isEqualTo(ImportLifecycle.LAZY);
}
@Test
public void importLifecycleFromReturnsEnum() {
for (ImportLifecycle enumeratedValue : ImportLifecycle.values()) {
assertThat(ImportLifecycle.from(enumeratedValue.name())).isEqualTo(enumeratedValue);
}
}
@Test
public void importLifecycleFromUnknownReturnsNull() {
assertThat(ImportLifecycle.from("UNKNOWN")).isNull();
}
@Test
public void importLifecycleIsEager() {
assertThat(ImportLifecycle.EAGER.isEager()).isTrue();
assertThat(ImportLifecycle.LAZY.isEager()).isFalse();
}
@Test
public void importLifecycleIsLazy() {
assertThat(ImportLifecycle.EAGER.isLazy()).isFalse();
assertThat(ImportLifecycle.LAZY.isLazy()).isTrue();
}
@Test
public void importLifecycleToStringIsDescriptive() {
assertThat(ImportLifecycle.EAGER.toString())
.isEqualTo("Imports cache data during Region bean post processing, after initialization");
assertThat(ImportLifecycle.LAZY.toString())
.isEqualTo("Imports cache data during the appropriate phase on Lifecycle start");
}
interface ApplicationContextAndEnvironmentAwareCacheDataImporterExporter
extends ApplicationContextAware, CacheDataImporterExporter, EnvironmentAware { }
}