diff --git a/spring-geode/src/main/java/org/springframework/geode/config/annotation/ClusterAwareConfiguration.java b/spring-geode/src/main/java/org/springframework/geode/config/annotation/ClusterAwareConfiguration.java index a7f5aea0..81b08dd7 100644 --- a/spring-geode/src/main/java/org/springframework/geode/config/annotation/ClusterAwareConfiguration.java +++ b/spring-geode/src/main/java/org/springframework/geode/config/annotation/ClusterAwareConfiguration.java @@ -23,6 +23,7 @@ import java.net.SocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; @@ -32,10 +33,13 @@ import org.apache.geode.cache.server.CacheServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.event.ContextClosedEvent; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; @@ -59,10 +63,13 @@ import org.springframework.util.StringUtils; * @see java.net.Socket * @see java.net.SocketAddress * @see org.apache.geode.cache.server.CacheServer + * @see org.springframework.context.ApplicationListener + * @see org.springframework.context.ConfigurableApplicationContext * @see org.springframework.context.annotation.Condition * @see org.springframework.context.annotation.ConditionContext * @see org.springframework.context.annotation.Configuration * @see org.springframework.context.annotation.Import + * @see org.springframework.context.event.ContextClosedEvent * @see org.springframework.core.env.ConfigurableEnvironment * @see org.springframework.core.env.Environment * @see org.springframework.core.env.PropertySource @@ -92,11 +99,15 @@ public class ClusterAwareConfiguration extends AbstractAnnotationConfigSupport { } @SuppressWarnings("unused") - static class ClusterAwareCondition implements Condition { + public static class ClusterAwareCondition implements Condition { private static final AtomicReference clusterAvailable = new AtomicReference<>(null); - static void reset() { + private static ApplicationListener clusterAwareConditionResetApplicationListener() { + return contextClosedEvent-> reset(); + } + + public static void reset() { clusterAvailable.set(null); } @@ -104,22 +115,46 @@ public class ClusterAwareConfiguration extends AbstractAnnotationConfigSupport { public synchronized boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { if (clusterAvailable.get() == null) { - - Environment environment = context.getEnvironment(); - - ConnectionEndpointList connectionEndpoints = new ConnectionEndpointList(getDefaultConnectionEndpoints()) - .add(getConfiguredConnectionEndpoints(environment)); - - int connectionCount = countConnections(connectionEndpoints); - - configureTopology(environment, connectionEndpoints, connectionCount); - - clusterAvailable.set(isMatch(connectionEndpoints, connectionCount)); + registerApplicationListener(context); + doMatch(context); } return Boolean.TRUE.equals(clusterAvailable.get()); } + ConditionContext registerApplicationListener(ConditionContext conditionContext) { + + Optional.ofNullable(conditionContext) + .map(ConditionContext::getResourceLoader) + .filter(ConfigurableApplicationContext.class::isInstance) + .map(ConfigurableApplicationContext.class::cast) + .ifPresent(applicationContext -> + applicationContext.addApplicationListener(clusterAwareConditionResetApplicationListener())); + + return conditionContext; + } + + ConditionContext doMatch(ConditionContext conditionContext) { + + Environment environment = conditionContext.getEnvironment(); + + ConnectionEndpointList connectionEndpoints = + new ConnectionEndpointList(getDefaultConnectionEndpoints()) + .add(getConfiguredConnectionEndpoints(environment)); + + int connectionCount = countConnections(connectionEndpoints); + + configureTopology(environment, connectionEndpoints, connectionCount); + + clusterAvailable.set(isMatch(connectionEndpoints, connectionCount)); + + return conditionContext; + } + + Logger getLogger() { + return logger; + } + List getDefaultConnectionEndpoints() { return Arrays.asList( @@ -190,23 +225,6 @@ public class ClusterAwareConfiguration extends AbstractAnnotationConfigSupport { return array; } - Logger getLogger() { - return logger; - } - - private boolean close(Socket socket) { - return ObjectUtils.doOperationSafely(() -> { - - if (socket != null) { - socket.close(); - return true; - } - - return false; - - }, cause -> false); - } - int countConnections(ConnectionEndpointList connectionEndpoints) { int count = 0; @@ -249,6 +267,20 @@ public class ClusterAwareConfiguration extends AbstractAnnotationConfigSupport { return socket; } + boolean close(Socket socket) { + + return ObjectUtils.doOperationSafely(() -> { + + if (socket != null) { + socket.close(); + return true; + } + + return false; + + }, cause -> false); + } + void configureTopology(Environment environment, ConnectionEndpointList connectionEndpoints, int connectionCount) { diff --git a/spring-geode/src/test/java/org/springframework/geode/config/annotation/ClusterAwareConfigurationUnitTests.java b/spring-geode/src/test/java/org/springframework/geode/config/annotation/ClusterAwareConfigurationUnitTests.java index 406db31b..120a23ae 100644 --- a/spring-geode/src/test/java/org/springframework/geode/config/annotation/ClusterAwareConfigurationUnitTests.java +++ b/spring-geode/src/test/java/org/springframework/geode/config/annotation/ClusterAwareConfigurationUnitTests.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -40,7 +41,11 @@ import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.data.gemfire.support.ConnectionEndpoint; @@ -51,9 +56,14 @@ import org.springframework.data.gemfire.tests.integration.IntegrationTestsSuppor * Unit Tests for {@link EnableClusterAware} and {@link ClusterAwareConfiguration}. * * @author John Blum + * @see java.net.Socket + * @see java.util.Properties * @see org.junit.Test * @see org.mockito.Mockito * @see org.mockito.Spy + * @see org.springframework.context.ApplicationListener + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.annotation.ConditionContext * @see org.springframework.core.env.ConfigurableEnvironment * @see org.springframework.data.gemfire.support.ConnectionEndpoint * @see org.springframework.data.gemfire.support.ConnectionEndpointList @@ -69,7 +79,32 @@ public class ClusterAwareConfigurationUnitTests extends IntegrationTestsSupport @Before @After public void setupAndTearDown() { + System.clearProperty(ClusterAwareConfiguration.SPRING_DATA_GEMFIRE_CACHE_CLIENT_REGION_SHORTCUT_PROPERTY); + ClusterAwareConfiguration.ClusterAwareCondition.reset(); + } + + @Test + public void matchRegistersApplicationListenerCallsDoMatch() { + + ConditionContext mockConditionContext = mock(ConditionContext.class); + + ConfigurableApplicationContext mockApplicationContext = mock(ConfigurableApplicationContext.class); + + Environment mockEnvironment = mock(Environment.class); + + when(mockConditionContext.getEnvironment()).thenReturn(mockEnvironment); + when(mockConditionContext.getResourceLoader()).thenReturn(mockApplicationContext); + + ClusterAwareConfiguration.ClusterAwareCondition condition = + spy(new ClusterAwareConfiguration.ClusterAwareCondition()); + + assertThat(condition.matches(mockConditionContext, null)).isFalse(); + + verify(mockApplicationContext, times(1)) + .addApplicationListener(isA(ApplicationListener.class)); + + verify(condition, times(1)).doMatch(eq(mockConditionContext)); } @Test