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(); + } +}
