This is an automated email from the ASF dual-hosted git repository.
csutherl pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new 5a4515fcce Use reflection to load clustering classes in
sessionsList.jsp so the sessions list page renders correctly when clustering
JARs are not present at runtime
5a4515fcce is described below
commit 5a4515fcce94bfb8a71d9c938d43166eab85e62a
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 | 4 +
webapps/manager/WEB-INF/jsp/sessionsList.jsp | 35 +++++++--
3 files changed, 119 insertions(+), 6 deletions(-)
diff --git a/test/org/apache/catalina/manager/TestManagerWebapp.java
b/test/org/apache/catalina/manager/TestManagerWebapp.java
index 3f281bb52e..1766eaa45c 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 7b5453cf68..629162bdca 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -264,6 +264,10 @@
Documentation: Expand the description of the <code>%S</code> (session
ID)
access log pattern token. (markt)
</fix>
+ <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)
</changelog>
</subsection>
<subsection name="Other">
diff --git a/webapps/manager/WEB-INF/jsp/sessionsList.jsp
b/webapps/manager/WEB-INF/jsp/sessionsList.jsp
index b4ae54d710..8742115d71 100644
--- a/webapps/manager/WEB-INF/jsp/sessionsList.jsp
+++ b/webapps/manager/WEB-INF/jsp/sessionsList.jsp
@@ -19,14 +19,33 @@
<%@page import="java.util.Collection" %>
<%@page import="org.apache.catalina.manager.JspHelper" %>
<%@page import="org.apache.catalina.Session" %>
-<%@page import="org.apache.catalina.ha.session.DeltaSession" %>
<%@page import="org.apache.catalina.util.ContextName" %>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"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);
@@ -104,11 +123,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]