From 0a9a2aeafcaf3621b5eb9a72105a0d6839245db9 Mon Sep 17 00:00:00 2001 From: Tomas Hofman Date: Mon, 30 May 2022 08:55:25 +0200 Subject: [PATCH] MODCLUSTER-754 Modify ResetRequestSourceImpl to send STOP request when proxy not aware of a context When an app server connects to a proxy for the first time or after a disruption, it sends an INFO request to the proxy. Proxy responds with a list of contexts that are registered for given server, and their states. The context states returned from the proxy are compared to the actual states of contexts on the app server, and if necessary, the server should send ENABLE / STOP requests to bring the proxy to an up-to-date state. The change in this commit makes the app server send a STOP request for a context in case when the proxy response to an INFO request doesn't contain given context. The purpose of this STOP request is to enforce context registration on the proxy side. --- .../mcmp/impl/ResetRequestSourceImpl.java | 7 +- .../mcmp/ResetRequestSourceTestCase.java | 129 +++++++++++------- 2 files changed, 88 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/jboss/modcluster/mcmp/impl/ResetRequestSourceImpl.java b/core/src/main/java/org/jboss/modcluster/mcmp/impl/ResetRequestSourceImpl.java index 064b1a420..7919ff77a 100644 --- a/core/src/main/java/org/jboss/modcluster/mcmp/impl/ResetRequestSourceImpl.java +++ b/core/src/main/java/org/jboss/modcluster/mcmp/impl/ResetRequestSourceImpl.java @@ -134,7 +134,12 @@ public List getResetRequests(Map> response : this.requestFactory.createDisableRequest(context)); } } else { - if (status == ResetRequestSource.Status.ENABLED) { + if (status == ResetRequestSource.Status.ENABLED || status == null) { + // Two cases are handled here: + // 1. Context is not started, but proxy reports the context as ENABLED + // => send STOP request, so that proxy disables the context. + // 2. Context is not started, proxy is not aware of the context (status == null) + // => send STOP reqeust, so that proxy registers the context. engineRequests.add(this.requestFactory.createStopRequest(context)); } } diff --git a/core/src/test/java/org/jboss/modcluster/mcmp/ResetRequestSourceTestCase.java b/core/src/test/java/org/jboss/modcluster/mcmp/ResetRequestSourceTestCase.java index dff4d866c..6209d4012 100755 --- a/core/src/test/java/org/jboss/modcluster/mcmp/ResetRequestSourceTestCase.java +++ b/core/src/test/java/org/jboss/modcluster/mcmp/ResetRequestSourceTestCase.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -37,6 +38,7 @@ import org.jboss.modcluster.container.Server; import org.jboss.modcluster.config.BalancerConfiguration; import org.jboss.modcluster.config.NodeConfiguration; +import org.jboss.modcluster.mcmp.impl.DefaultMCMPHandler; import org.jboss.modcluster.mcmp.impl.ResetRequestSourceImpl; import org.junit.Test; @@ -49,7 +51,17 @@ public class ResetRequestSourceTestCase { private final BalancerConfiguration balancerConfig = mock(BalancerConfiguration.class); private final MCMPRequestFactory requestFactory = mock(MCMPRequestFactory.class); - private ResetRequestSource source = new ResetRequestSourceImpl(this.nodeConfig, this.balancerConfig, this.requestFactory); + private final ResetRequestSource source = new ResetRequestSourceImpl(this.nodeConfig, this.balancerConfig, this.requestFactory); + + private final MCMPRequest configRequest = mock(MCMPRequest.class); + private final MCMPRequest enableRequest = mock(MCMPRequest.class); + private final MCMPRequest disableRequest = mock(MCMPRequest.class); + private final MCMPRequest stopRequest = mock(MCMPRequest.class); + + private static final String JVM_ROUTE = "host1"; + private static final String HOST_NAME = "host"; + private static final String ALIAS1 = "alias1"; + private static final String ALIAS2 = "alias2"; @Test public void getResetRequestsNoServer() { @@ -64,41 +76,34 @@ public void getResetRequestsNoServer() { } @Test - public void getResetRequests() throws Exception { - Server server = mock(Server.class); - ContextFilter contextFilter = mock(ContextFilter.class); - - this.source.init(server, contextFilter); - - verifyNoInteractions(server); + public void getResetRequests() { + setupMocks(true, true); - Engine engine = mock(Engine.class); - Host host = mock(Host.class); - Context context = mock(Context.class); - Context excludedContext = mock(Context.class); - MCMPRequest configRequest = mock(MCMPRequest.class); - MCMPRequest contextRequest = mock(MCMPRequest.class); + List requests = this.source.getResetRequests(Collections + .> emptyMap()); - when(contextFilter.getExcludedContexts(host)).thenReturn(Collections.singleton("/excluded")); - when(contextFilter.isAutoEnableContexts()).thenReturn(true); + assertEquals(2, requests.size()); - when(server.getEngines()).thenReturn(Collections.singleton(engine)); + assertSame(this.configRequest, requests.get(0)); + assertSame(this.enableRequest, requests.get(1)); + } - when(this.requestFactory.createConfigRequest(engine, this.nodeConfig, this.balancerConfig)).thenReturn(configRequest); + @Test + public void getResetRequestsDisableContexts() { + setupMocks(false, true); - when(engine.getJvmRoute()).thenReturn("host1"); - when(engine.getProxyConnector()).thenReturn(mock(Connector.class)); + List requests = this.source.getResetRequests(Collections + .> emptyMap()); - when(engine.getHosts()).thenReturn(Collections.singleton(host)); - when(host.getName()).thenReturn("host"); - when(host.getAliases()).thenReturn(new TreeSet(Arrays.asList("alias1", "alias2"))); - when(host.getContexts()).thenReturn(Arrays.asList(context, excludedContext)); - when(context.getPath()).thenReturn("/context"); - when(context.isStarted()).thenReturn(true); + assertEquals(2, requests.size()); - when(this.requestFactory.createEnableRequest(context)).thenReturn(contextRequest); + assertSame(configRequest, requests.get(0)); + assertSame(disableRequest, requests.get(1)); + } - when(excludedContext.getPath()).thenReturn("/excluded"); + @Test + public void getResetRequestsContextStoppedNoProxyStatus() { + setupMocks(true, false); List requests = this.source.getResetRequests(Collections .> emptyMap()); @@ -106,11 +111,39 @@ public void getResetRequests() throws Exception { assertEquals(2, requests.size()); assertSame(configRequest, requests.get(0)); - assertSame(contextRequest, requests.get(1)); + assertSame(stopRequest, requests.get(1)); + } + + @Test + public void getResetRequestsContextStoppedProxyStatusEnabled() { + setupMocks(true, false); + + Map> infoResponse = createInfoResponse( + ResetRequestSource.Status.ENABLED); + + List requests = this.source.getResetRequests(infoResponse); + + assertEquals(2, requests.size()); + + assertSame(configRequest, requests.get(0)); + assertSame(stopRequest, requests.get(1)); } @Test - public void getResetRequestsDisableContexts() throws Exception { + public void getResetRequestsContextStartedProxyStatusDisabled() { + setupMocks(true, true); + Map> infoResponse = createInfoResponse( + ResetRequestSource.Status.DISABLED); + + List requests = this.source.getResetRequests(infoResponse); + + assertEquals(2, requests.size()); + + assertSame(configRequest, requests.get(0)); + assertSame(enableRequest, requests.get(1)); + } + + private void setupMocks(boolean autoEnableContexts, boolean contextsAreStarted) { Server server = mock(Server.class); ContextFilter contextFilter = mock(ContextFilter.class); @@ -122,36 +155,38 @@ public void getResetRequestsDisableContexts() throws Exception { Host host = mock(Host.class); Context context = mock(Context.class); Context excludedContext = mock(Context.class); - MCMPRequest configRequest = mock(MCMPRequest.class); - MCMPRequest contextRequest = mock(MCMPRequest.class); when(contextFilter.getExcludedContexts(host)).thenReturn(Collections.singleton("/excluded")); - when(contextFilter.isAutoEnableContexts()).thenReturn(false); + when(contextFilter.isAutoEnableContexts()).thenReturn(autoEnableContexts); when(server.getEngines()).thenReturn(Collections.singleton(engine)); - when(this.requestFactory.createConfigRequest(engine, this.nodeConfig, this.balancerConfig)).thenReturn(configRequest); - - when(engine.getJvmRoute()).thenReturn("host1"); + when(engine.getJvmRoute()).thenReturn(JVM_ROUTE); when(engine.getProxyConnector()).thenReturn(mock(Connector.class)); when(engine.getHosts()).thenReturn(Collections.singleton(host)); - when(host.getName()).thenReturn("host"); - when(host.getAliases()).thenReturn(new TreeSet(Arrays.asList("alias1", "alias2"))); + when(host.getName()).thenReturn(HOST_NAME); + when(host.getAliases()).thenReturn(new TreeSet(Arrays.asList(HOST_NAME, ALIAS1, ALIAS2))); when(host.getContexts()).thenReturn(Arrays.asList(context, excludedContext)); when(context.getPath()).thenReturn("/context"); - when(context.isStarted()).thenReturn(true); + when(context.isStarted()).thenReturn(contextsAreStarted); - when(this.requestFactory.createDisableRequest(context)).thenReturn(contextRequest); + when(this.requestFactory.createConfigRequest(engine, this.nodeConfig, this.balancerConfig)) + .thenReturn(this.configRequest); + when(this.requestFactory.createEnableRequest(context)).thenReturn(this.enableRequest); + when(this.requestFactory.createDisableRequest(context)).thenReturn(this.disableRequest); + when(this.requestFactory.createStopRequest(context)).thenReturn(this.stopRequest); when(excludedContext.getPath()).thenReturn("/excluded"); + } - List requests = this.source.getResetRequests(Collections - .> emptyMap()); - - assertEquals(2, requests.size()); - - assertSame(configRequest, requests.get(0)); - assertSame(contextRequest, requests.get(1)); + private Map> createInfoResponse(ResetRequestSource.Status contextStatus) { + DefaultMCMPHandler.VirtualHostImpl virtualHost = new DefaultMCMPHandler.VirtualHostImpl(); + virtualHost.getAliases().add(HOST_NAME); + virtualHost.getAliases().add(ALIAS1); + virtualHost.getAliases().add(ALIAS2); + virtualHost.getContexts().put("/context", contextStatus); + return Collections.singletonMap(JVM_ROUTE, Collections.singleton(virtualHost)); } + }