You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2026/09/29 18:54:52 UTC
(tomcat) 12/13: Raise a BuildException when a JMX task or condition explicitly specifies a target that conflicts with an existing connection reference instead of silently ignoring it
This is an automated email from the ASF dual-hosted git repository.
rmaucher pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 84e6ebb1b63e4cd80ed032fa525ae3e3c7b5f509
Author: opencode <re...@apache.org>
AuthorDate: Tue Sep 29 19:37:47 2026 +0200
Raise a BuildException when a JMX task or condition explicitly specifies a target that conflicts with an existing connection reference instead of silently ignoring it
---
.../catalina/ant/jmx/JMXAccessorConditionBase.java | 9 ++-
.../apache/catalina/ant/jmx/JMXAccessorTask.java | 80 +++++++++++++++++-----
.../catalina/ant/jmx/TestJMXAccessorTask.java | 63 +++++++++++++++++
3 files changed, 132 insertions(+), 20 deletions(-)
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java b/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
index 084daf5b03..be21134b5d 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
@@ -38,8 +38,13 @@ public abstract class JMXAccessorConditionBase extends ProjectComponent implemen
}
private String url = null;
- private String host = "localhost";
- private String port = "8050";
+ /*
+ * A null host or port means the attribute was not specified. The defaults (localhost and 8050) are applied when
+ * the JMX service URL is built, so an explicitly specified value can be detected and compared against an existing
+ * connection reference.
+ */
+ private String host = null;
+ private String port = null;
private String password = null;
private String username = null;
private String name = null;
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java b/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
index bcefcda81f..7f2a40261b 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
@@ -95,6 +95,12 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
*/
public static final String JMX_SERVICE_SUFFIX = "/jmxrmi";
+ /*
+ * Suffix of the project reference under which the JMX service URL of a connection stored under reference
+ * <em>refId</em> is recorded, to be able to detect later requests that explicitly specify a different target.
+ */
+ private static final String JMX_SERVICE_URL_REF_SUFFIX = "-jmx-service-url";
+
// ----------------------------------------------------- Instance Variables
private String name = null;
@@ -103,9 +109,14 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
private String url = null;
- private String host = "localhost";
+ /*
+ * A null host or port means the attribute was not specified. The defaults (localhost and 8050) are applied when
+ * the JMX service URL is built, so an explicitly specified value can be detected and compared against an existing
+ * connection reference.
+ */
+ private String host = null;
- private String port = "8050";
+ private String port = null;
private String password = null;
@@ -436,8 +447,8 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
*
* @param url URL to be used for the JMX connection (if specified, it is a complete URL so host and port will
* not be used)
- * @param host Host name of the JMX server
- * @param port Port number for the JMX server
+ * @param host Host name of the JMX server ({@code null} selects the default host)
+ * @param port Port number for the JMX server ({@code null} selects the default port)
* @param username User name for the connection
* @param password Credentials corresponding to the specified user
*
@@ -450,12 +461,7 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
*/
public static JMXConnector createJMXConnector(String url, String host, String port, String username,
String password) throws MalformedURLException, IOException {
- String urlForJMX;
- if (url != null) {
- urlForJMX = url;
- } else {
- urlForJMX = JMX_SERVICE_PREFIX + host + ":" + port + JMX_SERVICE_SUFFIX;
- }
+ String urlForJMX = resolveJMXServiceURL(url, host, port);
Map<String,String[]> environment = null;
if (username != null && password != null) {
String[] credentials = new String[2];
@@ -467,6 +473,30 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
return JMXConnectorFactory.connect(new JMXServiceURL(urlForJMX), environment);
}
+ /*
+ * Build the JMX service URL from the connection parameters, applying the default host and port when they are not
+ * specified.
+ */
+ private static String resolveJMXServiceURL(String url, String host, String port) {
+ if (url != null) {
+ return url;
+ }
+ String effectiveHost = host != null ? host : "localhost";
+ String effectivePort = port != null ? port : "8050";
+ return JMX_SERVICE_PREFIX + effectiveHost + ":" + effectivePort + JMX_SERVICE_SUFFIX;
+ }
+
+ /*
+ * The JMX service URL targeted by the given connection parameters, or null when no target was specified at all
+ * (in which case the parameters say nothing about which server the caller wants to reach).
+ */
+ private static String specifiedJMXServiceURL(String url, String host, String port) {
+ if (url == null && host == null && port == null) {
+ return null;
+ }
+ return resolveJMXServiceURL(url, host, port);
+ }
+
/**
* test the if condition
*
@@ -515,18 +545,32 @@ public class JMXAccessorTask extends BaseRedirectorHelperTask {
boolean isRef = project != null && refId != null && !refId.isEmpty();
if (isRef) {
Object pref = project.getReference(refId);
- try {
- jmxServerConnection = (MBeanServerConnection) pref;
- } catch (ClassCastException cce) {
- project.log("wrong object reference " + refId + " - " + pref.getClass());
- return null;
+ if (pref != null) {
+ try {
+ jmxServerConnection = (MBeanServerConnection) pref;
+ } catch (ClassCastException cce) {
+ project.log("wrong object reference " + refId + " - " + pref.getClass());
+ return null;
+ }
+ }
+ if (jmxServerConnection != null) {
+ // The reference already holds an open connection and is reused. If this call explicitly specifies a
+ // different target, the explicit parameters would be silently ignored, so fail instead.
+ String specified = specifiedJMXServiceURL(url, host, port);
+ Object openedTo = project.getReference(refId + JMX_SERVICE_URL_REF_SUFFIX);
+ if (specified != null && openedTo != null && !specified.equals(openedTo)) {
+ throw new BuildException("The JMX connection reference '" + refId + "' already refers to a "
+ + "connection to '" + openedTo + "'. This task specifies a connection to '" + specified
+ + "'. Open the second server under a different ref or omit the connection parameters "
+ + "to reuse the existing connection.");
+ }
+ return jmxServerConnection;
}
}
- if (jmxServerConnection == null) {
- jmxServerConnection = createJMXConnection(url, host, port, username, password);
- }
+ jmxServerConnection = createJMXConnection(url, host, port, username, password);
if (isRef && jmxServerConnection != null) {
project.addReference(refId, jmxServerConnection);
+ project.addReference(refId + JMX_SERVICE_URL_REF_SUFFIX, resolveJMXServiceURL(url, host, port));
}
return jmxServerConnection;
}
diff --git a/test/org/apache/catalina/ant/jmx/TestJMXAccessorTask.java b/test/org/apache/catalina/ant/jmx/TestJMXAccessorTask.java
index b7104604d7..71927130c0 100644
--- a/test/org/apache/catalina/ant/jmx/TestJMXAccessorTask.java
+++ b/test/org/apache/catalina/ant/jmx/TestJMXAccessorTask.java
@@ -16,6 +16,14 @@
*/
package org.apache.catalina.ant.jmx;
+import java.lang.management.ManagementFactory;
+import java.net.ServerSocket;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
@@ -23,11 +31,15 @@ import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
public class TestJMXAccessorTask {
@@ -89,6 +101,57 @@ public class TestJMXAccessorTask {
}
+ /*
+ * A cached connection reference is reused. If the caller explicitly specifies a different target than the one the
+ * reference was opened to, a BuildException is raised instead of the explicit parameters being silently ignored.
+ */
+ @Test
+ public void testAccessJMXConnectionReuseAndMismatch() throws Exception {
+ int port = getAvailablePort();
+ Registry registry = LocateRegistry.createRegistry(port);
+ MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ JMXServiceURL serviceUrl = new JMXServiceURL(
+ JMXAccessorTask.JMX_SERVICE_PREFIX + "localhost:" + port + JMXAccessorTask.JMX_SERVICE_SUFFIX);
+ JMXConnectorServer connectorServer =
+ JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, null, mbeanServer);
+ connectorServer.start();
+ try {
+ Project project = new Project();
+ String ref = "jmx.server.test";
+ String host = "localhost";
+ String openPort = Integer.toString(port);
+
+ // First call opens the connection and stores it under the reference.
+ MBeanServerConnection first =
+ JMXAccessorTask.accessJMXConnection(project, null, host, openPort, null, null, ref);
+ Assert.assertNotNull(first);
+
+ // Reuse with no explicit target: the cached connection is returned.
+ MBeanServerConnection reused =
+ JMXAccessorTask.accessJMXConnection(project, null, null, null, null, null, ref);
+ Assert.assertSame(first, reused);
+
+ // Explicitly specifying a different port than the open connection must fail rather than be ignored.
+ try {
+ JMXAccessorTask.accessJMXConnection(project, null, host, "1", null, null, ref);
+ Assert.fail("Expected a BuildException for an explicit conflicting target");
+ } catch (BuildException expected) {
+ // Expected
+ }
+ } finally {
+ connectorServer.stop();
+ UnicastRemoteObject.unexportObject(registry, true);
+ }
+ }
+
+
+ private static int getAvailablePort() throws Exception {
+ try (ServerSocket socket = new ServerSocket(0)) {
+ return socket.getLocalPort();
+ }
+ }
+
+
private static TabularDataSupport createTabularData() throws OpenDataException {
CompositeType detailsType = new CompositeType("details", "details", new String[] { "name" },
new String[] { "name" }, new OpenType<?>[] { SimpleType.STRING });
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org