DATAGEODE-325 - Add FactoryBean for CacheResolver.
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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.data.gemfire.support;
|
||||
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheFactory;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
import org.apache.geode.cache.client.ClientCacheFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.gemfire.CacheResolver;
|
||||
import org.springframework.data.gemfire.client.support.ClientCacheFactoryCacheResolver;
|
||||
import org.springframework.data.gemfire.util.CacheUtils;
|
||||
import org.springframework.data.gemfire.util.CollectionUtils;
|
||||
import org.springframework.data.gemfire.util.SpringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring {@link FactoryBean} used to construct a custom, determined {@link CacheResolver} that strategically
|
||||
* and lazily resolves a cache instance.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see org.springframework.data.gemfire.support.AbstractFactoryBeanSupport
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unused" })
|
||||
public class SmartCacheResolverFactoryBean extends AbstractFactoryBeanSupport<CacheResolver<GemFireCache>>
|
||||
implements InitializingBean {
|
||||
|
||||
/**
|
||||
* Factory method used to construct a new instance of the {@link SmartCacheResolverFactoryBean}.
|
||||
*
|
||||
* @return a new instance of {@link SmartCacheResolverFactoryBean}.
|
||||
* @see org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean
|
||||
*/
|
||||
public static @NonNull SmartCacheResolverFactoryBean create() {
|
||||
return new SmartCacheResolverFactoryBean();
|
||||
}
|
||||
|
||||
private CacheResolver<GemFireCache> cacheResolver;
|
||||
|
||||
private CompositionStrategy compositionStrategy = CompositionStrategy.DEFAULT;
|
||||
|
||||
private GemFireCache cache;
|
||||
|
||||
private List<CacheResolver> configuredCacheResolvers;
|
||||
|
||||
private String cacheBeanName;
|
||||
|
||||
/**
|
||||
* Initializes the {@link CacheResolver} [composition] created by this {@link FactoryBean}.
|
||||
*
|
||||
* @see org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean.CompositionStrategy#use(SmartCacheResolverFactoryBean)
|
||||
* @see #getCompositionStrategy()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
this.cacheResolver = getCompositionStrategy().use(this);
|
||||
|
||||
Assert.state(this.cacheResolver != null,
|
||||
() -> String.format("No CacheResolver was composed with CompositionStrategy [%s]",
|
||||
getCompositionStrategy()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the constructed {@link CacheResolver} instance created by this {@link FactoryBean}.
|
||||
*
|
||||
* @return a reference to the constructed {@link CacheResolver} instance created by this {@link FactoryBean}.
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
*/
|
||||
CacheResolver<GemFireCache> getCacheResolver() {
|
||||
return this.cacheResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully constructed and initialized instance of the {@link CacheResolver}
|
||||
* created by this {@link FactoryBean}.
|
||||
*
|
||||
* If the {@link #afterPropertiesSet()} method has not yet been called, then this method returns a {@literal Proxy}
|
||||
* to the {@link CacheResolver} instance that will eventually be created by this {@link FactoryBean}.
|
||||
*
|
||||
* @return the {@link CacheResolver} instance constructed and initialized by this {@link FactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
* @see CacheResolverProxy
|
||||
*/
|
||||
@NonNull
|
||||
public CacheResolver<GemFireCache> getObject() {
|
||||
return new CacheResolverProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Class type} of the {@link CacheResolver} returned by this {@link FactoryBean}.
|
||||
*
|
||||
* @return the {@link Class type} of the {@link CacheResolver} returned by this {@link FactoryBean}.
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
* @see java.lang.Class
|
||||
*/
|
||||
@Nullable @Override
|
||||
public Class<?> getObjectType() {
|
||||
|
||||
CacheResolver<?> cacheResolver = getCacheResolver();
|
||||
|
||||
return cacheResolver != null ? cacheResolver.getClass() : CacheResolver.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link CacheResolver} capable of resolving a cache instance from the Spring {@link BeanFactory}.
|
||||
*
|
||||
* Returns {@literal null} if the {@link BeanFactory} reference is {@literal null} (i.e. not configured).
|
||||
*
|
||||
* @return a {@link CacheResolver} capable of resolving a cache instance from the Spring {@link BeanFactory}.
|
||||
* @see org.springframework.data.gemfire.support.BeanFactoryCacheResolver
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see #getBeanFactory()
|
||||
*/
|
||||
protected @Nullable CacheResolver newBeanFactoryCacheResolver() {
|
||||
|
||||
// Depending on the application context, there may or may not be a Spring ApplicationContext...
|
||||
// Think Spring on the GemFire/Geode server-side with no Spring container
|
||||
return Optional.ofNullable(getBeanFactory())
|
||||
.map(BeanFactoryCacheResolver::new)
|
||||
.map(cacheResolver -> {
|
||||
getCacheBeanName().ifPresent(cacheResolver::setCacheBeanName);
|
||||
return cacheResolver;
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link CacheResolver} capable of resolving a {@link ClientCache} instance using
|
||||
* the Apache Geode {@link ClientCacheFactory} API.
|
||||
*
|
||||
* @return a {@link CacheResolver} resolving a {@link ClientCache} instance.
|
||||
* @see org.springframework.data.gemfire.client.support.ClientCacheFactoryCacheResolver
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
*/
|
||||
protected @NonNull CacheResolver newClientCacheFactoryCacheResolver() {
|
||||
return new ClientCacheFactoryCacheResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link CacheResolver} capable of resolving a {@literal peer} {@link Cache} instance using
|
||||
* the Apache Geode {@link CacheFactory} API.
|
||||
*
|
||||
* @return a {@link CacheResolver} resolving a {@literal peer} {@link Cache} instance.
|
||||
* @see org.springframework.data.gemfire.support.CacheFactoryCacheResolver
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
*/
|
||||
protected @NonNull CacheResolver newPeerCacheFactoryCacheResolver() {
|
||||
return new CacheFactoryCacheResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link CacheResolver} capable of resolving a single, configured cache instance.
|
||||
*
|
||||
* Returns {@literal null} if a cache instance was not {@link #setCache(GemFireCache) configured}
|
||||
* on this {@link FactoryBean}.
|
||||
*
|
||||
* @return a {@link CacheResolver} capable of resolving a single, configured cache instance.
|
||||
* @see org.springframework.data.gemfire.support.SingleCacheCacheResolver
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see #getCache()
|
||||
*/
|
||||
protected @Nullable CacheResolver newSingleCacheCacheResolver() {
|
||||
|
||||
return getCache()
|
||||
.map(this::newSingleCacheCacheResolver)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private @NonNull CacheResolver newSingleCacheCacheResolver(@NonNull GemFireCache cache) {
|
||||
|
||||
return CacheUtils.isClient(cache)
|
||||
? SingleCacheCacheResolver.from((ClientCache) cache)
|
||||
: SingleCacheCacheResolver.from((Cache) cache);
|
||||
}
|
||||
|
||||
public final void setCache(@Nullable GemFireCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
protected Optional<GemFireCache> getCache() {
|
||||
return Optional.ofNullable(this.cache);
|
||||
}
|
||||
|
||||
public final void setCacheBeanName(@Nullable String cacheBeanName) {
|
||||
this.cacheBeanName = cacheBeanName;
|
||||
}
|
||||
|
||||
protected Optional<String> getCacheBeanName() {
|
||||
|
||||
return Optional.ofNullable(this.cacheBeanName)
|
||||
.filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
public final void setCompositionStrategy(CompositionStrategy compositionStrategy) {
|
||||
this.compositionStrategy = compositionStrategy;
|
||||
}
|
||||
|
||||
protected CompositionStrategy getCompositionStrategy() {
|
||||
return this.compositionStrategy != null ? this.compositionStrategy : CompositionStrategy.DEFAULT;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public final void setConfiguredCacheResolvers(List<CacheResolver> cacheResolvers) {
|
||||
|
||||
// NOTE: Be careful not to add the CacheResolverProxy from this SmartCacheResolverFactoryBean
|
||||
this.configuredCacheResolvers = CollectionUtils.nullSafeList(cacheResolvers).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(this::isNotCacheResolverProxyForThisFactoryBean)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected List<CacheResolver> getConfiguredCacheResolvers() {
|
||||
return this.configuredCacheResolvers;
|
||||
}
|
||||
|
||||
private boolean isCacheResolverProxyForThisFactoryBean(CacheResolver<?> cacheResolver) {
|
||||
|
||||
return cacheResolver instanceof CacheResolverProxy
|
||||
&& ((CacheResolverProxy) cacheResolver).getCacheResolverFactoryBean().equals(this);
|
||||
}
|
||||
|
||||
private boolean isNotCacheResolverProxyForThisFactoryBean(CacheResolver<?> cacheResolver) {
|
||||
return !isCacheResolverProxyForThisFactoryBean(cacheResolver);
|
||||
}
|
||||
|
||||
private List<CacheResolver> resolveCacheResolvers() {
|
||||
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
Assert.state(beanFactory != null,
|
||||
() -> String.format("A Spring context is not present and is required to use [%1$s.%2$s.%3$s]",
|
||||
CompositionStrategy.class.getEnclosingClass().getSimpleName(),
|
||||
CompositionStrategy.class.getSimpleName(), CompositionStrategy.USER_DEFINED.name()));
|
||||
|
||||
List<CacheResolver> orderedCacheResolvers =
|
||||
SpringUtils.getOrderedStreamOfBeansByType(beanFactory, CacheResolver.class)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
setConfiguredCacheResolvers(orderedCacheResolvers);
|
||||
|
||||
return orderedCacheResolvers;
|
||||
}
|
||||
|
||||
public SmartCacheResolverFactoryBean withCache(GemFireCache cache) {
|
||||
setCache(cache);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SmartCacheResolverFactoryBean withCacheBeanName(String cacheBeanName) {
|
||||
setCacheBeanName(cacheBeanName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SmartCacheResolverFactoryBean usingCompositionStrategy(CompositionStrategy compositionStrategy) {
|
||||
setCompositionStrategy(compositionStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
class CacheResolverProxy implements CacheResolver<GemFireCache> {
|
||||
|
||||
Optional<CacheResolver<GemFireCache>> getCacheResolver() {
|
||||
return Optional.ofNullable(getCacheResolverFactoryBean().getCacheResolver());
|
||||
}
|
||||
|
||||
SmartCacheResolverFactoryBean getCacheResolverFactoryBean() {
|
||||
return SmartCacheResolverFactoryBean.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GemFireCache resolve() {
|
||||
|
||||
return getCacheResolver()
|
||||
.map(CacheResolver::resolve)
|
||||
.orElseThrow(() -> newIllegalStateException("CacheResolver was not initialized"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the declaration of the algorithm to use to compose {@link CacheResolver CacheResolvers}
|
||||
* into a composition functioning as a single, complete and cohesive cache resolution strategy.
|
||||
*/
|
||||
enum CompositionStrategy {
|
||||
|
||||
DEFAULT {
|
||||
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
<T extends GemFireCache> CacheResolver<T> use(SmartCacheResolverFactoryBean factoryBean) {
|
||||
|
||||
return ComposableCacheResolver.compose(
|
||||
factoryBean.newSingleCacheCacheResolver(),
|
||||
factoryBean.newBeanFactoryCacheResolver(),
|
||||
factoryBean.newClientCacheFactoryCacheResolver(),
|
||||
factoryBean.newPeerCacheFactoryCacheResolver());
|
||||
}
|
||||
},
|
||||
|
||||
GEODE {
|
||||
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
<T extends GemFireCache> CacheResolver<T> use(SmartCacheResolverFactoryBean factoryBean) {
|
||||
|
||||
return ComposableCacheResolver.compose(
|
||||
factoryBean.newSingleCacheCacheResolver(),
|
||||
factoryBean.newClientCacheFactoryCacheResolver(),
|
||||
factoryBean.newPeerCacheFactoryCacheResolver()
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
SPRING {
|
||||
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
<T extends GemFireCache> CacheResolver<T> use(SmartCacheResolverFactoryBean factoryBean) {
|
||||
|
||||
return ComposableCacheResolver.compose(
|
||||
factoryBean.newSingleCacheCacheResolver(),
|
||||
factoryBean.newBeanFactoryCacheResolver()
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
USER_DEFINED {
|
||||
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
<T extends GemFireCache> CacheResolver<T> use(SmartCacheResolverFactoryBean factoryBean) {
|
||||
|
||||
List<CacheResolver> cacheResolvers = computeIfAbsent(factoryBean);
|
||||
|
||||
Assert.state(isNotEmpty(cacheResolvers), "No CacheResolver beans were defined");
|
||||
|
||||
CacheResolver<T> current = null;
|
||||
|
||||
for (CacheResolver<T> cacheResolver : cacheResolvers) {
|
||||
current = ComposableCacheResolver.compose(current, cacheResolver);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
private synchronized List<CacheResolver> computeIfAbsent(SmartCacheResolverFactoryBean factoryBean) {
|
||||
|
||||
return Optional.ofNullable(factoryBean.getConfiguredCacheResolvers())
|
||||
.filter(this::isNotEmpty)
|
||||
.orElseGet(factoryBean::resolveCacheResolvers);
|
||||
}
|
||||
|
||||
private boolean isNotEmpty(List<?> list) {
|
||||
return !(list == null || list.isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
abstract <T extends GemFireCache> CacheResolver<T> use(SmartCacheResolverFactoryBean factoryBean);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.data.gemfire.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
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.when;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.apache.geode.cache.CacheClosedException;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.gemfire.CacheResolver;
|
||||
import org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean.CacheResolverProxy;
|
||||
import org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean.CompositionStrategy;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration Tests for {@link SmartCacheResolverFactoryBean}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.context.annotation.Bean
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.core.annotation.Order
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.junit4.SpringRunner
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@SuppressWarnings({ "rawtypes", "unused" })
|
||||
public class SmartCacheResolverFactoryBeanIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
// Primary (SUT)
|
||||
private CacheResolver cacheResolver;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("A")
|
||||
private CacheResolver a;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("B")
|
||||
private CacheResolver b;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("C")
|
||||
private CacheResolver c;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("D")
|
||||
private CacheResolver d;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("U")
|
||||
private CacheResolver u;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("X")
|
||||
private CacheResolver x;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("Y")
|
||||
private CacheResolver y;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("Z")
|
||||
private CacheResolver z;
|
||||
|
||||
@Autowired
|
||||
private CacheResolver[] cacheResolvers;
|
||||
|
||||
@Autowired
|
||||
private ClientCache clientCache;
|
||||
|
||||
@Test
|
||||
public void cacheResolversAreInjectedInOrder() {
|
||||
assertThat(this.cacheResolvers).containsExactly(this.y, this.x, this.z, this.b, this.c, this.a, this.d,
|
||||
this.cacheResolver, this.u);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolvesCache() {
|
||||
|
||||
assertThat(this.cacheResolver).isInstanceOf(CacheResolverProxy.class);
|
||||
assertThat(((CacheResolverProxy) this.cacheResolver).getCacheResolver().orElse(null))
|
||||
.isInstanceOf(ComposableCacheResolver.class);
|
||||
assertThat(this.cacheResolver.resolve()).isEqualTo(this.clientCache);
|
||||
|
||||
InOrder inOrder = inOrder(this.a, this.b, this.c, this.d, this.u, this.x, this.y, this.z);
|
||||
|
||||
inOrder.verify(this.y, times(1)).resolve();
|
||||
inOrder.verify(this.x, times(1)).resolve();
|
||||
inOrder.verify(this.z, times(1)).resolve();
|
||||
inOrder.verify(this.b, atLeastOnce()).resolve();
|
||||
inOrder.verify(this.c, times(1)).resolve();
|
||||
inOrder.verify(this.a, times(1)).resolve();
|
||||
inOrder.verify(this.d, never()).resolve();
|
||||
inOrder.verify(this.u, never()).resolve();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
ClientCache mockClientCache() {
|
||||
return mock(ClientCache.class);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean("SmartCacheResolver")
|
||||
SmartCacheResolverFactoryBean smartCacheResolver() {
|
||||
return SmartCacheResolverFactoryBean.create()
|
||||
.usingCompositionStrategy(CompositionStrategy.USER_DEFINED);
|
||||
}
|
||||
|
||||
@Bean("A")
|
||||
@Order(3)
|
||||
CacheResolver a(ClientCache clientCache) {
|
||||
|
||||
CacheResolver mockCacheResolver = mock(CacheResolver.class, "A");
|
||||
|
||||
when(mockCacheResolver.resolve()).thenReturn(clientCache);
|
||||
|
||||
return mockCacheResolver;
|
||||
}
|
||||
|
||||
@Bean("B")
|
||||
@Order(1)
|
||||
CacheResolver b() {
|
||||
|
||||
CacheResolver mockCacheResolver = mock(CacheResolver.class, "B");
|
||||
|
||||
when(mockCacheResolver.resolve()).thenThrow(new CacheClosedException("TEST"));
|
||||
|
||||
return mockCacheResolver;
|
||||
}
|
||||
|
||||
@Bean("C")
|
||||
@Order(2)
|
||||
CacheResolver c() {
|
||||
return mock(CacheResolver.class, "C");
|
||||
}
|
||||
|
||||
@Bean("D")
|
||||
@Order(4)
|
||||
CacheResolver d() {
|
||||
return mock(CacheResolver.class, "D");
|
||||
}
|
||||
|
||||
@Bean("U")
|
||||
CacheResolver unorderedCacheResolver() {
|
||||
return mock(CacheResolver.class, "U");
|
||||
}
|
||||
|
||||
@Bean("X")
|
||||
CacheResolver x() {
|
||||
return spy(new AtOrderCacheResolver());
|
||||
}
|
||||
|
||||
@Bean("Y")
|
||||
CacheResolver y() {
|
||||
return spy(new OrderedCacheResolver());
|
||||
}
|
||||
|
||||
@Bean("Z")
|
||||
CacheResolver z() {
|
||||
return spy(new Z());
|
||||
}
|
||||
}
|
||||
|
||||
@Order(-1)
|
||||
static class AtOrderCacheResolver implements CacheResolver<GemFireCache> {
|
||||
|
||||
@Override
|
||||
public GemFireCache resolve() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class OrderedCacheResolver implements CacheResolver<GemFireCache>, Ordered {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GemFireCache resolve() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Order(0)
|
||||
static abstract class ZeroOrderedCacheResolver implements CacheResolver<GemFireCache> {
|
||||
|
||||
@Override
|
||||
public GemFireCache resolve() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class Z extends ZeroOrderedCacheResolver { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,831 @@
|
||||
/*
|
||||
* 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.data.gemfire.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
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.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.CacheClosedException;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.gemfire.CacheResolver;
|
||||
import org.springframework.data.gemfire.client.support.ClientCacheFactoryCacheResolver;
|
||||
import org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean.CacheResolverProxy;
|
||||
import org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean.CompositionStrategy;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link SmartCacheResolverFactoryBean}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see org.springframework.data.gemfire.support.SmartCacheResolverFactoryBean
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public class SmartCacheResolverFactoryBeanUnitTests {
|
||||
|
||||
private CacheResolver<GemFireCache> resolveCacheResolverFromProxy(CacheResolver<GemFireCache> cacheResolver) {
|
||||
|
||||
return cacheResolver instanceof CacheResolverProxy
|
||||
? ((CacheResolverProxy) cacheResolver).getCacheResolver().orElse(null)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingDefaultCompositionStrategyIsFullyLoaded() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCache(mockClientCache);
|
||||
factoryBean.setCacheBeanName("TestCacheBeanName");
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockClientCache);
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.DEFAULT);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
|
||||
ComposableCacheResolver<?> composed = (ComposableCacheResolver<?>) cacheResolver;
|
||||
|
||||
CacheResolver<?> one = composed.getCacheResolverOne();
|
||||
CacheResolver<?> two = composed.getCacheResolverTwo();
|
||||
|
||||
assertThat(one).isInstanceOf(ComposableCacheResolver.class);
|
||||
assertThat(two).isInstanceOf(CacheFactoryCacheResolver.class);
|
||||
|
||||
two = ((ComposableCacheResolver<?>) one).getCacheResolverTwo();
|
||||
one = ((ComposableCacheResolver<?>) one).getCacheResolverOne();
|
||||
|
||||
assertThat(one).isInstanceOf(ComposableCacheResolver.class);
|
||||
assertThat(two).isInstanceOf(ClientCacheFactoryCacheResolver.class);
|
||||
|
||||
two = ((ComposableCacheResolver<?>) one).getCacheResolverTwo();
|
||||
one = ((ComposableCacheResolver<?>) one).getCacheResolverOne();
|
||||
|
||||
assertThat(one.getClass().getName()).startsWith(SingleCacheCacheResolver.class.getName());
|
||||
assertThat(one.resolve()).isEqualTo(mockClientCache);
|
||||
assertThat(two).isInstanceOf(BeanFactoryCacheResolver.class);
|
||||
assertThat(((BeanFactoryCacheResolver) two).getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(((BeanFactoryCacheResolver) two).getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingDefaultCompositionStrategyIsConditionallyComposed() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isNull();
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
assertThat(((ComposableCacheResolver) cacheResolver).getCacheResolverOne())
|
||||
.isInstanceOf(ClientCacheFactoryCacheResolver.class);
|
||||
assertThat(((ComposableCacheResolver) cacheResolver).getCacheResolverTwo())
|
||||
.isInstanceOf(CacheFactoryCacheResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingDefaultCompositionStrategyResolvesCacheInOrder() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
Cache mockPeerCache = mock(Cache.class);
|
||||
|
||||
CacheResolver mockBeanFactoryCacheResolver = mock(CacheResolver.class);
|
||||
CacheResolver mockClientCacheFactoryCacheResolver = mock(CacheResolver.class);
|
||||
CacheResolver mockPeerCacheFactoryCacheResolver = mock(CacheResolver.class);
|
||||
CacheResolver mockSingleCacheCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = spy(new SmartCacheResolverFactoryBean());
|
||||
|
||||
doReturn(mockBeanFactoryCacheResolver).when(factoryBean).newBeanFactoryCacheResolver();
|
||||
doReturn(mockClientCacheFactoryCacheResolver).when(factoryBean).newClientCacheFactoryCacheResolver();
|
||||
doReturn(mockPeerCacheFactoryCacheResolver).when(factoryBean).newPeerCacheFactoryCacheResolver();
|
||||
doReturn(mockSingleCacheCacheResolver).when(factoryBean).newSingleCacheCacheResolver();
|
||||
|
||||
doThrow(new NoSuchBeanDefinitionException("TEST")).when(mockBeanFactoryCacheResolver).resolve();
|
||||
doReturn(mockPeerCache).when(mockPeerCacheFactoryCacheResolver).resolve();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCache(mockPeerCache);
|
||||
factoryBean.setCacheBeanName("TestCacheBeanName");
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockPeerCache);
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.DEFAULT);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver.resolve()).isEqualTo(mockPeerCache);
|
||||
|
||||
InOrder inOrder = inOrder(mockBeanFactoryCacheResolver, mockClientCacheFactoryCacheResolver,
|
||||
mockPeerCacheFactoryCacheResolver, mockSingleCacheCacheResolver);
|
||||
|
||||
inOrder.verify(mockSingleCacheCacheResolver, times(1)).resolve();
|
||||
inOrder.verify(mockBeanFactoryCacheResolver, atLeastOnce()).resolve();
|
||||
inOrder.verify(mockClientCacheFactoryCacheResolver, times(1)).resolve();
|
||||
inOrder.verify(mockPeerCacheFactoryCacheResolver, times(1)).resolve();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingGeodeCompositionStrategyIsFullyLoaded() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCache(mockClientCache);
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.GEODE);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockClientCache);
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.GEODE);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
|
||||
CacheResolver<?> one = ((ComposableCacheResolver) cacheResolver).getCacheResolverOne();
|
||||
CacheResolver<?> two = ((ComposableCacheResolver) cacheResolver).getCacheResolverTwo();
|
||||
|
||||
assertThat(one).isInstanceOf(ComposableCacheResolver.class);
|
||||
assertThat(two).isInstanceOf(CacheFactoryCacheResolver.class);
|
||||
|
||||
two = ((ComposableCacheResolver) one).getCacheResolverTwo();
|
||||
one = ((ComposableCacheResolver) one).getCacheResolverOne();
|
||||
|
||||
assertThat(one.getClass().getName()).startsWith(SingleCacheCacheResolver.class.getName());
|
||||
assertThat(two).isInstanceOf(ClientCacheFactoryCacheResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingGeodeCompositionStrategyIsConditionallyComposed() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.GEODE);
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.GEODE);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
|
||||
CacheResolver<?> one = ((ComposableCacheResolver) cacheResolver).getCacheResolverOne();
|
||||
CacheResolver<?> two = ((ComposableCacheResolver) cacheResolver).getCacheResolverTwo();
|
||||
|
||||
assertThat(one).isInstanceOf(ClientCacheFactoryCacheResolver.class);
|
||||
assertThat(two).isInstanceOf(CacheFactoryCacheResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingGeodeCompositionStrategyResolvesCacheInOrder() {
|
||||
|
||||
Cache mockPeerCache = mock(Cache.class);
|
||||
|
||||
CacheResolver mockClientCacheFactoryCacheResolver = mock(CacheResolver.class);
|
||||
CacheResolver mockPeerCacheFactoryCacheResolver = mock(CacheResolver.class);
|
||||
CacheResolver mockSingleCacheCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = spy(new SmartCacheResolverFactoryBean());
|
||||
|
||||
doReturn(mockClientCacheFactoryCacheResolver).when(factoryBean).newClientCacheFactoryCacheResolver();
|
||||
doReturn(mockPeerCacheFactoryCacheResolver).when(factoryBean).newPeerCacheFactoryCacheResolver();
|
||||
doReturn(mockSingleCacheCacheResolver).when(factoryBean).newSingleCacheCacheResolver();
|
||||
|
||||
doThrow(new CacheClosedException("TEST")).when(mockClientCacheFactoryCacheResolver).resolve();
|
||||
doReturn(mockPeerCache).when(mockPeerCacheFactoryCacheResolver).resolve();
|
||||
|
||||
factoryBean.setCache(mockPeerCache);
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.GEODE);
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockPeerCache);
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.GEODE);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver.resolve()).isEqualTo(mockPeerCache);
|
||||
|
||||
InOrder inOrder = inOrder(mockClientCacheFactoryCacheResolver,
|
||||
mockPeerCacheFactoryCacheResolver, mockSingleCacheCacheResolver);
|
||||
|
||||
inOrder.verify(mockSingleCacheCacheResolver, times(1)).resolve();
|
||||
inOrder.verify(mockClientCacheFactoryCacheResolver, atLeastOnce()).resolve();
|
||||
inOrder.verify(mockPeerCacheFactoryCacheResolver, times(1)).resolve();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingSpringCompositionStrategyIsFullyLoaded() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCache(mockClientCache);
|
||||
factoryBean.setCacheBeanName("TestCacheBeanName");
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockClientCache);
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.SPRING);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
|
||||
CacheResolver<?> one = ((ComposableCacheResolver) cacheResolver).getCacheResolverOne();
|
||||
CacheResolver<?> two = ((ComposableCacheResolver) cacheResolver).getCacheResolverTwo();
|
||||
|
||||
assertThat(one.getClass().getName()).startsWith(SingleCacheCacheResolver.class.getName());
|
||||
assertThat(two).isInstanceOf(BeanFactoryCacheResolver.class);
|
||||
assertThat(((BeanFactoryCacheResolver) two).getCacheBeanName().orElse(null))
|
||||
.isEqualTo("TestCacheBeanName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingSpringCompositionStrategyIsConditionallyComposedWithBeanFactoryCacheLoader() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.SPRING);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isInstanceOf(BeanFactoryCacheResolver.class);
|
||||
assertThat(((BeanFactoryCacheResolver) cacheResolver).getCacheBeanName().orElse(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingSpringCompositionStrategyIsConditionallyComposedWithSingleCacheCacheLoader() {
|
||||
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setCache(mockClientCache);
|
||||
factoryBean.setCacheBeanName("TestCacheBeanName");
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isNull();
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockClientCache);
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.SPRING);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver.getClass().getName()).startsWith(SingleCacheCacheResolver.class.getName());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void factoryBeanUsingSpringCompositionStrategyComposesNoCacheResolverThrowsIllegalStateException() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setCacheBeanName("TestCacheBeanName");
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isNull();
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.SPRING);
|
||||
|
||||
try {
|
||||
factoryBean.afterPropertiesSet();
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("No CacheResolver was composed with CompositionStrategy [%s]",
|
||||
CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
assertThat(resolveCacheResolverFromProxy(factoryBean.getObject())).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingSpringCompositionStrategyResolvesCacheInOrder() {
|
||||
|
||||
Cache mockCache = mock(Cache.class);
|
||||
|
||||
CacheResolver mockBeanFactoryCacheResolver = mock(CacheResolver.class);
|
||||
CacheResolver mockSingleCacheCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
doReturn(mockCache).when(mockBeanFactoryCacheResolver).resolve();
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = spy(new SmartCacheResolverFactoryBean());
|
||||
|
||||
doReturn(mockBeanFactoryCacheResolver).when(factoryBean).newBeanFactoryCacheResolver();
|
||||
doReturn(mockSingleCacheCacheResolver).when(factoryBean).newSingleCacheCacheResolver();
|
||||
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.SPRING);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
assertThat(cacheResolver.resolve()).isEqualTo(mockCache);
|
||||
|
||||
InOrder inOrder = inOrder(mockBeanFactoryCacheResolver, mockSingleCacheCacheResolver);
|
||||
|
||||
inOrder.verify(mockSingleCacheCacheResolver, times(1)).resolve();
|
||||
inOrder.verify(mockBeanFactoryCacheResolver, times(1)).resolve();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void factoryBeanUsingUserDefinedCompositionStrategy() {
|
||||
|
||||
// Questionable?? Simulate Spring container behavior (i.e. BeanFactory.getBeanProvider(..).orderedStream())
|
||||
// by returning an ordered (sorted) Stream based on @Order annotation/Ordered interface.
|
||||
Stream<CacheResolver<? extends GemFireCache>> cacheResolverStream = Stream.of(spy(new A()), spy(new B()))
|
||||
.sorted(AnnotationAwareOrderComparator.INSTANCE);
|
||||
|
||||
ObjectProvider<CacheResolver<? extends GemFireCache>> mockObjectProvider = mock(ObjectProvider.class);
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
when(mockObjectProvider.orderedStream()).thenReturn(cacheResolverStream);
|
||||
when(mockBeanFactory.getBeanProvider(ArgumentMatchers.<Class>any())).thenReturn(mockObjectProvider);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.USER_DEFINED);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.USER_DEFINED);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
CacheResolver<?> cacheResolver = resolveCacheResolverFromProxy(factoryBean.getObject());
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(ComposableCacheResolver.class);
|
||||
|
||||
CacheResolver<?> one = ((ComposableCacheResolver) cacheResolver).getCacheResolverOne();
|
||||
CacheResolver<?> two = ((ComposableCacheResolver) cacheResolver).getCacheResolverTwo();
|
||||
|
||||
assertThat(one).isInstanceOf(B.class);
|
||||
assertThat(two).isInstanceOf(A.class);
|
||||
assertThat(cacheResolver.resolve()).isEqualTo(B.MOCK_CACHE);
|
||||
|
||||
InOrder inOrder = inOrder(one, two);
|
||||
|
||||
inOrder.verify(one, times(1)).resolve();
|
||||
inOrder.verify(two, never()).resolve();
|
||||
|
||||
verify(mockBeanFactory, times(1)).getBeanProvider(eq(CacheResolver.class));
|
||||
verify(mockObjectProvider, times(1)).orderedStream();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void factoryBeanUsingUserDefinedCompositionStrategyWithNoBeanFactory() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.USER_DEFINED);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isNull();
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.USER_DEFINED);
|
||||
|
||||
try {
|
||||
factoryBean.afterPropertiesSet();
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
|
||||
String expectedMessage = "A Spring context is not present and is required to use [%1$s.%2$s.%3$s]";
|
||||
|
||||
assertThat(expected).hasMessage(expectedMessage, SmartCacheResolverFactoryBean.class.getSimpleName(),
|
||||
CompositionStrategy.class.getSimpleName(), CompositionStrategy.USER_DEFINED);
|
||||
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
assertThat(resolveCacheResolverFromProxy(factoryBean.getObject())).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void factoryBeanUsingUserDefinedCompositionStrategyWithNoCacheResolverBeans() {
|
||||
|
||||
Stream<CacheResolver> mockStream = mock(Stream.class);
|
||||
|
||||
ObjectProvider mockBeanProvider = mock(ObjectProvider.class);
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
when(mockBeanProvider.orderedStream()).thenReturn(mockStream);
|
||||
when(mockBeanFactory.getBeanProvider(eq(CacheResolver.class))).thenReturn(mockBeanProvider);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.USER_DEFINED);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.USER_DEFINED);
|
||||
|
||||
try {
|
||||
factoryBean.afterPropertiesSet();
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("No CacheResolver beans were defined");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
finally {
|
||||
|
||||
assertThat(resolveCacheResolverFromProxy(factoryBean.getObject())).isNull();
|
||||
|
||||
verify(mockBeanFactory, times(1)).getBeanProvider(eq(CacheResolver.class));
|
||||
verify(mockBeanProvider, times(1)).orderedStream();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getObjectReturnsProxy() {
|
||||
|
||||
CacheResolver mockCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = spy(new SmartCacheResolverFactoryBean());
|
||||
|
||||
CacheResolver cacheResolver = factoryBean.getObject();
|
||||
|
||||
assertThat(cacheResolver).isNotNull();
|
||||
assertThat(cacheResolver).isInstanceOf(CacheResolverProxy.class);
|
||||
assertThat(((CacheResolverProxy) cacheResolver).getCacheResolver().orElse(null)).isNull();
|
||||
assertThat(((CacheResolverProxy) cacheResolver).getCacheResolverFactoryBean()).isSameAs(factoryBean);
|
||||
|
||||
doReturn(mockCacheResolver).when(factoryBean).getCacheResolver();
|
||||
|
||||
assertThat(((CacheResolverProxy) cacheResolver).getCacheResolver().orElse(null))
|
||||
.isEqualTo(mockCacheResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getObjectTypeReturnsCacheResolverClassType() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = spy(new SmartCacheResolverFactoryBean());
|
||||
|
||||
doReturn(null).when(factoryBean).getCacheResolver();
|
||||
|
||||
assertThat(factoryBean.getObjectType()).isEqualTo(CacheResolver.class);
|
||||
|
||||
verify(factoryBean, never()).getObject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getObjectTypeReturnsCacheResolverInstanceType() {
|
||||
|
||||
CacheResolver mockCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = spy(new SmartCacheResolverFactoryBean());
|
||||
|
||||
doReturn(mockCacheResolver).when(factoryBean).getCacheResolver();
|
||||
|
||||
assertThat(factoryBean.getObjectType()).isEqualTo(mockCacheResolver.getClass());
|
||||
|
||||
verify(factoryBean, never()).getObject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newBeanFactoryCacheResolverWithCacheBeanName() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
factoryBean.setCacheBeanName("TestCacheBeanName");
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCacheBeanName");
|
||||
|
||||
CacheResolver beanFactoryCacheResolver = factoryBean.newBeanFactoryCacheResolver();
|
||||
|
||||
assertThat(beanFactoryCacheResolver).isInstanceOf(BeanFactoryCacheResolver.class);
|
||||
assertThat(((BeanFactoryCacheResolver) beanFactoryCacheResolver).getCacheBeanName().orElse(null))
|
||||
.isEqualTo("TestCacheBeanName");
|
||||
|
||||
verifyNoInteractions(mockBeanFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newBeanFactoryCacheResolverWithNoCacheBeanName() {
|
||||
|
||||
BeanFactory mockBeanFactory = mock(BeanFactory.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setBeanFactory(mockBeanFactory);
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
|
||||
CacheResolver beanFactoryCacheResolver = factoryBean.newBeanFactoryCacheResolver();
|
||||
|
||||
assertThat(beanFactoryCacheResolver).isInstanceOf(BeanFactoryCacheResolver.class);
|
||||
assertThat(((BeanFactoryCacheResolver) beanFactoryCacheResolver).getCacheBeanName().orElse(null)).isNull();
|
||||
|
||||
verifyNoInteractions(mockBeanFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newBeanFactoryCacheResolverWithNullBeanFactory() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getBeanFactory()).isNull();
|
||||
assertThat(factoryBean.newBeanFactoryCacheResolver()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newClientCacheFactoryCacheResolverIsNotNull() {
|
||||
assertThat(new SmartCacheResolverFactoryBean().newClientCacheFactoryCacheResolver())
|
||||
.isInstanceOf(ClientCacheFactoryCacheResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newPeerCacheFactoryCacheResolverIsNotNull() {
|
||||
assertThat(new SmartCacheResolverFactoryBean().newPeerCacheFactoryCacheResolver())
|
||||
.isInstanceOf(CacheFactoryCacheResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newSingleCacheCacheResolverWithClientCache() {
|
||||
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setCache(mockClientCache);
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockClientCache);
|
||||
assertThat(factoryBean.newSingleCacheCacheResolver().getClass().getName())
|
||||
.startsWith(SingleCacheCacheResolver.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newSingleCacheCacheResolverWithPeerCache() {
|
||||
|
||||
Cache mockPeerCache = mock(Cache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
factoryBean.setCache(mockPeerCache);
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockPeerCache);
|
||||
assertThat(factoryBean.newSingleCacheCacheResolver().getClass().getName())
|
||||
.startsWith(SingleCacheCacheResolver.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newSingleCacheCacheResolverWithNullCache() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
assertThat(factoryBean.newSingleCacheCacheResolver()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetCache() {
|
||||
|
||||
GemFireCache mockCache = mock(GemFireCache.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
|
||||
factoryBean.setCache(mockCache);
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isEqualTo(mockCache);
|
||||
|
||||
factoryBean.setCache(null);
|
||||
|
||||
assertThat(factoryBean.getCache().orElse(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetCacheBeanName() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
|
||||
factoryBean.setCacheBeanName("TestCache");
|
||||
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isEqualTo("TestCache");
|
||||
|
||||
factoryBean.setCacheBeanName(" ");
|
||||
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
|
||||
factoryBean.setCacheBeanName("");
|
||||
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
|
||||
factoryBean.setCacheBeanName(null);
|
||||
|
||||
assertThat(factoryBean.getCacheBeanName().orElse(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetCompositionStrategy() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.DEFAULT);
|
||||
|
||||
factoryBean.setCompositionStrategy(CompositionStrategy.SPRING);
|
||||
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.SPRING);
|
||||
|
||||
factoryBean.setCompositionStrategy(null);
|
||||
|
||||
assertThat(factoryBean.getCompositionStrategy()).isEqualTo(CompositionStrategy.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetConfiguredCacheResolvers() {
|
||||
|
||||
CacheResolver a = mock(CacheResolver.class);
|
||||
CacheResolver b = mock(CacheResolver.class);
|
||||
CacheResolver c = mock(CacheResolver.class);
|
||||
|
||||
List<CacheResolver> cacheResolvers = Arrays.asList(a, null, b, c, null, null);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
assertThat(factoryBean.getConfiguredCacheResolvers()).isNull();
|
||||
|
||||
factoryBean.setConfiguredCacheResolvers(cacheResolvers);
|
||||
|
||||
assertThat(factoryBean.getConfiguredCacheResolvers()).containsExactly(a, b, c);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetConfiguredCacheResolversWithDifferentFactoryBeanCacheResolverReference() {
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBeanOne = new SmartCacheResolverFactoryBean();
|
||||
|
||||
CacheResolver a = mock(CacheResolver.class);
|
||||
CacheResolver b = mock(CacheResolver.class);
|
||||
CacheResolver c = factoryBeanOne.getObject();
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBeanTwo = new SmartCacheResolverFactoryBean();
|
||||
|
||||
List<CacheResolver> cacheResolvers = Arrays.asList(a, b, c);
|
||||
|
||||
factoryBeanTwo.setConfiguredCacheResolvers(cacheResolvers);
|
||||
|
||||
assertThat(factoryBeanTwo.getConfiguredCacheResolvers()).containsExactly(a, b, c);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetConfiguredCacheResolversWithSelfReference() {
|
||||
|
||||
CacheResolver a = mock(CacheResolver.class);
|
||||
CacheResolver b = mock(CacheResolver.class);
|
||||
|
||||
SmartCacheResolverFactoryBean factoryBean = new SmartCacheResolverFactoryBean();
|
||||
|
||||
List<CacheResolver> cacheResolvers = Arrays.asList(a, b, factoryBean.getObject());
|
||||
|
||||
factoryBean.setConfiguredCacheResolvers(cacheResolvers);
|
||||
|
||||
assertThat(factoryBean.getConfiguredCacheResolvers()).containsExactly(a, b);
|
||||
}
|
||||
|
||||
static class A implements CacheResolver<ClientCache>, Ordered {
|
||||
|
||||
@Override
|
||||
public ClientCache resolve() {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
@Order(1)
|
||||
static class B implements CacheResolver<Cache> {
|
||||
|
||||
static final Cache MOCK_CACHE = mock(Cache.class);
|
||||
|
||||
@Override
|
||||
public Cache resolve() {
|
||||
return MOCK_CACHE;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user