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

joerghoh pushed a commit to branch SLING-13378
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-starter-test-services.git

commit 3d46a998237aabf09060ccbd44ec88a7f048d559
Author: Joerg Hoh <[email protected]>
AuthorDate: Wed Oct 7 11:24:41 2026 +0200

    Fix JSON escaping of MBean names in JmxServlet
    
    Use JSON-P instead of manually quoting ObjectName strings so valid
    names containing quotes and backslashes round-trip correctly.
    
    Add regression tests for plain, quoted, and unquoted backslash names,
    with a test-scoped JSON-P provider and unit test documentation.
---
 pom.xml                                            |   6 ++
 .../sling/starter/testservices/jmx/JmxServlet.java |  17 ++-
 .../starter/testservices/jmx/JmxServletTest.java   | 115 +++++++++++++++++++++
 3 files changed, 129 insertions(+), 9 deletions(-)

diff --git a/pom.xml b/pom.xml
index a95789a..7b59a40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,12 @@
             <artifactId>jakarta.json-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson</artifactId>
+            <version>1.1.7</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-jcr-server</artifactId>
diff --git 
a/src/main/java/org/apache/sling/starter/testservices/jmx/JmxServlet.java 
b/src/main/java/org/apache/sling/starter/testservices/jmx/JmxServlet.java
index c26887f..b5ce77c 100644
--- a/src/main/java/org/apache/sling/starter/testservices/jmx/JmxServlet.java
+++ b/src/main/java/org/apache/sling/starter/testservices/jmx/JmxServlet.java
@@ -28,6 +28,9 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
 
+import jakarta.json.Json;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonWriter;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
 import org.apache.sling.api.servlets.SlingAllMethodsServlet;
@@ -56,17 +59,13 @@ public class JmxServlet extends SlingAllMethodsServlet {
             response.setStatus(HttpServletResponse.SC_OK);
 
             // list MBean names under the domain "org.apache.sling"
-            response.getWriter().write("[");
-            boolean first = true;
+            JsonArrayBuilder names = Json.createArrayBuilder();
             for (ObjectName name : server.queryNames(queryName, null)) {
-                if (first) {
-                    first = false;
-                } else {
-                    response.getWriter().write(",");
-                }
-                response.getWriter().write("\"" + name + "\"");
+                names.add(name.toString());
+            }
+            try (JsonWriter writer = Json.createWriter(response.getWriter())) {
+                writer.writeArray(names.build());
             }
-            response.getWriter().write("]");
         } catch (MalformedObjectNameException e) {
             throw new ServletException(e);
         }
diff --git 
a/src/test/java/org/apache/sling/starter/testservices/jmx/JmxServletTest.java 
b/src/test/java/org/apache/sling/starter/testservices/jmx/JmxServletTest.java
new file mode 100644
index 0000000..f3b4303
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/starter/testservices/jmx/JmxServletTest.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.starter.testservices.jmx;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Proxy;
+import java.util.UUID;
+
+import jakarta.json.Json;
+import jakarta.json.JsonReader;
+import jakarta.json.JsonString;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JmxServletTest {
+
+    @Test
+    public void plainNameRoundTrips() throws Exception {
+        assertNameRoundTrips("plain");
+    }
+
+    @Test
+    public void quotedNameRoundTrips() throws Exception {
+        assertNameRoundTrips(ObjectName.quote("quotes\" and backslash\\"));
+    }
+
+    @Test
+    public void unquotedBackslashRoundTrips() throws Exception {
+        assertNameRoundTrips("backslash\\name");
+    }
+
+    private void assertNameRoundTrips(String nameProperty) throws Exception {
+        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+        ObjectName name = new ObjectName(
+                "org.apache.sling:type=JsonSerializationTest,id=" + 
UUID.randomUUID() + ",name=" + nameProperty);
+        server.registerMBean(new TestBean(), name);
+        try {
+            assertTrue("JMX must accept the test MBean name", 
server.isRegistered(name));
+            assertEquals("registered", server.getAttribute(name, "Value"));
+
+            String content = render();
+            try (JsonReader reader = Json.createReader(new 
StringReader(content))) {
+                assertTrue(
+                        "The servlet must preserve the registered name in its 
JSON response: " + content,
+                        
reader.readArray().getValuesAs(JsonString.class).stream()
+                                .map(JsonString::getString)
+                                .anyMatch(name.toString()::equals));
+            }
+        } finally {
+            server.unregisterMBean(name);
+        }
+    }
+
+    public interface TestBeanMBean {
+        String getValue();
+    }
+
+    public static class TestBean implements TestBeanMBean {
+        @Override
+        public String getValue() {
+            return "registered";
+        }
+    }
+
+    private String render() throws Exception {
+        StringWriter output = new StringWriter();
+        PrintWriter writer = new PrintWriter(output);
+        SlingHttpServletResponse response = (SlingHttpServletResponse) 
Proxy.newProxyInstance(
+                SlingHttpServletResponse.class.getClassLoader(),
+                new Class<?>[] {SlingHttpServletResponse.class},
+                (proxy, method, arguments) -> {
+                    switch (method.getName()) {
+                        case "getWriter":
+                            return writer;
+                        case "setContentType":
+                            assertEquals("application/json", arguments[0]);
+                            return null;
+                        case "setStatus":
+                            assertEquals(HttpServletResponse.SC_OK, 
arguments[0]);
+                            return null;
+                        default:
+                            throw new 
UnsupportedOperationException(method.getName());
+                    }
+                });
+        new JmxServlet().doGet(null, response);
+        writer.flush();
+        return output.toString();
+    }
+}

Reply via email to