This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit ab90082aaa7e7d472d2c9a70148c26812052acfc Author: opencode <[email protected]> AuthorDate: Sun Oct 11 12:34:11 2026 +0200 Hand out a copy of the static members list The accessor of the static members returned the internal list that the membership provider iterates from its ping thread and at start and stop. A caller that mutated the returned list at runtime raced that iteration of an unsynchronized array list, with the member list occasionally read in an inconsistent state and members silently missing from pings and start notifications. The accessor now hands out a copy, and its contract says so, leaving the provider with the configured list, which only the service itself still touches. The signature keeps the array list type, so callers compiled against the accessor are unaffected, while the read only callers in this repository keep working against the copy. Add tests pinning the contract, an accessor that returns a different list each call and mutations of the returned list that leave the provider list untouched. --- .../tribes/membership/StaticMembershipService.java | 7 ++++--- .../membership/TestStaticMembershipService.java | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/java/org/apache/catalina/tribes/membership/StaticMembershipService.java b/java/org/apache/catalina/tribes/membership/StaticMembershipService.java index b4d9d3101e..2d4f8c399e 100644 --- a/java/org/apache/catalina/tribes/membership/StaticMembershipService.java +++ b/java/org/apache/catalina/tribes/membership/StaticMembershipService.java @@ -164,11 +164,12 @@ public class StaticMembershipService extends MembershipServiceBase implements St } /** - * Returns the list of static members. - * @return the static members list + * Returns a copy of the list of static members. Mutating the returned list has no effect on + * this service. The provider uses the configured list directly while it runs. + * @return a copy of the static members list */ public ArrayList<StaticMember> getStaticMembers() { - return staticMembers; + return new ArrayList<>(staticMembers); } /** diff --git a/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java b/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java index ddafa78835..71d35f5c5c 100644 --- a/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java +++ b/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java @@ -16,6 +16,7 @@ */ package org.apache.catalina.tribes.membership; +import java.util.ArrayList; import java.util.Properties; import org.junit.Assert; @@ -120,4 +121,25 @@ public class TestStaticMembershipService { service.stop(level); } } + + /* + * The accessor hands out the list that the provider iterates while it runs. A caller that + * mutated the returned list raced that iteration, so the accessor must hand out a copy + * instead of the live list. + */ + @Test + public void testGetStaticMembersReturnsACopy() throws Exception { + StaticMembershipService service = new StaticMembershipService(); + StaticMember member = new StaticMember("127.0.0.1", 4005, -1); + service.addStaticMember(member); + + ArrayList<StaticMember> members = service.getStaticMembers(); + Assert.assertNotSame("the accessor must not hand out the same list twice", members, + service.getStaticMembers()); + Assert.assertTrue(members.remove(member)); + Assert.assertFalse(members.contains(member)); + + // The live list that the provider reads must be untouched by the copy mutation + Assert.assertTrue(service.getStaticMembers().contains(member)); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
