This is an automated email from the ASF dual-hosted git repository.
csutherl 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 91ac751845 Use reflection to load clustering classes in
sessionsList.jsp so the sessions list page renders correctly when clustering
JARs are not present at runtime
91ac751845 is described below
commit 91ac751845e853418f84bf917201c591810b3733
Author: Coty Sutherland <[email protected]>
AuthorDate: Fri Aug 7 21:59:38 2026 -0400
Use reflection to load clustering classes in sessionsList.jsp so the
sessions list page renders correctly when clustering JARs are not present at
runtime
---
.../apache/catalina/manager/TestManagerWebapp.java | 86 ++++++++++++++++++++++
webapps/docs/changelog.xml | 5 ++
webapps/manager/WEB-INF/jsp/sessionsList.jsp | 35 +++++++--
3 files changed, 120 insertions(+), 6 deletions(-)
diff --git a/test/org/apache/catalina/manager/TestManagerWebapp.java
b/test/org/apache/catalina/manager/TestManagerWebapp.java
index 12261e9584..1e23f0d7af 100644
--- a/test/org/apache/catalina/manager/TestManagerWebapp.java
+++ b/test/org/apache/catalina/manager/TestManagerWebapp.java
@@ -22,6 +22,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletResponse;
@@ -34,6 +36,7 @@ import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.authenticator.TestBasicAuthParser.BasicAuthHeader;
+import org.apache.catalina.filters.Constants;
import org.apache.catalina.realm.MemoryRealm;
import org.apache.catalina.realm.MessageDigestCredentialHandler;
import org.apache.catalina.startup.Catalina;
@@ -614,6 +617,89 @@ public class TestManagerWebapp extends TomcatBaseTest {
}
}
+ /**
+ * Verify that manager JSPs compile and render correctly.
+ */
+ @Test
+ public void testJsps() throws Exception {
+ ignoreTearDown = true;
+ Tomcat tomcat = getTomcatInstance();
+ tomcat.addUser("admin", "sekr3t");
+ tomcat.addRole("admin", "manager-gui");
+
+ File webappDir = new File(getBuildDirectory(), "webapps");
+ File appDir = new File(webappDir, "manager");
+ tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath());
+
+ Context ctx = tomcat.addContext("/testapp", null);
+ Tomcat.addServlet(ctx, "default", new
org.apache.catalina.servlets.DefaultServlet());
+ ctx.addServletMappingDecoded("/", "default");
+
+ tomcat.start();
+
+ SimpleHttpClient client = new SimpleHttpClient() {
+ @Override
+ public boolean isResponseBodyOK() {
+ return true;
+ }
+ };
+ client.setPort(getPort());
+ String basicHeader =
+ (new BasicAuthHeader("Basic", "admin",
"sekr3t")).getHeader().toString();
+
+ // Hit the HTML manager entry point to get a session and CSRF nonce
+ // @formatter:off
+ client.setRequest(new String[] {
+ "GET /manager/html HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Authorization: " + basicHeader + CRLF +
+ "Connection: Close" + CRLF +
+ CRLF
+ });
+ // @formatter:on
+ client.connect();
+ client.processRequest(true);
+ Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode());
+
+ String body = client.getResponseBody();
+ Pattern noncePattern = Pattern.compile(
+ Pattern.quote(Constants.CSRF_NONCE_REQUEST_PARAM) +
"=([A-F0-9]+)");
+ Matcher m = noncePattern.matcher(body);
+ Assert.assertTrue("CSRF nonce not found in manager HTML response",
m.find());
+ String nonce = m.group(1);
+
+ String sessionCookie = null;
+ for (String header : client.getResponseHeaders()) {
+ if (header.startsWith("Set-Cookie:")) {
+ String cookieValue =
header.substring("Set-Cookie:".length()).trim();
+ sessionCookie = cookieValue.split(";")[0];
+ break;
+ }
+ }
+ Assert.assertNotNull("Session cookie not found", sessionCookie);
+
+ // Access sessions list page with the CSRF nonce
+ // @formatter:off
+ client.setRequest(new String[] {
+ "GET /manager/html/sessions?path=/testapp&" +
+ Constants.CSRF_NONCE_REQUEST_PARAM + "=" + nonce +
+ " HTTP/1.1" + CRLF +
+ "Host: localhost" + CRLF +
+ "Authorization: " + basicHeader + CRLF +
+ "Cookie: " + sessionCookie + CRLF +
+ "Connection: Close" + CRLF +
+ CRLF
+ });
+ // @formatter:on
+ client.connect();
+ client.processRequest(true);
+ Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode());
+ Assert.assertTrue(client.getResponseBody().contains("Sessions
Administration"));
+ Assert.assertTrue(client.getResponseBody().contains("active
Sessions"));
+
+ tomcat.stop();
+ }
+
private static class FailOnceListener implements LifecycleListener {
private volatile boolean firstRun = true;
@Override
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 260382eedc..27aea75843 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -460,6 +460,11 @@
Manager: Drop session handling dedicated to extracting the locale from
Tapestry attributes, used for locale session sorting. (remm)
</update>
+ <fix>
+ Manager: Use reflection to load clustering classes in
+ <code>sessionsList.jsp</code> so the sessions list page renders
+ correctly when clustering JARs are not present. (csutherl)
+ </fix>
<!-- Entries for backport and removal before 12.0.0-M1 below this line
-->
<fix>
Documentation: Better sample httpd configuration for use with SSLValve
diff --git a/webapps/manager/WEB-INF/jsp/sessionsList.jsp
b/webapps/manager/WEB-INF/jsp/sessionsList.jsp
index f7e780a23e..82a2c7f830 100644
--- a/webapps/manager/WEB-INF/jsp/sessionsList.jsp
+++ b/webapps/manager/WEB-INF/jsp/sessionsList.jsp
@@ -18,7 +18,6 @@
<%@page session="false" contentType="text/html; charset=UTF-8" %>
<%@page import="java.util.Collection" %>
<%@page import="org.apache.catalina.Session" %>
-<%@page import="org.apache.catalina.ha.session.DeltaSession" %>
<%@page import="org.apache.catalina.manager.Constants" %>
<%@page import="org.apache.catalina.manager.JspHelper" %>
<%@page import="org.apache.catalina.util.ContextName" %>
@@ -27,7 +26,27 @@
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<%@page import="org.apache.catalina.manager.DummyProxySession"%><html
xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<%@page import="java.lang.reflect.Method"%>
+<%@page import="org.apache.catalina.manager.DummyProxySession"%>
+<%!
+private static final Class<?> deltaSessionClass;
+private static final Method isPrimaryMethod;
+static {
+ Class<?> clazz = null;
+ Method method = null;
+ try {
+ clazz = Class.forName("org.apache.catalina.ha.session.DeltaSession");
+ method = clazz.getMethod("isPrimarySession");
+ } catch (ClassNotFoundException e) {
+ // Expected when clustering JARs are not present
+ } catch (NoSuchMethodException e) {
+ // Should not happen if the class is available
+ }
+ deltaSessionClass = clazz;
+ isPrimaryMethod = method;
+}
+%>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<% String path = (String) request.getAttribute("path");
String version = (String) request.getAttribute("version");
ContextName cn = new ContextName(path, version);
@@ -105,11 +124,15 @@
for (Session currentSession : activeSessions) {
String currentSessionId = JspHelper.escapeXml(currentSession.getId());
String type;
- if (currentSession instanceof DeltaSession) {
- if (((DeltaSession) currentSession).isPrimarySession()) {
+ if (deltaSessionClass != null &&
deltaSessionClass.isInstance(currentSession)) {
+ try {
+ if
(Boolean.TRUE.equals(isPrimaryMethod.invoke(currentSession))) {
+ type = "Primary";
+ } else {
+ type = "Backup";
+ }
+ } catch (Exception e) {
type = "Primary";
- } else {
- type = "Backup";
}
} else if (currentSession instanceof DummyProxySession) {
type = "Proxy";
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]