This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit c3ae5b6bfbac436596e3aba32ce730d181d32e62
Author: opencode <[email protected]>
AuthorDate: Sat Oct 17 14:53:25 2026 +0200

    Remove stray raw path entry added to the WebSocket template match map when 
a programmatic registration duplicates an annotated POJO template path
    
    The template match map is keyed by the normalized path in which named
    template parameters are replaced with numeric indices. In the case in
    which a programmatic registration duplicates the template path of an
    annotated POJO for the same endpoint class, the specification requires
    the new match to be ignored. The code copied from the exact match
    branch below restored the old match into the map using the raw path as
    the key. Unlike the exact match branch, the template branch uses
    putIfAbsent() which does not modify the map when a match already
    exists, so no restoration is required and the put instead added a
    second, raw path keyed entry to the map for every such registration,
    both keys referencing the same match.
    
    The redundant put is removed. The exact match branch is unchanged
    since there the preceding put() does overwrite the old match and the
    restoration is required.
    
    The regression test extends testDuplicatePaths22, which exercises this
    code path, to assert via reflection on the template match map that
    exactly one entry, keyed by the normalized path, remains after the
    duplicate registration is ignored.
---
 .../apache/tomcat/websocket/server/WsServerContainer.java   |  5 +++--
 .../tomcat/websocket/server/TestWsServerContainer.java      | 13 +++++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/tomcat/websocket/server/WsServerContainer.java 
b/java/org/apache/tomcat/websocket/server/WsServerContainer.java
index d104d344b4..2492af2700 100644
--- a/java/org/apache/tomcat/websocket/server/WsServerContainer.java
+++ b/java/org/apache/tomcat/websocket/server/WsServerContainer.java
@@ -166,8 +166,9 @@ public class WsServerContainer extends WsWebSocketContainer 
implements ServerCon
                     // before POJOs in WsSci#onStartup()
                     if (oldMatch.fromAnnotatedPojo() && 
!newMatch.fromAnnotatedPojo() &&
                             oldMatch.config().getEndpointClass() == 
newMatch.config().getEndpointClass()) {
-                        // The WebSocket spec says to ignore the new match in 
this case
-                        templateMatches.put(path, oldMatch);
+                        // The WebSocket spec says to ignore the new match in 
this case. The old
+                        // match has not been replaced by putIfAbsent() above 
so, unlike the exact
+                        // match case below, there is no need to restore the 
old match in the map.
                     } else {
                         // Duplicate uriTemplate;
                         throw new 
DeploymentException(sm.getString("serverContainer.duplicatePaths", path,
diff --git a/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java 
b/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java
index 17fbffd462..7a7222f687 100644
--- a/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java
+++ b/test/org/apache/tomcat/websocket/server/TestWsServerContainer.java
@@ -364,6 +364,19 @@ public class TestWsServerContainer extends 
WebSocketBaseTest {
         sc.addEndpoint(configA);
 
         Assert.assertNotEquals(configA, sc.findMapping("/foo/{a}").config());
+
+        // The old match must remain stored under the normalized path. 
Ignoring the new match must
+        // not add an additional entry to the map.
+        Field configTemplateMatchMapField = 
WsServerContainer.class.getDeclaredField("configTemplateMatchMap");
+        configTemplateMatchMapField.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        Map<Integer, ConcurrentSkipListMap<String, Object>> 
configTemplateMatchMap =
+                (Map<Integer, ConcurrentSkipListMap<String, Object>>) 
configTemplateMatchMapField.get(sc);
+
+        ConcurrentSkipListMap<String, Object> templateMatches = 
configTemplateMatchMap.get(Integer.valueOf(2));
+
+        Assert.assertEquals(1, templateMatches.size());
+        Assert.assertTrue(templateMatches.containsKey("/foo/{0}"));
     }
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to