This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 4127ca5f2855e02abde0c0012e608fb662ff1ad6
Author: opencode <[email protected]>
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 5e7dd9884f..d6b71df30d 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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to