7

This question: When using a JMX server with ephemeral port, how to get the server port number? indicates that we can use sun.management.ConnectorAddressLink with the params specified to discover the ephemeral JMX port (if we start our process with com.sun.management.jmxremote.port=0).

However for Java 9+, these classes were made private and can no longer be accessed. Is there any way one could programmatically find which port JMX has bound to?

UkFLSUI
  • 5,509
  • 6
  • 32
  • 47
FreeMemory
  • 8,444
  • 7
  • 37
  • 49
  • This question was having a bounty + grace period and I posted an answer before bounty expired. I was hoping bounty will be awarded base don the submission date time. – Amit Vyas Jul 21 '20 at 19:10

1 Answers1

2

ConnectorAddressLink class can be invoked via jdk.internal.agent module.

Below sample code shows how we can programmatically find JMX ephemeral port in java 9/11/14 (Tested with OpenJDK versions only).

Java 9+ Code:

In the below code you can get PID or use 0 to denote current PID hence providing a way to show JMX URL in both scenarios and they will be identical.

public class JMXEphemeralPortTest_JAVA9Plus {

  public static void main(String[] args) throws IOException {
    String jmxURL = "NO JMX URL";
    String jmxURL2 = "NO JMX URL";
    if (isJava9Plus(System.getProperties())) {
      long pid = ProcessHandle.current().pid();
      jmxURL = "From Java 9 Plus:-->" + importRemoteFrom_Java9Plus(Math.toIntExact(pid));
      jmxURL2 = "From Java 9 Plus:-->" + importRemoteFrom_Java9Plus(0);
    }
    System.out.println(jmxURL);
    System.out.println(jmxURL2);
  }
  private static boolean isJava9Plus(Properties properties) {
    double javaVersion = Double.parseDouble(properties.getProperty("java.specification.version"));
    System.out.println("Java Version:"+javaVersion);
    return javaVersion >= 1.9;
  }
  private static String importRemoteFrom_Java9Plus(int pid) {
    try {
      final Class<?> clazz = Class.forName("jdk.internal.agent.ConnectorAddressLink");
      final Method method = clazz.getMethod("importRemoteFrom", int.class);
      final Object instance = clazz.getDeclaredConstructor().newInstance();
      Map<String, String> map = (Map<String, String>) method.invoke(instance, pid);
      return map.get("sun.management.JMXConnectorServer.0.remoteAddress");
    } catch (Exception e) {
      throw new IllegalStateException("Could not load needed java 9+ class", e);
    }
  }
}

*Java 9+ VM Arguments for the module and JMX:

--add-modules jdk.management,jdk.management.agent
--add-exports=jdk.management.agent/jdk.internal.agent=ALL-UNNAMED
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port=0
-Dcom.sun.management.jmxremote.local.only=false

Java 9+ Result:

Java Version:14.0 From Java 9 Plus:-->service:jmx:rmi:///jndi/rmi://XXXX:50678/jmxrmi From Java 9 Plus:-->service:jmx:rmi:///jndi/rmi://XXXX:50678/jmxrmi

Java Version:9.0 From Java 9 Plus:-->service:jmx:rmi:///jndi/rmi://XXXX:60342/jmxrmi From Java 9 Plus:-->service:jmx:rmi:///jndi/rmi://XXXX:60342/jmxrmi

Happy programming.

Amit Vyas
  • 790
  • 3
  • 10