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

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

commit 04cf4fb09f2927e82c1ba4363f033d8f101f6cb1
Author: opencode <[email protected]>
AuthorDate: Tue Sep 29 19:26:31 2026 +0200

    Close the JMX connection owned by a task or condition when no reference is 
used to share it
---
 .../catalina/ant/jmx/JMXAccessorConditionBase.java | 37 +++++++++-
 .../apache/catalina/ant/jmx/JMXAccessorTask.java   | 82 +++++++++++++++++-----
 2 files changed, 98 insertions(+), 21 deletions(-)

diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java 
b/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
index 6d08cd478e..084daf5b03 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorConditionBase.java
@@ -21,6 +21,7 @@ import java.net.MalformedURLException;
 
 import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
 
 import org.apache.tools.ant.ProjectComponent;
 import org.apache.tools.ant.taskdefs.condition.Condition;
@@ -46,6 +47,12 @@ public abstract class JMXAccessorConditionBase extends 
ProjectComponent implemen
     private String value;
     private String ref = "jmx.server";
 
+    /*
+     * Connector created for the current evaluation when the connection is not 
shared through a project reference. It
+     * is closed once the value has been read so repeated waitfor evaluations 
do not accumulate connections.
+     */
+    private JMXConnector jmxConnector;
+
     /**
      * Get the attribute name.
      *
@@ -217,8 +224,29 @@ public abstract class JMXAccessorConditionBase extends 
ProjectComponent implemen
      * @throws IOException           Connection error
      */
     protected MBeanServerConnection getJMXConnection() throws 
MalformedURLException, IOException {
-        return JMXAccessorTask.accessJMXConnection(getProject(), getUrl(), 
getHost(), getPort(), getUsername(),
-                getPassword(), ref);
+        if (ref != null && !ref.isEmpty() && getProject() != null) {
+            // Reuse or establish a shared connection stored in the project 
reference; this condition does not own it.
+            return JMXAccessorTask.accessJMXConnection(getProject(), getUrl(), 
getHost(), getPort(), getUsername(),
+                    getPassword(), ref);
+        }
+        // No reference to store it in: this condition owns the connection and 
closes it after the evaluation.
+        jmxConnector = JMXAccessorTask.createJMXConnector(getUrl(), getHost(), 
getPort(), getUsername(), getPassword());
+        return jmxConnector.getMBeanServerConnection();
+    }
+
+    /**
+     * Close the JMX connection owned by this condition, if any. Connections 
shared through a project reference are
+     * left open for reuse.
+     */
+    private void closeJMXConnector() {
+        if (jmxConnector != null) {
+            try {
+                jmxConnector.close();
+            } catch (IOException e) {
+                // Ignore errors while closing
+            }
+            jmxConnector = null;
+        }
     }
 
     /**
@@ -228,7 +256,8 @@ public abstract class JMXAccessorConditionBase extends 
ProjectComponent implemen
      */
     protected String accessJMXValue() {
         try {
-            Object result = getJMXConnection().getAttribute(new 
ObjectName(name), attribute);
+            MBeanServerConnection jmxServerConnection = getJMXConnection();
+            Object result = jmxServerConnection.getAttribute(new 
ObjectName(name), attribute);
             if (result != null) {
                 return result.toString();
             }
@@ -237,6 +266,8 @@ public abstract class JMXAccessorConditionBase extends 
ProjectComponent implemen
              * Exceptions are ignored for compatibility with the waitFor task 
when waiting for the server to start. If
              * the exception was thrown the build task would fail rather than 
wait.
              */
+        } finally {
+            closeJMXConnector();
         }
         return null;
     }
