This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new c7b5cf882f Add JSON stats to the status servlet
c7b5cf882f is described below
commit c7b5cf882fe847b3bc495d46bf188f8614be7b3e
Author: remm <[email protected]>
AuthorDate: Tue Oct 17 15:57:46 2023 +0200
Add JSON stats to the status servlet
The format is easy to parse and validate and is a straight equivalent to
the XML view.
I did not add the detailed processor view, it seems way to verbose these
days.
---
.../catalina/manager/StatusManagerServlet.java | 54 ++---
.../apache/catalina/manager/StatusTransformer.java | 264 ++++++++++++++++++---
.../catalina/manager/TestStatusTransformer.java | 82 +++++++
webapps/docs/changelog.xml | 5 +
webapps/docs/manager-howto.xml | 5 +
5 files changed, 345 insertions(+), 65 deletions(-)
diff --git a/java/org/apache/catalina/manager/StatusManagerServlet.java
b/java/org/apache/catalina/manager/StatusManagerServlet.java
index ab6e8010ee..912973117a 100644
--- a/java/org/apache/catalina/manager/StatusManagerServlet.java
+++ b/java/org/apache/catalina/manager/StatusManagerServlet.java
@@ -170,12 +170,16 @@ public class StatusManagerServlet extends HttpServlet
implements NotificationLis
StringManager smClient = StringManager.getManager(Constants.Package,
request.getLocales());
- // mode is flag for HTML or XML output
+ // mode is flag for HTML, JSON or XML output
int mode = 0;
// if ?XML=true, set the mode to XML
if (request.getParameter("XML") != null &&
request.getParameter("XML").equals("true")) {
mode = 1;
}
+ // if ?JSON=true, set the mode to JSON
+ if (request.getParameter("JSON") != null &&
request.getParameter("JSON").equals("true")) {
+ mode = 2;
+ }
StatusTransformer.setContentType(response, mode);
PrintWriter writer = response.getWriter();
@@ -271,32 +275,28 @@ public class StatusManagerServlet extends HttpServlet
implements NotificationLis
// use StatusTransformer to output status
StatusTransformer.writeVMState(writer, mode, args);
- for (ObjectName objectName : threadPools) {
- String name = objectName.getKeyProperty("name");
- args = new Object[19];
- args[0] =
smClient.getString("htmlManagerServlet.connectorStateMaxThreads");
- args[1] =
smClient.getString("htmlManagerServlet.connectorStateThreadCount");
- args[2] =
smClient.getString("htmlManagerServlet.connectorStateThreadBusy");
- args[3] =
smClient.getString("htmlManagerServlet.connectorStateAliveSocketCount");
- args[4] =
smClient.getString("htmlManagerServlet.connectorStateMaxProcessingTime");
- args[5] =
smClient.getString("htmlManagerServlet.connectorStateProcessingTime");
- args[6] =
smClient.getString("htmlManagerServlet.connectorStateRequestCount");
- args[7] =
smClient.getString("htmlManagerServlet.connectorStateErrorCount");
- args[8] =
smClient.getString("htmlManagerServlet.connectorStateBytesReceived");
- args[9] =
smClient.getString("htmlManagerServlet.connectorStateBytesSent");
- args[10] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleStage");
- args[11] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleTime");
- args[12] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleBSent");
- args[13] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleBRecv");
- args[14] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleClientForw");
- args[15] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleClientAct");
- args[16] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleVHost");
- args[17] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleRequest");
- args[18] =
smClient.getString("htmlManagerServlet.connectorStateHint");
- // use StatusTransformer to output status
- StatusTransformer.writeConnectorState(writer, objectName,
name, mBeanServer, globalRequestProcessors,
- requestProcessors, mode, args);
- }
+ args = new Object[19];
+ args[0] =
smClient.getString("htmlManagerServlet.connectorStateMaxThreads");
+ args[1] =
smClient.getString("htmlManagerServlet.connectorStateThreadCount");
+ args[2] =
smClient.getString("htmlManagerServlet.connectorStateThreadBusy");
+ args[3] =
smClient.getString("htmlManagerServlet.connectorStateAliveSocketCount");
+ args[4] =
smClient.getString("htmlManagerServlet.connectorStateMaxProcessingTime");
+ args[5] =
smClient.getString("htmlManagerServlet.connectorStateProcessingTime");
+ args[6] =
smClient.getString("htmlManagerServlet.connectorStateRequestCount");
+ args[7] =
smClient.getString("htmlManagerServlet.connectorStateErrorCount");
+ args[8] =
smClient.getString("htmlManagerServlet.connectorStateBytesReceived");
+ args[9] =
smClient.getString("htmlManagerServlet.connectorStateBytesSent");
+ args[10] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleStage");
+ args[11] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleTime");
+ args[12] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleBSent");
+ args[13] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleBRecv");
+ args[14] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleClientForw");
+ args[15] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleClientAct");
+ args[16] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleVHost");
+ args[17] =
smClient.getString("htmlManagerServlet.connectorStateTableTitleRequest");
+ args[18] =
smClient.getString("htmlManagerServlet.connectorStateHint");
+ StatusTransformer.writeConnectorsState(writer, mBeanServer,
threadPools, globalRequestProcessors,
+ requestProcessors, mode, args);
if (request.getPathInfo() != null &&
request.getPathInfo().equals("/all")) {
// Note: Retrieving the full status is much slower
diff --git a/java/org/apache/catalina/manager/StatusTransformer.java
b/java/org/apache/catalina/manager/StatusTransformer.java
index 1d51ea1713..e4fbf52ca7 100644
--- a/java/org/apache/catalina/manager/StatusTransformer.java
+++ b/java/org/apache/catalina/manager/StatusTransformer.java
@@ -34,6 +34,7 @@ import javax.management.ObjectName;
import jakarta.servlet.http.HttpServletResponse;
+import org.apache.tomcat.util.json.JSONFilter;
import org.apache.tomcat.util.security.Escape;
/**
@@ -51,6 +52,8 @@ public class StatusTransformer {
response.setContentType("text/html;charset=" + Constants.CHARSET);
} else if (mode == 1) {
response.setContentType("text/xml;charset=" + Constants.CHARSET);
+ } else if (mode == 2) {
+ response.setContentType("application/json");
}
}
@@ -60,7 +63,7 @@ public class StatusTransformer {
*
* @param writer the PrintWriter to use
* @param args Path prefix for URLs
- * @param mode - 0 = HTML header, 1 = XML declaration
+ * @param mode - 0 = HTML header, 1 = XML declaration, 2 = JSON
*/
public static void writeHeader(PrintWriter writer, Object[] args, int
mode) {
if (mode == 0) {
@@ -70,6 +73,8 @@ public class StatusTransformer {
writer.write(Constants.XML_DECLARATION);
writer.print(MessageFormat.format(Constants.XML_STYLE, args));
writer.write("<status>");
+ } else if (mode == 2) {
+
writer.append('{').append('"').append("tomcat").append('"').append(':').append('{').println();
}
}
@@ -122,6 +127,8 @@ public class StatusTransformer {
writer.print(Constants.HTML_TAIL_SECTION);
} else if (mode == 1) {
writer.write("</status>");
+ } else if (mode == 2) {
+ writer.append('}').append('}');
}
}
@@ -203,11 +210,87 @@ public class StatusTransformer {
}
writer.write("</jvm>");
+ } else if (mode == 2) {
+
writer.append('"').append("jvm").append('"').append(':').append('{').println();
+
+
writer.append('"').append("memory").append('"').append(':').append('{');
+ appendJSonValue(writer, "free",
Long.toString(Runtime.getRuntime().freeMemory())).append(',');
+ appendJSonValue(writer, "total",
Long.toString(Runtime.getRuntime().totalMemory())).append(',');
+ appendJSonValue(writer, "max",
Long.toString(Runtime.getRuntime().maxMemory()));
+ writer.append('}').append(',').println();
+
+
writer.append('"').append("memorypool").append('"').append(':').append('[');
+ boolean first = true;
+ for (MemoryPoolMXBean memoryPoolMBean : memoryPoolMBeans.values())
{
+ MemoryUsage usage = memoryPoolMBean.getUsage();
+ if (first) {
+ first = false;
+ } else {
+ writer.append(',').println();
+ }
+ writer.append('{');
+ appendJSonValue(writer, "name",
JSONFilter.escape(memoryPoolMBean.getName())).append(',');
+ appendJSonValue(writer, "type",
memoryPoolMBean.getType().toString()).append(',');
+ appendJSonValue(writer, "usageInit",
Long.toString(usage.getInit())).append(',');
+ appendJSonValue(writer, "usageCommitted",
Long.toString(usage.getCommitted())).append(',');
+ appendJSonValue(writer, "usageMax",
Long.toString(usage.getMax())).append(',');
+ appendJSonValue(writer, "usageUsed",
Long.toString(usage.getUsed()));
+ writer.append('}');
+ }
+ writer.append(']').println();
+
+ writer.append('}');
}
}
+ private static PrintWriter appendJSonValue(PrintWriter writer, String
name, String value) {
+ return
writer.append('"').append(name).append('"').append(':').append('"').append(value).append('"');
+ }
+
+
+ /**
+ * Write connector state.
+ *
+ * @param writer The output writer
+ * @param mBeanServer MBean server
+ * @param threadPools MBean names for the thread pools of the
connectors
+ * @param globalRequestProcessors MBean names for the global request
processors
+ * @param requestProcessors MBean names for the request processors
+ * @param mode Mode <code>0</code> will generate HTML.
Mode <code>1</code> will generate XML.
+ * @param args I18n labels for the Connector state
values
+ *
+ * @throws Exception Propagated JMX error
+ */
+ public static void writeConnectorsState(PrintWriter writer, MBeanServer
mBeanServer,
+ List<ObjectName> threadPools,
+ List<ObjectName> globalRequestProcessors, List<ObjectName>
requestProcessors, int mode, Object[] args)
+ throws Exception {
+ if (mode == 2) {
+ writer.append(',').println();
+
writer.append('"').append("connector").append('"').append(':').append('[').println();
+ }
+ boolean first = true;
+ for (ObjectName objectName : threadPools) {
+ if (first) {
+ first = false;
+ } else {
+ if (mode == 2) {
+ writer.append(',').println();
+ }
+ }
+ String name = objectName.getKeyProperty("name");
+ // use StatusTransformer to output status
+ StatusTransformer.writeConnectorState(writer, objectName, name,
mBeanServer, globalRequestProcessors,
+ requestProcessors, mode, args);
+ }
+ if (mode == 2) {
+ writer.append(']').println();
+ }
+ }
+
+
/**
* Write connector state.
*
@@ -344,6 +427,38 @@ public class StatusTransformer {
}
writer.write("</connector>");
+ } else if (mode == 2) {
+ writer.append('{').println();
+
+ appendJSonValue(writer, "name",
JSONFilter.escape(name)).append(',').println();
+
writer.append('"').append("threadInfo").append('"').append(':').append('{');
+ appendJSonValue(writer, "maxThreads",
mBeanServer.getAttribute(tpName, "maxThreads").toString()).append(',');
+ appendJSonValue(writer, "currentThreadCount",
mBeanServer.getAttribute(tpName, "currentThreadCount").toString()).append(',');
+ appendJSonValue(writer, "currentThreadsBusy",
mBeanServer.getAttribute(tpName, "currentThreadsBusy").toString());
+ writer.append('}');
+
+ ObjectName grpName = null;
+ for (ObjectName objectName : globalRequestProcessors) {
+ // Find the HTTP/1.1 RequestGroupInfo - BZ 65404
+ if (name.equals(objectName.getKeyProperty("name")) &&
objectName.getKeyProperty("Upgrade") == null) {
+ grpName = objectName;
+ }
+ }
+
+ if (grpName != null) {
+ writer.append(',').println();
+
writer.append('"').append("requestInfo").append('"').append(':').append('{');
+ appendJSonValue(writer, "maxTime",
mBeanServer.getAttribute(grpName, "maxTime").toString()).append(',');
+ appendJSonValue(writer, "processingTime",
mBeanServer.getAttribute(grpName, "processingTime").toString()).append(',');
+ appendJSonValue(writer, "requestCount",
mBeanServer.getAttribute(grpName, "requestCount").toString()).append(',');
+ appendJSonValue(writer, "errorCount",
mBeanServer.getAttribute(grpName, "errorCount").toString()).append(',');
+ appendJSonValue(writer, "bytesReceived",
mBeanServer.getAttribute(grpName, "bytesReceived").toString()).append(',');
+ appendJSonValue(writer, "bytesSent",
mBeanServer.getAttribute(grpName, "bytesSent").toString());
+ writer.append('}').println();
+ // Note: No detailed per processor info
+ }
+
+ writer.append('}');
}
}
@@ -530,9 +645,9 @@ public class StatusTransformer {
*/
public static void writeDetailedState(PrintWriter writer, MBeanServer
mBeanServer, int mode) throws Exception {
+ ObjectName queryHosts = new ObjectName("*:j2eeType=WebModule,*");
+ Set<ObjectName> hostsON = mBeanServer.queryNames(queryHosts, null);
if (mode == 0) {
- ObjectName queryHosts = new ObjectName("*:j2eeType=WebModule,*");
- Set<ObjectName> hostsON = mBeanServer.queryNames(queryHosts, null);
// Navigation menu
writer.print("<h1>");
@@ -575,6 +690,21 @@ public class StatusTransformer {
} else if (mode == 1) {
// for now we don't write out the Detailed state in XML
+ } else if (mode == 2) {
+ writer.append(',').println();
+
writer.append('"').append("context").append('"').append(':').append('[');
+ Iterator<ObjectName> iterator = hostsON.iterator();
+ boolean first = true;
+ while (iterator.hasNext()) {
+ if (first) {
+ first = false;
+ } else {
+ writer.append(',').println();
+ }
+ ObjectName contextON = iterator.next();
+ writeContext(writer, contextON, mBeanServer, mode);
+ }
+ writer.append(']').println();
}
}
@@ -593,42 +723,43 @@ public class StatusTransformer {
protected static void writeContext(PrintWriter writer, ObjectName
objectName, MBeanServer mBeanServer, int mode)
throws Exception {
- if (mode == 0) {
- String webModuleName = objectName.getKeyProperty("name");
- String name = webModuleName;
- if (name == null) {
- return;
- }
+ String webModuleName = objectName.getKeyProperty("name");
+ String name = webModuleName;
+ if (name == null) {
+ return;
+ }
- String hostName = null;
- String contextName = null;
- if (name.startsWith("//")) {
- name = name.substring(2);
- }
- int slash = name.indexOf('/');
- if (slash != -1) {
- hostName = name.substring(0, slash);
- contextName = name.substring(slash);
- } else {
- return;
- }
+ String hostName = null;
+ String contextName = null;
+ if (name.startsWith("//")) {
+ name = name.substring(2);
+ }
+ int slash = name.indexOf('/');
+ if (slash != -1) {
+ hostName = name.substring(0, slash);
+ contextName = name.substring(slash);
+ } else {
+ return;
+ }
- ObjectName queryManager = new ObjectName(
- objectName.getDomain() + ":type=Manager,context=" +
contextName + ",host=" + hostName + ",*");
- Set<ObjectName> managersON = mBeanServer.queryNames(queryManager,
null);
- ObjectName managerON = null;
- for (ObjectName aManagersON : managersON) {
- managerON = aManagersON;
- }
+ ObjectName queryManager = new ObjectName(
+ objectName.getDomain() + ":type=Manager,context=" +
contextName + ",host=" + hostName + ",*");
+ Set<ObjectName> managersON = mBeanServer.queryNames(queryManager,
null);
+ ObjectName managerON = null;
+ for (ObjectName aManagersON : managersON) {
+ managerON = aManagersON;
+ }
- ObjectName queryJspMonitor =
- new ObjectName(objectName.getDomain() +
":type=JspMonitor,WebModule=" + webModuleName + ",*");
- Set<ObjectName> jspMonitorONs =
mBeanServer.queryNames(queryJspMonitor, null);
+ ObjectName queryJspMonitor =
+ new ObjectName(objectName.getDomain() +
":type=JspMonitor,WebModule=" + webModuleName + ",*");
+ Set<ObjectName> jspMonitorONs =
mBeanServer.queryNames(queryJspMonitor, null);
- // Special case for the root context
- if (contextName.equals("/")) {
- contextName = "";
- }
+ // Special case for the root context
+ if (contextName.equals("/")) {
+ contextName = "";
+ }
+
+ if (mode == 0) {
writer.print("<h1>");
writer.print(Escape.htmlElementContent(name));
@@ -659,6 +790,35 @@ public class StatusTransformer {
} else if (mode == 1) {
// for now we don't write out the context in XML
+ } else if (mode == 2) {
+ writer.append('{');
+ appendJSonValue(writer, "name",
JSONFilter.escape(JSONFilter.escape(name))).append(',');
+ appendJSonValue(writer, "startTime",
+ new Date(((Long) mBeanServer.getAttribute(objectName,
"startTime")).longValue()).toString()).append(',');
+ appendJSonValue(writer, "startupTime",
mBeanServer.getAttribute(objectName, "startupTime").toString()).append(',');
+ appendJSonValue(writer, "tldScanTime",
mBeanServer.getAttribute(objectName, "tldScanTime").toString());
+ if (managerON != null) {
+ writeManager(writer, managerON, mBeanServer, mode);
+ }
+ if (jspMonitorONs != null) {
+ writeJspMonitor(writer, jspMonitorONs, mBeanServer, mode);
+ }
+ writer.append(',').println();
+
writer.append('"').append("wrapper").append('"').append(':').append('[');
+ String onStr = objectName.getDomain() +
":j2eeType=Servlet,WebModule=" + webModuleName + ",*";
+ ObjectName servletObjectName = new ObjectName(onStr);
+ Set<ObjectInstance> set =
mBeanServer.queryMBeans(servletObjectName, null);
+ boolean first = true;
+ for (ObjectInstance oi : set) {
+ if (first) {
+ first = false;
+ } else {
+ writer.append(',').println();
+ }
+ writeWrapper(writer, oi.getObjectName(), mBeanServer, mode);
+ }
+ writer.append(']').println();
+ writer.append('}');
}
}
@@ -697,6 +857,18 @@ public class StatusTransformer {
writer.print(formatTime(mBeanServer.getAttribute(objectName,
"processingTime"), false));
} else if (mode == 1) {
// for now we don't write out the wrapper details
+ } else if (mode == 2) {
+ writer.append(',').println();
+
writer.append('"').append("manager").append('"').append(':').append('{');
+ appendJSonValue(writer, "activeSessions",
mBeanServer.getAttribute(objectName, "activeSessions").toString()).append(',');
+ appendJSonValue(writer, "sessionCounter",
mBeanServer.getAttribute(objectName, "sessionCounter").toString()).append(',');
+ appendJSonValue(writer, "maxActive",
mBeanServer.getAttribute(objectName, "maxActive").toString()).append(',');
+ appendJSonValue(writer, "rejectedSessions",
mBeanServer.getAttribute(objectName,
"rejectedSessions").toString()).append(',');
+ appendJSonValue(writer, "expiredSessions",
mBeanServer.getAttribute(objectName, "expiredSessions").toString()).append(',');
+ appendJSonValue(writer, "sessionMaxAliveTime",
mBeanServer.getAttribute(objectName,
"sessionMaxAliveTime").toString()).append(',');
+ appendJSonValue(writer, "sessionAverageAliveTime",
mBeanServer.getAttribute(objectName,
"sessionAverageAliveTime").toString()).append(',');
+ appendJSonValue(writer, "processingTime",
mBeanServer.getAttribute(objectName, "processingTime").toString());
+ writer.append('}');
}
}
@@ -733,6 +905,12 @@ public class StatusTransformer {
writer.print(jspReloadCount);
} else if (mode == 1) {
// for now we don't write out anything
+ } else if (mode == 2) {
+ writer.append(',').println();
+
writer.append('"').append("jsp").append('"').append(':').append('{');
+ appendJSonValue(writer, "jspCount",
Integer.toString(jspCount)).append(',');
+ appendJSonValue(writer, "jspReloadCount",
Integer.toString(jspReloadCount));
+ writer.append('}');
}
}
@@ -750,11 +928,11 @@ public class StatusTransformer {
public static void writeWrapper(PrintWriter writer, ObjectName objectName,
MBeanServer mBeanServer, int mode)
throws Exception {
- if (mode == 0) {
- String servletName = objectName.getKeyProperty("name");
+ String servletName = objectName.getKeyProperty("name");
- String[] mappings = (String[]) mBeanServer.invoke(objectName,
"findMappings", null, null);
+ String[] mappings = (String[]) mBeanServer.invoke(objectName,
"findMappings", null, null);
+ if (mode == 0) {
writer.print("<h2>");
writer.print(Escape.htmlElementContent(servletName));
if (mappings != null && mappings.length > 0) {
@@ -785,6 +963,16 @@ public class StatusTransformer {
writer.print("</p>");
} else if (mode == 1) {
// for now we don't write out the wrapper details
+ } else if (mode == 2) {
+ writer.append('{');
+ appendJSonValue(writer, "servletName",
JSONFilter.escape(servletName)).append(',');
+ appendJSonValue(writer, "processingTime",
mBeanServer.getAttribute(objectName, "processingTime").toString()).append(',');
+ appendJSonValue(writer, "maxTime",
mBeanServer.getAttribute(objectName, "maxTime").toString()).append(',');
+ appendJSonValue(writer, "requestCount",
mBeanServer.getAttribute(objectName, "requestCount").toString()).append(',');
+ appendJSonValue(writer, "errorCount",
mBeanServer.getAttribute(objectName, "errorCount").toString()).append(',');
+ appendJSonValue(writer, "loadTime",
mBeanServer.getAttribute(objectName, "loadTime").toString()).append(',');
+ appendJSonValue(writer, "classLoadTime",
mBeanServer.getAttribute(objectName, "classLoadTime").toString());
+ writer.append('}');
}
}
diff --git a/test/org/apache/catalina/manager/TestStatusTransformer.java
b/test/org/apache/catalina/manager/TestStatusTransformer.java
new file mode 100644
index 0000000000..b031004979
--- /dev/null
+++ b/test/org/apache/catalina/manager/TestStatusTransformer.java
@@ -0,0 +1,82 @@
+/*
+ * 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.catalina.manager;
+
+import java.io.File;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.apache.catalina.startup.SimpleHttpClient.CRLF;
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.json.JSONParser;
+
+public class TestStatusTransformer extends TomcatBaseTest {
+
+ @Test
+ public void testJSON() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // Add default servlet to make some requests
+ File appDir = new File("test/webapp");
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+ ctxt.setPrivileged(true);
+ Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default",
+ "org.apache.catalina.servlets.DefaultServlet");
+ defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1");
+ ctxt.addServletMappingDecoded("/", "default");
+ Tomcat.addServlet(ctxt, "status",
"org.apache.catalina.manager.StatusManagerServlet");
+ ctxt.addServletMappingDecoded("/status/*", "status");
+ ctxt.addMimeMapping("html", "text/html");
+ Context ctxt2 = tomcat.addContext("/test", null);
+ Tomcat.addServlet(ctxt2, "status",
"org.apache.catalina.manager.StatusManagerServlet");
+ ctxt.addServletMappingDecoded("/somepath/*", "status");
+ tomcat.start();
+
+ SimpleHttpClient client = new SimpleHttpClient() {
+ @Override
+ public boolean isResponseBodyOK() {
+ return true;
+ }
+ };
+ client.setPort(getPort());
+ client.setRequest(new String[] {
+ "GET /index.html HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Connection: Close" + CRLF + CRLF });
+ client.connect();
+ client.processRequest(true);
+
+ client.setRequest(new String[] {
+ "GET /status/all?JSON=true HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Connection: Close" + CRLF + CRLF });
+ client.connect();
+ client.processRequest(true);
+ String json = client.getResponseBody();
+
+ JSONParser parser = new JSONParser(json);
+ String result = parser.parse().toString();
+ System.out.println(result);
+ Assert.assertTrue(result.contains("name=localhost/"));
+ }
+
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f2cb8d1c79..69cc6ad255 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -111,6 +111,11 @@
<bug>67667</bug>: <code>TLSCertificateReloadListener</code> prints
unreadable
rendering of <code>X509Certificate#getNotAfter()</code>. (michaelo)
</fix>
+ <update>
+ The status servlet included in the manager webapp can now output
+ statistics as JSON, using the <code>JSON=true</code> URL parameter.
+ (remm)
+ </update>
</changelog>
</subsection>
<subsection name="Other">
diff --git a/webapps/docs/manager-howto.xml b/webapps/docs/manager-howto.xml
index 6ae9da195c..f21ac584d3 100644
--- a/webapps/docs/manager-howto.xml
+++ b/webapps/docs/manager-howto.xml
@@ -1036,6 +1036,11 @@
http://localhost:8080/manager/status/all?XML=true</source>
<p>Displays server status information in XML format.</p>
+<source>http://localhost:8080/manager/status?JSON=true
+http://localhost:8080/manager/status/all?JSON=true</source>
+
+<p>Displays server status information in JSON format.</p>
+
<p>First, you have the server and JVM version number, JVM provider, OS name
and number followed by the architecture type.</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]