diff --git a/spring-data-geode/src/main/java/org/springframework/data/gemfire/RegionResolver.java b/spring-data-geode/src/main/java/org/springframework/data/gemfire/RegionResolver.java new file mode 100644 index 00000000..047d0cb9 --- /dev/null +++ b/spring-data-geode/src/main/java/org/springframework/data/gemfire/RegionResolver.java @@ -0,0 +1,69 @@ +/* + * 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; + +import org.apache.geode.cache.Region; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * The {@link RegionResolver} interface is a {@literal Strategy} interface used to encapsulate different algorithms + * (Strategies) used to resolve a cache {@link Region}. + * + * @author John Blum + * @see org.apache.geode.cache.Region + * @since 2.3.0 + */ +@FunctionalInterface +public interface RegionResolver { + + /** + * Returns a {@link Region} resolved with the given {@link String name}. + * + * @param {@link Class type} of the {@link Region} key. + * @param {@link Class type} of the {@link Region} value; + * @param regionName {@link String name} of the {@link Region} to resolve; may be {@literal null}. + * @return the resolved {@link Region} with the given {@link String name}; may be {@literal null}. + * @see org.apache.geode.cache.Region + * @see java.lang.String + */ + @Nullable Region resolve(@Nullable String regionName); + + /** + * Requires a {@link Region} resolved from the given {@link String name}. + * + * @param {@link Class type} of the {@link Region} key. + * @param {@link Class type} of the {@link Region} value; + * @param regionName {@link String name} of the {@link Region} to resolve; must not be {@literal null}. + * @return the resolved {@link Region} with the given {@link String name}; never {@literal null}. + * @throws IllegalStateException if the resolved {@link Region} is {@literal null}, i.e. does not exist. + * @see org.apache.geode.cache.Region + * @see java.lang.String + * @see #resolve(String) + */ + default @NonNull Region require(@NonNull String regionName) { + + Region region = StringUtils.hasText(regionName) ? resolve(regionName) : null; + + Assert.state(region != null, + () -> String.format("Region with name [%s] not found", regionName)); + + return region; + } +} diff --git a/spring-data-geode/src/test/java/org/springframework/data/gemfire/RegionResolverUnitTests.java b/spring-data-geode/src/test/java/org/springframework/data/gemfire/RegionResolverUnitTests.java new file mode 100644 index 00000000..81a7a5b8 --- /dev/null +++ b/spring-data-geode/src/test/java/org/springframework/data/gemfire/RegionResolverUnitTests.java @@ -0,0 +1,82 @@ +/* + * 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; + +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.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import org.apache.geode.cache.Region; + +/** + * Unit Tests for {@link RegionResolver}. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.apache.geode.cache.Region + * @see org.springframework.data.gemfire.RegionResolver + * @since 2.3.0 + */ +public class RegionResolverUnitTests { + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void requiresRegionForNameReturnsRegion() { + + Region mockRegion = mock(Region.class); + + RegionResolver regionResolver = mock(RegionResolver.class); + + when(regionResolver.require(anyString())).thenCallRealMethod(); + when(regionResolver.resolve(eq("MockRegion"))).thenReturn(mockRegion); + + assertThat(regionResolver.require("MockRegion")).isEqualTo(mockRegion); + + verify(regionResolver, times(1)).resolve(eq("MockRegion")); + verifyNoInteractions(mockRegion); + } + + @Test(expected = IllegalStateException.class) + public void requiredRegionResolvingToNullThrowsIllegalStateException() { + + RegionResolver regionResolver = mock(RegionResolver.class); + + when(regionResolver.require(anyString())).thenCallRealMethod(); + when(regionResolver.resolve(anyString())).thenReturn(null); + + try { + regionResolver.require("TestRegion"); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("Region with name [TestRegion] not found"); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(regionResolver, times(1)).resolve(eq("TestRegion")); + } + } +}