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 70d0a422712e516c15a9ec9c5dbc892d56b5c8b4 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 ebbe610bc8..5ec41eb5dc 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]
