Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discovering JMX Ephemeral Port Number for Java 9+

Tags:

java

jmx

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?

like image 878
FreeMemory Avatar asked Jul 10 '20 09:07

FreeMemory


People also ask

What is the JMX port number?

The default port for secure JMX access is 9875 and the default username and password are admin and springsource .

Is JMX a TCP or UDP?

The JMX Messaging Protocol (JMXMP) connector is a configuration of the generic connector where the transport protocol is based on TCP and the object wrapping is native Java serialization.

What is JMX RMI?

JMX is a generic API for publishing an interface to monitor the internals of a Java application ( A Java version of SNMP ). RMI is for Remote Method Invocation a Java specific implementation of a Remote Procedure Call interface, they are not related at all.


1 Answers

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.

like image 133
Amit Vyas Avatar answered Sep 28 '22 00:09

Amit Vyas