DATAGEODE-317 - Add PoolResolver implementation to compose a collection of PoolResolvers.

This commit is contained in:
John Blum
2020-03-25 17:49:31 -07:00
parent e867d155bc
commit 62d4abe012
2 changed files with 424 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
/*
* 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.client.support;
import java.util.Arrays;
import org.apache.geode.cache.client.Pool;
import org.springframework.data.gemfire.client.PoolResolver;
import org.springframework.data.gemfire.util.ArrayUtils;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Composite of {@link PoolResolver PoolResolvers} functioning as a single {@link PoolResolver}.
*
* @author John Blum
* @see org.apache.geode.cache.client.Pool
* @see org.springframework.data.gemfire.client.PoolResolver
* @since 2.3.0
*/
public class ComposablePoolResolver implements PoolResolver {
/**
* Null-safe factory method to compose an array of {@link PoolResolver} objects.
*
* Preserves order in the composition.
*
* @param poolResolvers array of {@link PoolResolver} objects to compose.
* @return a composition from the array of {@link PoolResolver} objects; may return {@literal null}.
* @see org.springframework.data.gemfire.client.PoolResolver
* @see #compose(Iterable)
*/
public static @Nullable PoolResolver compose(@Nullable PoolResolver... poolResolvers) {
return compose(Arrays.asList(ArrayUtils.nullSafeArray(poolResolvers, PoolResolver.class)));
}
/**
* Null-safe factory method to compose an {@link Iterable} of {@link PoolResolver} objects.
*
* Preserves order in the composition if the {@link Iterable} collection-like data structure is ordered,
* like a {@link java.util.List}).
*
* @param poolResolvers {@link Iterable} of {@link PoolResolver} objects to compose.
* @return a composition from the {@link Iterable} of {@link PoolResolver} objects; may return {@literal null}.
* @see org.springframework.data.gemfire.client.PoolResolver
* @see java.lang.Iterable
* @see #compose(PoolResolver, PoolResolver)
*/
public static @Nullable PoolResolver compose(@Nullable Iterable<PoolResolver> poolResolvers) {
PoolResolver current = null;
for (PoolResolver poolResolver : CollectionUtils.nullSafeIterable(poolResolvers)) {
current = compose(current, poolResolver);
}
return current;
}
/**
* Null-safe factory method to compose two {@link PoolResolver} objects in a composition.
*
* @param one first {@link PoolResolver} in the composition.
* @param two second {@link PoolResolver} in the composition.
* @return a composition from the two {@link PoolResolver} objects. Returns the first {@link PoolResolver}
* if the second {@link PoolResolver} is {@literal null}. Returns the second {@link PoolResolver} if the first
* {@link PoolResolver} is {@literal null}. Returns {@literal null} if both {@link PoolResolver} arguments
* are {@literal null}.
* @see org.springframework.data.gemfire.client.PoolResolver
* @see #ComposablePoolResolver(PoolResolver, PoolResolver)
*/
public static @Nullable PoolResolver compose(@Nullable PoolResolver one, @Nullable PoolResolver two) {
return one == null ? two : two == null ? one : new ComposablePoolResolver(one, two);
}
private final PoolResolver poolResolverOne;
private final PoolResolver poolResolverTwo;
/**
* Constructs a new instance of {@link ComposablePoolResolver} initialized and composed of two {@link PoolResolver}
* implementations that will function as one.
*
* @param poolResolverOne first {@link PoolResolver} in the composition order.
* @param poolResolverTwo second {@link PoolResolver} in the composition order.
* @throws IllegalArgumentException if either the first or second {@link PoolResolver} are {@literal null}.
* @see org.springframework.data.gemfire.client.PoolResolver
*/
protected ComposablePoolResolver(PoolResolver poolResolverOne, PoolResolver poolResolverTwo) {
Assert.notNull(poolResolverOne, "PoolResolver 1 must not be null");
Assert.notNull(poolResolverTwo, "PoolResolver 2 must not be null");
this.poolResolverOne = poolResolverOne;
this.poolResolverTwo = poolResolverTwo;
}
/**
* Returns a reference to the first {@link PoolResolver} in the composition.
*
* @return a reference to the first {@link PoolResolver} in the composition.
* @see org.springframework.data.gemfire.client.PoolResolver
*/
protected @NonNull PoolResolver getPoolResolverOne() {
return this.poolResolverOne;
}
/**
* Returns a reference to the second {@link PoolResolver} in the composition.
*
* @return a reference to the second {@link PoolResolver} in the composition.
* @see org.springframework.data.gemfire.client.PoolResolver
*/
protected @NonNull PoolResolver getPoolResolverTwo() {
return this.poolResolverTwo;
}
/**
* Attempts to resolve a {@link Pool} with the given {@link String name} by delegating to the composed
* {@link PoolResolver} objects.
*
* The first {@link PoolResolver} in the composition to resolve a {@link Pool} with the given {@link String name}
* stops the resolution process and returns the target {@link Pool}. If no {@link Pool} with the given
* {@link String name} can be resolved by any {@link PoolResolver} in the composition, then {@literal null}
* will be returned.
*
* @param poolName {@link String name} of the {@link Pool} to resolve.
* @return the resolved {@link Pool} or {@literal null} if a {@link Pool} with {@link String name}
* cannot be resolved.
* @see org.apache.geode.cache.client.Pool
* @see #getPoolResolverOne()
* @see #getPoolResolverTwo()
*/
@Nullable @Override
public Pool resolve(@Nullable String poolName) {
Pool pool = getPoolResolverOne().resolve(poolName);
return pool != null ? pool : getPoolResolverTwo().resolve(poolName);
}
}

