Add Spring Lifecycle-aware CacheDataImporterExporter implementation.
Resolves gh-90.
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.gemfire.support.SmartLifecycleSupport;
|
||||
import org.springframework.geode.data.CacheDataImporterExporter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link CacheDataImporterExporter} implementation using the {@literal Decorator Software Design Pattern} to wrap
|
||||
* an existing {@link CacheDataImporterExporter} in order to {@literal decorate} the cache ({@link Region}) data
|
||||
* import & export operations, making them Spring {@link ApplicationContext}, {@link Lifecycle} aware and capable.
|
||||
*
|
||||
* This wrapper {@literal decorates} the Apache Geode cache {@link Region Region} data import operation enabling it
|
||||
* to be configured {@link ImportLifecycle#EAGER eagerly}, after the Region bean as been initialized,
|
||||
* or {@link ImportLifecycle#LAZY lazily}, once all beans have been fully initialized and the Spring
|
||||
* {@link ApplicationContext} refreshed.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.context.ApplicationContextAware
|
||||
* @see org.springframework.context.EnvironmentAware
|
||||
* @see org.springframework.context.Lifecycle
|
||||
* @see org.springframework.core.env.Environment
|
||||
* @see org.springframework.data.gemfire.support.SmartLifecycleSupport
|
||||
* @see org.springframework.geode.data.CacheDataImporterExporter
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Decorator_pattern">Decorator Software Design Pattern</a>
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class LifecycleAwareCacheDataImporterExporter
|
||||
implements ApplicationContextAware, CacheDataImporterExporter, EnvironmentAware, SmartLifecycleSupport {
|
||||
|
||||
protected static final int DEFAULT_IMPORT_PHASE = Integer.MIN_VALUE + 1000000;
|
||||
|
||||
protected static final String CACHE_DATA_IMPORT_LIFECYCLE_PROPERTY_NAME =
|
||||
"spring.boot.data.gemfire.cache.data.import.lifecycle";
|
||||
|
||||
protected static final String CACHE_DATA_IMPORT_PHASE_PROPERTY_NAME =
|
||||
"spring.boot.data.gemfire.cache.data.import.phase";
|
||||
|
||||
private final AtomicReference<ImportLifecycle> resolvedImportLifecycle = new AtomicReference<>(null);
|
||||
private final AtomicReference<Integer> resolvedImportPhase = new AtomicReference<>(null);
|
||||
|
||||
private final CacheDataImporterExporter importerExporter;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private final Set<Region> regionsForImport = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link LifecycleAwareCacheDataImporterExporter} initialized with the given,
|
||||
* target {@link CacheDataImporterExporter} that is wrapped by this implementation to decorate all cache import
|
||||
* & export data operations in order to make them {@link Lifecycle} aware and capable.
|
||||
*
|
||||
* @param importerExporter {@link CacheDataImporterExporter} wrapped by this implementation to {@literal decorate}
|
||||
* the cache data import/export operations to be {@link Lifecycle} aware and capable; must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if {@link CacheDataImporterExporter} is {@literal null}.
|
||||
* @see org.springframework.geode.data.CacheDataImporterExporter
|
||||
*/
|
||||
public LifecycleAwareCacheDataImporterExporter(@NonNull CacheDataImporterExporter importerExporter) {
|
||||
|
||||
Assert.notNull(importerExporter, "The CacheDataImporterExporter to decorate must not be null");
|
||||
|
||||
this.importerExporter = importerExporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a reference to the Spring {@link ApplicationContext}.
|
||||
*
|
||||
* @param applicationContext Spring {@link ApplicationContext} in which this component operates.
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
|
||||
CacheDataImporterExporter importerExporter = getCacheDataImporterExporter();
|
||||
|
||||
if (applicationContext != null && importerExporter instanceof ApplicationContextAware) {
|
||||
((ApplicationContextAware) importerExporter).setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the configured {@link CacheDataImporterExporter} wrapped by this {@link Lifecycle} aware
|
||||
* and capable {@link CacheDataImporterExporter}.
|
||||
*
|
||||
* @return the {@link CacheDataImporterExporter} enhanced and used as the delegate for this {@link Lifecycle} aware
|
||||
* and capable {@link CacheDataImporterExporter}; never {@literal null}.
|
||||
*/
|
||||
protected @NonNull CacheDataImporterExporter getCacheDataImporterExporter() {
|
||||
return this.importerExporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a reference to the {@link Environment} used to access the configuration for the behavior of
|
||||
* the cache data import.
|
||||
*
|
||||
* @param environment {@link Environment} used to access context specific configuration for the cache data import.
|
||||
* @see org.springframework.core.env.Environment
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
|
||||
this.environment = environment;
|
||||
|
||||
CacheDataImporterExporter importerExporter = getCacheDataImporterExporter();
|
||||
|
||||
if (environment != null && importerExporter instanceof EnvironmentAware) {
|
||||
((EnvironmentAware) importerExporter).setEnvironment(environment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Optional}, configured reference to the {@link Environment} used to access the configuration
|
||||
* for the behavior of the cache data import.
|
||||
*
|
||||
* If a reference to {@link Environment} was not configured, then this method will return {@link Optional#empty()}.
|
||||
*
|
||||
* @return an {@link Optional} reference to the configured {@link Environment}, or {@link Optional#empty()} if no
|
||||
* {@link Environment} was configured.
|
||||
* @see org.springframework.core.env.Environment
|
||||
* @see #setEnvironment(Environment)
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
protected Optional<Environment> getEnvironment() {
|
||||
return Optional.ofNullable(this.environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return resolveImportPhase();
|
||||
}
|
||||
|
||||
Set<Region> getRegionsForImport() {
|
||||
return this.regionsForImport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the configured {@link ImportLifecycle}.
|
||||
*
|
||||
* The cache data import lifecycle is configured with the
|
||||
* {@literal spring.boot.data.gemfire.cache.data.import.lifecycle} property
|
||||
* in Spring Boot {@literal application.properties}.
|
||||
*
|
||||
* @return the configured {@link ImportLifecycle}.
|
||||
* @see LifecycleAwareCacheDataImporterExporter.ImportLifecycle
|
||||
*/
|
||||
protected ImportLifecycle resolveImportLifecycle() {
|
||||
|
||||
return resolvedImportLifecycle.updateAndGet(currentValue -> currentValue != null ? currentValue
|
||||
: getEnvironment()
|
||||
.map(env -> env.getProperty(CACHE_DATA_IMPORT_LIFECYCLE_PROPERTY_NAME, String.class,
|
||||
ImportLifecycle.getDefault().name()))
|
||||
.map(ImportLifecycle::from)
|
||||
.orElseGet(ImportLifecycle::getDefault));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the configured {@link SmartLifecycleSupport#getPhase() SmartLifecycle Phase} in which the cache data
|
||||
* import will be performed.
|
||||
*
|
||||
* @return the configured {@link SmartLifecycleSupport#getPhase() SmartLifecycle Phase}.
|
||||
* @see #getPhase()
|
||||
*/
|
||||
protected int resolveImportPhase() {
|
||||
|
||||
return resolvedImportPhase.updateAndGet(currentValue -> currentValue != null ? currentValue
|
||||
: getEnvironment()
|
||||
.map(env -> env.getProperty(CACHE_DATA_IMPORT_PHASE_PROPERTY_NAME, Integer.class, DEFAULT_IMPORT_PHASE))
|
||||
.orElse(DEFAULT_IMPORT_PHASE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@NonNull @Override
|
||||
public Region exportFrom(@NonNull Region region) {
|
||||
return getCacheDataImporterExporter().exportFrom(region);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@NonNull @Override
|
||||
public Region importInto(@NonNull Region region) {
|
||||
|
||||
if (resolveImportLifecycle().isEager()) {
|
||||
return getCacheDataImporterExporter().importInto(region);
|
||||
}
|
||||
else {
|
||||
getRegionsForImport().add(region);
|
||||
return region;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the cache data import for each of the targeted {@link Region Regions}.
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
// Technically, the resolveImportLifecycle().isLazy() check is not strictly required since if the cache data
|
||||
// import is "eager", then the regionsForImport Set will be empty anyway.
|
||||
if (resolveImportLifecycle().isLazy()) {
|
||||
getRegionsForImport().forEach(getCacheDataImporterExporter()::importInto);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ImportLifecycle {
|
||||
|
||||
EAGER("Imports cache data during Region bean post processing, after initialization"),
|
||||
LAZY("Imports cache data during the appropriate phase on Lifecycle start");
|
||||
|
||||
private final String description;
|
||||
|
||||
ImportLifecycle(@NonNull String description) {
|
||||
|
||||
Assert.hasText(description, "The enumerated value must have a description");
|
||||
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static @NonNull ImportLifecycle getDefault() {
|
||||
return LAZY;
|
||||
}
|
||||
|
||||
public static @Nullable ImportLifecycle from(String name) {
|
||||
|
||||
for (ImportLifecycle importCycle : values()) {
|
||||
if (importCycle.name().equalsIgnoreCase(name)) {
|
||||
return importCycle;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isEager() {
|
||||
return EAGER.equals(this);
|
||||
}
|
||||
|
||||
public boolean isLazy() {
|
||||
return LAZY.equals(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 { }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user