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

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

commit b5c07c9f165d1a0a27739123eb45066e25c25ee3
Author: opencode <[email protected]>
AuthorDate: Wed Oct 14 13:28:54 2026 +0200

    Store ContextService port-component refs outside generic properties
    
    The port-component-ref mappings of a <service-ref> (the Service
    Endpoint Interface to port-component-link associations) were stored in
    the generic properties map inherited from ResourceBase, via
    addPortcomponent() -> setProperty(). The same map also holds the
    <mapped-name> value, which MappedNameRule stores under the key
    mappedName for every resource that allows that element. Since
    getServiceendpoints() simply returned listProperties(), any
    <service-ref> that declared a <mapped-name> - which is legal per the
    Servlet specification - was reported as having a bogus service endpoint
    named mappedName, whose port-component-link was the mapped-name
    value.
    
    That bogus endpoint leaked to several consumers:
    
      * NamingContextListener.addService() bound a
        SERVICEENDPOINTINTERFACE/PORTCOMPONENTLINK pair for mappedName
        into the application's JNDI ServiceRef.
      * WebXml.toXml() serialized an extra
        <port-component-ref><service-endpoint-interface>mappedName
        </service-endpoint-interface>...</port-component-ref> element into
        the effective web.xml.
      * ContextService.toString() printed mappedName as an endpoint, as did
        the storeconfig output for a global ServiceRef.
    
    Port-component mappings now live in a dedicated map, kept separate from
    the generic properties, and are included in equals()/hashCode().
    WebXml.toXml() reads the port link through getPortlink() rather than by
    treating the endpoint name as a property key; the mapped-name element
    continues to be read from the mappedName property. As a side effect,
    endpoint names are no longer emitted as generic attributes when a global
    <ServiceRef> is written by storeconfig.
    
    Tests: TestWebXml now checks that a service-ref with a mapped-name
    reports only its real endpoint, retains the mapped-name as a property,
    and serializes a single correct port-component-ref in the effective
    web.xml.
---
 .../tomcat/util/descriptor/web/ContextService.java | 15 ++++-
 .../apache/tomcat/util/descriptor/web/WebXml.java  |  2 +-
 .../tomcat/util/descriptor/web/TestWebXml.java     | 77 ++++++++++++++++++++++
 3 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/tomcat/util/descriptor/web/ContextService.java 
b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
index 690ff3fb1a..06834cf494 100644
--- a/java/org/apache/tomcat/util/descriptor/web/ContextService.java
+++ b/java/org/apache/tomcat/util/descriptor/web/ContextService.java
@@ -263,6 +263,11 @@ public class ContextService extends ResourceBase {
         this.serviceqname[1] = localpart;
     }
 