View File

@@ -0,0 +1,268 @@
/*
* 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.client.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.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.function.Supplier;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.client.Pool;
import org.springframework.data.gemfire.client.PoolResolver;
/**
* Unit Tests for {@link ComposablePoolResolver}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.apache.geode.cache.client.Pool
* @see org.springframework.data.gemfire.client.PoolResolver
* @see org.springframework.data.gemfire.client.support.ComposablePoolResolver
* @since 2.3.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ComposablePoolResolverUnitTests {
@Mock
private Pool mockPool;
@Mock(name = "one")
private PoolResolver mockPoolResolverOne;
@Mock(name = "two")
private PoolResolver mockPoolResolverTwo;
@Mock(name = "three")
private PoolResolver mockPoolResolverThree;
@Test
@SuppressWarnings("all")
public void composeWithArrayOfPoolResolvers() {
PoolResolver poolResolver = ComposablePoolResolver
.compose(this.mockPoolResolverOne, this.mockPoolResolverTwo, this.mockPoolResolverThree);
assertThat(poolResolver).isInstanceOf(ComposablePoolResolver.class);
PoolResolver one = ((ComposablePoolResolver) poolResolver).getPoolResolverOne();
PoolResolver two = ((ComposablePoolResolver) poolResolver).getPoolResolverTwo();
assertThat(one).isInstanceOf(ComposablePoolResolver.class);
assertThat(((ComposablePoolResolver) one).getPoolResolverOne()).isEqualTo(this.mockPoolResolverOne);
assertThat(((ComposablePoolResolver) one).getPoolResolverTwo()).isEqualTo(this.mockPoolResolverTwo);
assertThat(two).isEqualTo(this.mockPoolResolverThree);
}
@Test
public void composeWithArrayContainingSinglePoolResolver() {
assertThat(ComposablePoolResolver.compose(this.mockPoolResolverOne))
.isEqualTo(this.mockPoolResolverOne);
}
@Test
public void composeWithArrayContainingNonNullPoolResolverAndNullPoolResolver() {
PoolResolver[] poolResolvers = { this.mockPoolResolverOne, null };
assertThat(ComposablePoolResolver.compose(poolResolvers)).isEqualTo(this.mockPoolResolverOne);
}
@Test
@SuppressWarnings("all")
public void composeWithArrayContainingNullAndNonNullPoolResolvers() {
PoolResolver[] poolResolvers = { null, this.mockPoolResolverOne, null, this.mockPoolResolverTwo, null };
PoolResolver poolResolver = ComposablePoolResolver.compose(poolResolvers);
assertThat(poolResolver).isInstanceOf(ComposablePoolResolver.class);
assertThat(((ComposablePoolResolver) poolResolver).getPoolResolverOne()).isEqualTo(this.mockPoolResolverOne);
assertThat(((ComposablePoolResolver) poolResolver).getPoolResolverTwo()).isEqualTo(this.mockPoolResolverTwo);
}
@Test
@SuppressWarnings("all")
public void composeWithArrayContainingOrganizedNullAndNonNullPoolResolvers() {
PoolResolver[] poolResolvers = {
null, this.mockPoolResolverOne, this.mockPoolResolverTwo, null, this.mockPoolResolverThree, null, null
};
PoolResolver poolResolver = ComposablePoolResolver.compose(poolResolvers);
assertThat(poolResolver).isInstanceOf(ComposablePoolResolver.class);
PoolResolver one = ((ComposablePoolResolver) poolResolver).getPoolResolverOne();
PoolResolver two = ((ComposablePoolResolver) poolResolver).getPoolResolverTwo();
assertThat(one).isInstanceOf(ComposablePoolResolver.class);
assertThat(((ComposablePoolResolver) one).getPoolResolverOne()).isEqualTo(this.mockPoolResolverOne);
assertThat(((ComposablePoolResolver) one).getPoolResolverTwo()).isEqualTo(this.mockPoolResolverTwo);
assertThat(two).isEqualTo(this.mockPoolResolverThree);
}
@Test
public void composeWithEmptyArrayReturnsNull() {
assertThat(ComposablePoolResolver.compose()).isNull();
}
@Test
public void composeWithNullArrayIsNullSafe() {
assertThat(ComposablePoolResolver.compose((PoolResolver[]) null)).isNull();
}
@Test
public void composeWithNullIterableIsNullSafe() {
assertThat(ComposablePoolResolver.compose((Iterable<PoolResolver>) null)).isNull();
}
@Test
public void composeWithAllNullPoolResolvers() {
assertThat(ComposablePoolResolver.compose(null, null)).isNull();
}
@Test
public void composeWithSingleNullPoolResolver() {
assertThat(ComposablePoolResolver.compose(this.mockPoolResolverOne, null))
.isEqualTo(this.mockPoolResolverOne);
assertThat(ComposablePoolResolver.compose(null, this.mockPoolResolverTwo))
.isEqualTo(this.mockPoolResolverTwo);
}
@Test
public void composeWithTwoNonNullPoolResolvers() {
PoolResolver poolResolver = ComposablePoolResolver.compose(this.mockPoolResolverOne, this.mockPoolResolverTwo);
assertThat(poolResolver).isInstanceOf(ComposablePoolResolver.class);
assertThat(((ComposablePoolResolver) poolResolver).getPoolResolverOne()).isEqualTo(this.mockPoolResolverOne);
assertThat(((ComposablePoolResolver) poolResolver).getPoolResolverTwo()).isEqualTo(this.mockPoolResolverTwo);
}
@Test
public void constructComposablePoolResolver() {
ComposablePoolResolver poolResolver =
new ComposablePoolResolver(this.mockPoolResolverOne, this.mockPoolResolverTwo);
assertThat(poolResolver).isNotNull();
assertThat(poolResolver.getPoolResolverOne()).isEqualTo(this.mockPoolResolverOne);
assertThat(poolResolver.getPoolResolverTwo()).isEqualTo(this.mockPoolResolverTwo);
}
public void testConstructComposablePoolResolverWithNullPoolResolver(Supplier<ComposablePoolResolver> constructor,
String expectedMessage) {
try {
constructor.get();
}
catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage(expectedMessage);
assertThat(expected).hasNoCause();
throw expected;
}
}
@Test(expected = IllegalArgumentException.class)
public void constructComposablePoolResolverWithNullPoolResolverAsFirstArgument() {
testConstructComposablePoolResolverWithNullPoolResolver(
() -> new ComposablePoolResolver(null, this.mockPoolResolverTwo),
"PoolResolver 1 must not be null");
}
@Test(expected = IllegalArgumentException.class)
public void constructComposablePoolResolverWithNullPoolResolverAsSecondArgument() {
testConstructComposablePoolResolverWithNullPoolResolver(
() -> new ComposablePoolResolver(this.mockPoolResolverOne, null),
"PoolResolver 2 must not be null");
}
@Test
public void resolveReturnsFromFirstPoolResolver() {
when(this.mockPoolResolverOne.resolve(anyString())).thenReturn(this.mockPool);
PoolResolver poolResolver = ComposablePoolResolver.compose(this.mockPoolResolverOne, this.mockPoolResolverTwo);
assertThat(poolResolver.resolve("TestPool")).isEqualTo(this.mockPool);
verify(this.mockPoolResolverOne, times(1)).resolve(eq("TestPool"));
verify(this.mockPoolResolverTwo, never()).resolve(anyString());
}
@Test
public void resolveReturnsFromSecondPoolResolver() {
when(this.mockPoolResolverTwo.resolve(anyString())).thenReturn(this.mockPool);
PoolResolver poolResolver = ComposablePoolResolver.compose(this.mockPoolResolverOne, this.mockPoolResolverTwo);
assertThat(poolResolver.resolve("TestPool")).isEqualTo(this.mockPool);
verify(this.mockPoolResolverOne, times(1)).resolve(eq("TestPool"));
verify(this.mockPoolResolverTwo, times(1)).resolve(eq("TestPool"));
}
@Test
@SuppressWarnings("all")
public void resolveReturnsPoolFromLastPoolResolverInOrder() {
when(this.mockPoolResolverTwo.resolve(anyString())).thenReturn(this.mockPool);
PoolResolver poolResolver = ComposablePoolResolver.compose(null, this.mockPoolResolverThree,
null , null, this.mockPoolResolverOne, null, this.mockPoolResolverTwo, null, null);
assertThat(poolResolver.resolve("TestPool")).isEqualTo(this.mockPool);
InOrder ordered = inOrder(this.mockPoolResolverThree, this.mockPoolResolverOne, this.mockPoolResolverTwo);
ordered.verify(this.mockPoolResolverThree, times(1)).resolve(eq("TestPool"));
ordered.verify(this.mockPoolResolverOne, times(1)).resolve(eq("TestPool"));
ordered.verify(this.mockPoolResolverTwo, times(1)).resolve(eq("TestPool"));
}
@Test
public void resolveReturnsNull() {
PoolResolver poolResolver = ComposablePoolResolver.compose(this.mockPoolResolverOne, this.mockPoolResolverTwo);
assertThat(poolResolver.resolve("TestPool")).isNull();
verify(this.mockPoolResolverOne, times(1)).resolve(eq("TestPool"));
verify(this.mockPoolResolverTwo, times(1)).resolve(eq("TestPool"));
}
}