Adds support to properly clean up and 'reset' the ClusterAwareConfiguration.ClusterAwareCondition state on ApplicationContext closed events.

This commit is contained in:
John Blum
2019-09-12 17:40:08 -07:00
parent bf9bcb866e
commit ff3ddaf586
2 changed files with 97 additions and 30 deletions

View File

@@ -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<Boolean> clusterAvailable = new AtomicReference<>(null);
static void reset() {
private static ApplicationListener<ContextClosedEvent> 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<ConnectionEndpoint> 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.<Boolean>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.<Boolean>doOperationSafely(() -> {
if (socket != null) {
socket.close();
return true;
}
return false;
}, cause -> false);
}
void configureTopology(Environment environment, ConnectionEndpointList connectionEndpoints,
int connectionCount) {

View File

@@ -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