DATAGEODE-244 - Add RegionResolver implementation to resolve a Region by name from a RegionService or CacheResolver.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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 java.util.Optional;
|
||||
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionService;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
|
||||
import org.springframework.data.gemfire.CacheResolver;
|
||||
import org.springframework.data.gemfire.RegionResolver;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link RegionResolver} implementation used to resolve a cache {@link Region} identified by {@link String name}
|
||||
* using the configured {@link RegionService}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.RegionService
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see org.springframework.data.gemfire.RegionResolver
|
||||
* @see org.springframework.data.gemfire.support.AbstractCachingRegionResolver
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class RegionServiceRegionResolver<T extends RegionService> extends AbstractCachingRegionResolver {
|
||||
|
||||
/**
|
||||
* Factory method used to construct a {@link RegionServiceRegionResolver} from a {@link CacheResolver}.
|
||||
*
|
||||
* The {@link CacheResolver} will resolve an instance of {@link GemFireCache}, such as a {@literal peer}
|
||||
* {@link Cache} or a {@link ClientCache}, which is a {@link RegionService} capable of resolving a {@link Region}
|
||||
* identified by {@link String name}.
|
||||
*
|
||||
* @param <S> {@link Class subclass} of {@link GemFireCache}.
|
||||
* @param cacheResolver {@link CacheResolver} used to resolve the {@link RegionService}.
|
||||
* @return a new instance of {@link RegionServiceRegionResolver}.
|
||||
* @throws IllegalArgumentException if {@link CacheResolver} is {@literal null}.
|
||||
* @see #RegionServiceRegionResolver(RegionServiceResolver)
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
*/
|
||||
@NonNull
|
||||
public static <S extends GemFireCache> RegionServiceRegionResolver<S> from(@NonNull CacheResolver<S> cacheResolver) {
|
||||
|
||||
Assert.notNull(cacheResolver, "CacheResolver must not be null");
|
||||
|
||||
return new RegionServiceRegionResolver<>(() -> Optional.ofNullable(cacheResolver.resolve()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to construct a {@link RegionServiceRegionResolver} initialized with
|
||||
* the given {@link RegionService}.
|
||||
*
|
||||
* The {@link RegionService} may be an instance of {@link GemFireCache}, such as a {@literal peer} {@link Cache}
|
||||
* or {@link ClientCache}.
|
||||
*
|
||||
* @param regionService {@link RegionService} used to resolve cache {@link Region Regions}
|
||||
* identified by {@link String name}; may be {@literal null}.
|
||||
* @return a new instance of {@link RegionServiceRegionResolver}.
|
||||
* @see #RegionServiceRegionResolver(RegionServiceResolver)
|
||||
* @see org.apache.geode.cache.RegionService
|
||||
*/
|
||||
@NonNull
|
||||
public static RegionServiceRegionResolver<RegionService> from(@Nullable RegionService regionService) {
|
||||
return new RegionServiceRegionResolver<>(() -> Optional.ofNullable(regionService));
|
||||
}
|
||||
|
||||
private final RegionServiceResolver<T> resolver;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link RegionServiceRegionResolver} initialized with
|
||||
* the given {@link RegionServiceResolver}.
|
||||
*
|
||||
* @param resolver {@link RegionServiceResolver} used to resolve the {@link RegionService} that is used to resolve
|
||||
* cache {@link Region Regions} by {@link String name}.
|
||||
* @throws IllegalArgumentException if {@link RegionServiceResolver} is {@literal null}.
|
||||
* @see RegionServiceResolver
|
||||
*/
|
||||
public RegionServiceRegionResolver(RegionServiceResolver<T> resolver) {
|
||||
|
||||
Assert.notNull(resolver, "RegionServiceResolver must not be null");
|
||||
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured {@link RegionServiceResolver} used to resolve the {@link RegionService} that is then used
|
||||
* to resolve cache {@link Region Regions} by {@link String name}.
|
||||
*
|
||||
* @return the configured {@link RegionServiceResolver}.
|
||||
* @see RegionServiceResolver
|
||||
*/
|
||||
protected RegionServiceResolver<T> getRegionServiceResolver() {
|
||||
return this.resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a cache {@link Region} identified by the given {@link String name} using the configured
|
||||
* {@link RegionService} resolved from the {@link RegionServiceResolver}.
|
||||
*
|
||||
* @param <K> {@link Class type} of the {@link Region} key.
|
||||
* @param <V> {@link Class type} of the {@link Region} value.
|
||||
* @param regionName {@link String name} of the {@link Region} to resolve.
|
||||
* @return the resolved cache {@link Region} identified by the given {@link String name}; may be {@literal null}.
|
||||
* @see org.apache.geode.cache.RegionService#getRegion(String)
|
||||
* @see #getRegionServiceResolver()
|
||||
*/
|
||||
@Nullable @Override
|
||||
protected <K, V> Region<K, V> doResolve(@Nullable String regionName) {
|
||||
|
||||
return getRegionServiceResolver().resolve()
|
||||
.<Region<K, V>>map(regionService -> regionService.getRegion(regionName))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface RegionServiceResolver<T extends RegionService> {
|
||||
Optional<T> resolve();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
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 static org.springframework.data.gemfire.support.RegionServiceRegionResolver.RegionServiceResolver;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.apache.geode.cache.AttributesMutator;
|
||||
import org.apache.geode.cache.Cache;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
import org.apache.geode.cache.RegionService;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
|
||||
import org.springframework.data.gemfire.CacheResolver;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link RegionServiceRegionResolver}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.junit.MockitoJUnitRunner
|
||||
* @see org.apache.geode.cache.Cache
|
||||
* @see org.apache.geode.cache.GemFireCache
|
||||
* @see org.apache.geode.cache.Region
|
||||
* @see org.apache.geode.cache.RegionService
|
||||
* @see org.apache.geode.cache.client.ClientCache
|
||||
* @see org.springframework.data.gemfire.CacheResolver
|
||||
* @see org.springframework.data.gemfire.support.RegionServiceRegionResolver
|
||||
* @see org.springframework.data.gemfire.support.RegionServiceRegionResolver.RegionServiceResolver
|
||||
* @since 2.3.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public class RegionServiceRegionResolverUnitTests {
|
||||
|
||||
@Mock
|
||||
private Region mockRegion;
|
||||
|
||||
@Before
|
||||
public void setupMockRegion() {
|
||||
|
||||
AttributesMutator mockAttributesMutator = mock(AttributesMutator.class);
|
||||
|
||||
when(this.mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator);
|
||||
when(mockAttributesMutator.getRegion()).thenReturn(this.mockRegion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructRegionServiceRegionResolverWithNonNullRegionServiceResolver() {
|
||||
|
||||
RegionServiceResolver mockRegionServiceResolver = mock(RegionServiceResolver.class);
|
||||
|
||||
RegionServiceRegionResolver regionResolver = new RegionServiceRegionResolver(mockRegionServiceResolver);
|
||||
|
||||
assertThat(regionResolver).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isEqualTo(mockRegionServiceResolver);
|
||||
|
||||
verifyNoInteractions(mockRegionServiceResolver);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructRegionServiceRegionResolverWithNull() {
|
||||
|
||||
try {
|
||||
new RegionServiceRegionResolver<>(null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("RegionServiceResolver must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNonNullCacheResolverResolvingClientCache() {
|
||||
|
||||
ClientCache mockClientCache = mock(ClientCache.class);
|
||||
|
||||
when(mockClientCache.getRegion(anyString())).thenReturn(this.mockRegion);
|
||||
|
||||
CacheResolver<ClientCache> mockClientCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
when(mockClientCacheResolver.resolve()).thenReturn(mockClientCache);
|
||||
|
||||
RegionServiceRegionResolver regionResolver = RegionServiceRegionResolver.from(mockClientCacheResolver);
|
||||
|
||||
assertThat(regionResolver).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isNotNull();
|
||||
assertThat(regionResolver.doResolve("TestRegion")).isEqualTo(this.mockRegion);
|
||||
|
||||
verify(mockClientCache, times(1)).getRegion(eq("TestRegion"));
|
||||
verify(mockClientCacheResolver, times(1)).resolve();
|
||||
verifyNoInteractions(this.mockRegion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNonNullCacheResolveResolvingNullCacheResolvesNullRegion() {
|
||||
|
||||
CacheResolver<GemFireCache> mockCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
when(mockCacheResolver.resolve()).thenReturn(null);
|
||||
|
||||
RegionServiceRegionResolver regionResolver = RegionServiceRegionResolver.from(mockCacheResolver);
|
||||
|
||||
assertThat(regionResolver).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isNotNull();
|
||||
assertThat(regionResolver.doResolve("TestRegion")).isNull();
|
||||
|
||||
verify(mockCacheResolver, times(1)).resolve();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNonNullCacheResolverResolvingPeerCache() {
|
||||
|
||||
CacheResolver<Cache> mockPeerCacheResolver = mock(CacheResolver.class);
|
||||
|
||||
RegionServiceRegionResolver regionResolver = RegionServiceRegionResolver.from(mockPeerCacheResolver);
|
||||
|
||||
assertThat(regionResolver).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isNotNull();
|
||||
|
||||
verify(mockPeerCacheResolver, never()).resolve();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void fromNullCacheResolverThrowsIllegalArgumentException() {
|
||||
|
||||
try {
|
||||
RegionServiceRegionResolver.from((CacheResolver<GemFireCache>) null);
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
|
||||
assertThat(expected).hasMessage("CacheResolver must not be null");
|
||||
assertThat(expected).hasNoCause();
|
||||
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNonNullRegionService() {
|
||||
|
||||
RegionService mockRegionService = mock(RegionService.class);
|
||||
|
||||
when(mockRegionService.getRegion(anyString())).thenReturn(this.mockRegion);
|
||||
|
||||
RegionServiceRegionResolver regionResolver = RegionServiceRegionResolver.from(mockRegionService);
|
||||
|
||||
assertThat(regionResolver).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver().resolve().orElse(null)).isEqualTo(mockRegionService);
|
||||
assertThat(regionResolver.doResolve("TestRegion")).isEqualTo(this.mockRegion);
|
||||
|
||||
verify(mockRegionService, times(1)).getRegion(eq("TestRegion"));
|
||||
verifyNoInteractions(this.mockRegion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNullRegionServiceIsNullSafe() {
|
||||
|
||||
RegionServiceRegionResolver regionResolver = RegionServiceRegionResolver.from((RegionService) null);
|
||||
|
||||
assertThat(regionResolver).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isNotNull();
|
||||
assertThat(regionResolver.getRegionServiceResolver().resolve().orElse(null)).isNull();
|
||||
assertThat(regionResolver.doResolve("TestRegion")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveCachesAndReturnsRegion() {
|
||||
|
||||
RegionService mockRegionService = mock(RegionService.class);
|
||||
|
||||
RegionServiceResolver mockRegionServiceResolver = mock(RegionServiceResolver.class);
|
||||
|
||||
when(mockRegionService.getRegion(anyString())).thenReturn(this.mockRegion);
|
||||
when(mockRegionServiceResolver.resolve()).thenReturn(Optional.of(mockRegionService));
|
||||
|
||||
RegionServiceRegionResolver regionResolver = spy(new RegionServiceRegionResolver(mockRegionServiceResolver));
|
||||
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isEqualTo(mockRegionServiceResolver);
|
||||
|
||||
assertThat(regionResolver.resolve("MockRegion")).isEqualTo(this.mockRegion);
|
||||
assertThat(regionResolver.resolve("MockRegion")).isEqualTo(this.mockRegion);
|
||||
|
||||
verify(mockRegionServiceResolver, times(1)).resolve();
|
||||
verify(mockRegionService, times(1)).getRegion("MockRegion");
|
||||
verify(regionResolver, times(1)).doResolve(eq("MockRegion"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveReturnsNullWhenRegionServiceReturnsNull() {
|
||||
|
||||
RegionService mockRegionService = mock(RegionService.class);
|
||||
|
||||
RegionServiceResolver mockRegionServiceResolver = mock(RegionServiceResolver.class);
|
||||
|
||||
when(mockRegionService.getRegion(anyString())).thenReturn(null);
|
||||
when(mockRegionServiceResolver.resolve()).thenReturn(Optional.of(mockRegionService));
|
||||
|
||||
RegionServiceRegionResolver regionResolver = spy(new RegionServiceRegionResolver(mockRegionServiceResolver));
|
||||
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isEqualTo(mockRegionServiceResolver);
|
||||
|
||||
assertThat(regionResolver.resolve("ProxyRegion")).isNull();
|
||||
assertThat(regionResolver.resolve("ProxyRegion")).isNull();
|
||||
|
||||
verify(mockRegionServiceResolver, times(2)).resolve();
|
||||
verify(mockRegionService, times(2)).getRegion("ProxyRegion");
|
||||
verify(regionResolver, times(2)).doResolve(eq("ProxyRegion"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveReturnsNullWhenRegionServiceResolvesToNull() {
|
||||
|
||||
RegionServiceResolver mockRegionServiceResolver = mock(RegionServiceResolver.class);
|
||||
|
||||
RegionServiceRegionResolver regionResolver = spy(new RegionServiceRegionResolver(mockRegionServiceResolver));
|
||||
|
||||
assertThat(regionResolver.getRegionServiceResolver()).isEqualTo(mockRegionServiceResolver);
|
||||
assertThat(regionResolver.resolve("TestRegion")).isNull();
|
||||
assertThat(regionResolver.resolve("TestRegion")).isNull();
|
||||
|
||||
verify(mockRegionServiceResolver, times(2)).resolve();
|
||||
verify(regionResolver, times(2)).doResolve(eq("TestRegion"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user