This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 4b494c19d4 Review "Keep monitor, heartbeat and JMX alive across
partial ... stops"
4b494c19d4 is described below
commit 4b494c19d4c1254957373266231b8433ba9c33ce
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Oct 21 11:23:25 2026 +0100
Review "Keep monitor, heartbeat and JMX alive across partial ... stops"
Clean-up TLS if all services have stopped
Keep channel on partial stop for Interceptors
---
.../tribes/group/ChannelInterceptorBase.java | 32 +++++++++++++++++----
.../apache/catalina/tribes/group/GroupChannel.java | 2 +-
.../tribes/group/TestGroupChannelStartStop.java | 33 ++++++++++++++++++++++
3 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
b/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
index c535233675..89a607ce18 100644
--- a/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
+++ b/java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
@@ -187,14 +187,34 @@ public abstract class ChannelInterceptorBase implements
ChannelInterceptor {
if (getNext() != null) {
getNext().stop(svc);
}
- if (oname != null) {
- JmxRegistry jmxRegistry = JmxRegistry.getRegistry(channel);
- if (jmxRegistry != null) {
- jmxRegistry.unregisterJmx(oname);
+ // Only release the channel and the JMX registration once all services
have been stopped. A partial stop leaves
+ // services running that still use them.
+ if (isFullyStopped()) {
+ if (oname != null) {
+ JmxRegistry jmxRegistry = JmxRegistry.getRegistry(channel);
+ if (jmxRegistry != null) {
+ jmxRegistry.unregisterJmx(oname);
+ }
+ oname = null;
}
- oname = null;
+ channel = null;
}
- channel = null;
+ }
+
+ /**
+ * Determine if all services of the coordinator at the end of the
interceptor stack have been stopped.
+ *
+ * @return {@code true} if no service is started, or if there is no
coordinator after this interceptor
+ */
+ private boolean isFullyStopped() {
+ ChannelInterceptor interceptor = getNext();
+ while (interceptor != null) {
+ if (interceptor instanceof ChannelCoordinator coordinator) {
+ return coordinator.getStartLevel() == 0;
+ }
+ interceptor = interceptor.getNext();
+ }
+ return true;
}
@Override
diff --git a/java/org/apache/catalina/tribes/group/GroupChannel.java
b/java/org/apache/catalina/tribes/group/GroupChannel.java
index 6b246de5de..81f6fb2d3b 100644
--- a/java/org/apache/catalina/tribes/group/GroupChannel.java
+++ b/java/org/apache/catalina/tribes/group/GroupChannel.java
@@ -533,7 +533,7 @@ public class GroupChannel extends ChannelInterceptorBase
implements ManagedChann
heartbeatFuture.cancel(true);
heartbeatFuture = null;
}
- if ((svc & DEFAULT) == DEFAULT && sslContext != null) {
+ if (sslContext != null) {
sslContext.close();
sslContext = null;
}
diff --git
a/test/org/apache/catalina/tribes/group/TestGroupChannelStartStop.java
b/test/org/apache/catalina/tribes/group/TestGroupChannelStartStop.java
index cd6f2765a4..0268deab7c 100644
--- a/test/org/apache/catalina/tribes/group/TestGroupChannelStartStop.java
+++ b/test/org/apache/catalina/tribes/group/TestGroupChannelStartStop.java
@@ -16,14 +16,17 @@
*/
package org.apache.catalina.tribes.group;
+import java.util.Iterator;
import java.util.concurrent.ScheduledFuture;
import org.junit.After;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelInterceptor;
import org.apache.catalina.tribes.membership.McastService;
import org.apache.catalina.tribes.transport.ReceiverBase;
import org.apache.catalina.tribes.transport.ReplicationTransmitter;
@@ -166,6 +169,36 @@ public class TestGroupChannelStartStop {
Assert.assertTrue(heartbeat.isCancelled());
}
+ @Test
+ public void testStagedStopReleasesSslContext() throws Exception {
+ channel.setPskIdentity("tribes-test");
+ channel.setPskKey("000102030405060708090a0b0c0d0e0f");
+ channel.start(Channel.DEFAULT);
+ // The TLS context is only available when the TLS implementation is
+ Assume.assumeNotNull(channel.getSslContext());
+ // Stop in stages: no single call stops all of the services
+ channel.stop(Channel.SND_RX_SEQ);
+ Assert.assertNotNull(channel.getSslContext());
+ channel.stop(Channel.SND_TX_SEQ | Channel.MBR_RX_SEQ |
Channel.MBR_TX_SEQ);
+ Assert.assertNull(channel.getSslContext());
+ }
+
+ @Test
+ public void testPartialStopKeepsInterceptorChannel() throws Exception {
+ channel.start(Channel.DEFAULT);
+ channel.stop(Channel.SND_RX_SEQ);
+ // services are still running, the interceptors must still be attached
to the channel
+ for (Iterator<ChannelInterceptor> it = channel.getInterceptors();
it.hasNext();) {
+ ChannelInterceptor interceptor = it.next();
+ Assert.assertSame(interceptor.getClass().getName(), channel,
interceptor.getChannel());
+ }
+ channel.stop(Channel.SND_TX_SEQ | Channel.MBR_RX_SEQ |
Channel.MBR_TX_SEQ);
+ for (Iterator<ChannelInterceptor> it = channel.getInterceptors();
it.hasNext();) {
+ ChannelInterceptor interceptor = it.next();
+ Assert.assertNull(interceptor.getClass().getName(),
interceptor.getChannel());
+ }
+ }
+
@Test
public void testOverlappingPartialStart() throws Exception {
// start one service, then start everything: the already
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]