diff --git a/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java 
b/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
index 91d66c4ef8..4a80078fde 100644
--- a/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
+++ b/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
@@ -125,6 +125,12 @@ public class JMXAccessorTask extends 
BaseRedirectorHelperTask {
 
     private final Properties properties = new Properties();
 
+    /*
+     * Connector owned by this task for the duration of a single execution. 
Only set when the connection was created
+     * by this task and is not shared via a project reference, so it can be 
closed when the task completes.
+     */
+    private JMXConnector jmxConnector;
+
     // ------------------------------------------------------------- Properties
 
     /**
@@ -398,6 +404,7 @@ public class JMXAccessorTask extends 
BaseRedirectorHelperTask {
                     handleErrorOutput(e.getMessage());
                 }
             } finally {
+                closeJMXConnector();
                 closeRedirector();
             }
         }
@@ -420,6 +427,29 @@ public class JMXAccessorTask extends 
BaseRedirectorHelperTask {
      */
     public static MBeanServerConnection createJMXConnection(String url, String 
host, String port, String username,
             String password) throws MalformedURLException, IOException {
+        return createJMXConnector(url, host, port, username, 
password).getMBeanServerConnection();
+    }
+
+    /**
+     * Create a new JMX Connection with auth when username and password is 
set. The caller is responsible for closing
+     * the returned connector.
+     *
+     * @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 username User name for the connection
+     * @param password Credentials corresponding to the specified user
+     *
+     * @throws MalformedURLException Invalid URL specified
+     * @throws IOException           Other connection error
+     *
+     * @return the JMX connector
+     *
+     * @since 12.0.x
+     */
+    public static JMXConnector createJMXConnector(String url, String host, 
String port, String username,
+            String password) throws MalformedURLException, IOException {
         String urlForJMX;
         if (url != null) {
             urlForJMX = url;
@@ -434,9 +464,7 @@ public class JMXAccessorTask extends 
BaseRedirectorHelperTask {
             environment = new HashMap<>();
             environment.put(JMXConnector.CREDENTIALS, credentials);
         }
-        // FIXME: Referencing JMXConnector instead of MBeanServerConnection is 
needed to close the connection
-        return JMXConnectorFactory.connect(new JMXServiceURL(urlForJMX), 
environment).getMBeanServerConnection();
-
+        return JMXConnectorFactory.connect(new JMXServiceURL(urlForJMX), 
environment);
     }
 
     /**
@@ -515,30 +543,48 @@ public class JMXAccessorTask extends 
BaseRedirectorHelperTask {
      */
     protected MBeanServerConnection getJMXConnection() throws 
MalformedURLException, IOException {
         MBeanServerConnection jmxServerConnection = null;
-        if (isUseRef()) {
-            Object pref;
-            if (getProject() != null) {
-                pref = getProject().getReference(getRef());
-                if (pref != null) {
-                    try {
-                        jmxServerConnection = (MBeanServerConnection) pref;
-                    } catch (ClassCastException cce) {
-                        getProject().log("Wrong object reference " + getRef() 
+ " - " + pref.getClass());
-                        return null;
-                    }
+        boolean refAvailable = isUseRef() && getProject() != null;
+        if (refAvailable) {
+            Object pref = getProject().getReference(getRef());
+            if (pref != null) {
+                try {
+                    jmxServerConnection = (MBeanServerConnection) pref;
+                } catch (ClassCastException cce) {
+                    getProject().log("Wrong object reference " + getRef() + " 
- " + pref.getClass());
+                    return null;
                 }
             }
-            if (jmxServerConnection == null) {
+        }
+        if (jmxServerConnection == null) {
+            if (refAvailable) {
+                // A reference is available: the connection is stored in the 
project for reuse by later tasks in
+                // the same build and is intentionally left open, so this task 
does not own it.
                 jmxServerConnection = accessJMXConnection(getProject(), 
getUrl(), getHost(), getPort(), getUsername(),
                         getPassword(), getRef());
+            } else {
+                // No reference to store it in: this task owns the connection 
and closes it when it completes.
+                jmxConnector = createJMXConnector(getUrl(), getHost(), 
getPort(), getUsername(), getPassword());
+                jmxServerConnection = jmxConnector.getMBeanServerConnection();
             }
-        } else {
-            jmxServerConnection = accessJMXConnection(getProject(), getUrl(), 
getHost(), getPort(), getUsername(),
-                    getPassword(), null);
         }
         return jmxServerConnection;
     }
 
+    /**
+     * Close the JMX connection owned by this task, if any. Connections shared 
through a project reference are not
+     * owned by this task and are left open for reuse.
+     */
+    private void closeJMXConnector() {
+        if (jmxConnector != null) {
+            try {
+                jmxConnector.close();
+            } catch (IOException e) {
+                // Ignore errors while closing
+            }
+            jmxConnector = null;
+        }
+    }
+
     /**
      * Execute the specified command, based on the configured properties.
      *


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to