+    /**
+     * The port component mappings, keyed by service endpoint interface.
+     */
+    private final Map<String,String> portComponents = new HashMap<>();
+
     /**
      * Declares a client dependency on the container to resolving a Service 
Endpoint Interface to a WSDL port. It
      * optionally associates the Service Endpoint Interface with a particular 
port-component.
@@ -270,7 +275,7 @@ public class ContextService extends ResourceBase {
      * @return the endpoint names iterator
      */
     public Iterator<String> getServiceendpoints() {
-        return this.listProperties();
+        return this.portComponents.keySet().iterator();
     }
 
     /**
@@ -280,7 +285,7 @@ public class ContextService extends ResourceBase {
      * @return the port link
      */
     public String getPortlink(String serviceendpoint) {
-        return (String) this.getProperty(serviceendpoint);
+        return this.portComponents.get(serviceendpoint);
     }
 
     /**
@@ -293,7 +298,7 @@ public class ContextService extends ResourceBase {
         if (portlink == null) {
             portlink = "";
         }
-        this.setProperty(serviceendpoint, portlink);
+        this.portComponents.put(serviceendpoint, portlink);
     }
 
     /**
@@ -405,6 +410,7 @@ public class ContextService extends ResourceBase {
         result = prime * result + handlers.hashCode();
         result = prime * result + ((jaxrpcmappingfile == null) ? 0 : 
jaxrpcmappingfile.hashCode());
         result = prime * result + ((largeIcon == null) ? 0 : 
largeIcon.hashCode());
+        result = prime * result + portComponents.hashCode();
         result = prime * result + ((serviceInterface == null) ? 0 : 
serviceInterface.hashCode());
         result = prime * result + Arrays.hashCode(serviceqname);
         result = prime * result + ((smallIcon == null) ? 0 : 
smallIcon.hashCode());
@@ -449,6 +455,9 @@ public class ContextService extends ResourceBase {
         } else if (!largeIcon.equals(other.largeIcon)) {
             return false;
         }
+        if (!portComponents.equals(other.portComponents)) {
+            return false;
+        }
         if (serviceInterface == null) {
             if (other.serviceInterface != null) {
                 return false;
diff --git a/java/org/apache/tomcat/util/descriptor/web/WebXml.java 
b/java/org/apache/tomcat/util/descriptor/web/WebXml.java
index c984ee474f..2f4973dee0 100644
--- a/java/org/apache/tomcat/util/descriptor/web/WebXml.java
+++ b/java/org/apache/tomcat/util/descriptor/web/WebXml.java
@@ -1956,7 +1956,7 @@ public class WebXml extends XmlEncodingBase implements 
DocumentProperties.Charse
                     String endpoint = endpointIter.next();
                     sb.append("    <port-component-ref>\n");
                     appendElement(sb, INDENT6, "service-endpoint-interface", 
endpoint);
-                    appendElement(sb, INDENT6, "port-component-link", 
serviceRef.getProperty(endpoint));
+                    appendElement(sb, INDENT6, "port-component-link", 
serviceRef.getPortlink(endpoint));
                     sb.append("    </port-component-ref>\n");
                 }
                 Iterator<String> handlerIter = serviceRef.getHandlers();
diff --git a/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java 
b/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java
index 7d086caada..8209d3f09b 100644
--- a/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java
+++ b/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java
@@ -20,8 +20,12 @@ import java.io.File;
 import java.io.IOException;
 import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -803,4 +807,77 @@ public class TestWebXml {
 
         Assert.assertEquals(input, output);
     }
+
+
+    @Test
+    public void testServiceRefMappedNameIsNotAnEndpoint() throws Exception {
+        // The <mapped-name> of a <service-ref> is stored in the generic
+        // properties of ResourceBase. It must not be confused with the
+        // port-component-ref entries which were historically stored in the
+        // same map.
+        String input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<web-app xmlns=\"https://jakarta.ee/xml/ns/jakartaee\"\n"; +
+                "         
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"; +
+                "         
xsi:schemaLocation=\"https://jakarta.ee/xml/ns/jakartaee"; +
+                " https://jakarta.ee/xml/ns/jakartaee/web-app_6_2.xsd\"\n"; +
+                "         version=\"6.2\"\n" +
+                "         metadata-complete=\"true\">\n" +
+                "\n" +
+                "  <service-ref>\n" +
+                "    <service-ref-name>service/Test</service-ref-name>\n" +
+                "    
<service-interface>com.example.TestService</service-interface>\n" +
+                "    <service-qname>test</service-qname>\n" +
+                "    <port-component-ref>\n" +
+                "      
<service-endpoint-interface>com.example.Hello</service-endpoint-interface>\n" +
+                "      <port-component-link>HelloLink</port-component-link>\n" 
+
+                "    </port-component-ref>\n" +
+                "    <mapped-name>service/TestMapped</mapped-name>\n" +
+                "  </service-ref>\n" +
+                "\n" +
+                "</web-app>";
+
+        Digester digester = DigesterFactory.newDigester(true, true, new 
WebRuleSet(), true);
+        XmlErrorHandler handler = new XmlErrorHandler();
+        digester.setErrorHandler(handler);
+
+        InputSource is = new InputSource(new StringReader(input));
+        WebXml webxml = new WebXml();
+        digester.push(webxml);
+        digester.parse(is);
+
+        Assert.assertEquals(0, handler.getErrors().size());
+        Assert.assertEquals(0, handler.getWarnings().size());
+
+        ContextService service = webxml.getServiceRefs().get("service/Test");
+        Assert.assertNotNull(service);
+
+        // Only the real endpoint must be reported.
+        List<String> endpoints = new ArrayList<>();
+        Iterator<String> iter = service.getServiceendpoints();
+        while (iter.hasNext()) {
+            endpoints.add(iter.next());
+        }
+        Assert.assertEquals(Collections.singletonList("com.example.Hello"), 
endpoints);
+        Assert.assertEquals("HelloLink", 
service.getPortlink("com.example.Hello"));
+
+        // The mapped-name remains available as the only generic property.
+        List<String> properties = new ArrayList<>();
+        Iterator<String> propIter = service.listProperties();
+        while (propIter.hasNext()) {
+            properties.add(propIter.next());
+        }
+        Assert.assertEquals(Collections.singletonList("mappedName"), 
properties);
+        Assert.assertEquals("service/TestMapped", 
service.getProperty("mappedName"));
+
+        // The effective web.xml serialization must contain exactly the real
+        // port-component-ref and the mapped-name.
+        String output = webxml.toXml();
+        int firstPortComponentRef = output.indexOf("<port-component-ref>");
+        Assert.assertTrue(output, firstPortComponentRef >= 0);
+        Assert.assertTrue(output,
+                output.indexOf("<port-component-ref>", firstPortComponentRef + 
1) < 0);
+        Assert.assertFalse(output, 
output.contains("<service-endpoint-interface>mappedName</service-endpoint-interface>"));
+        Assert.assertTrue(output, 
output.contains("<port-component-link>HelloLink</port-component-link>"));
+        Assert.assertTrue(output, 
output.contains("<mapped-name>service/TestMapped</mapped-name>"));
+    }
 }


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

Reply via email to