CASSANDRASC-59: Expose JMX host and port from JMXClient

In this commit, we expose the JMX host and port from JMXClient to make it available
to implementations of the `ICassandraAdapter`.

This information can be valuable as different implementations and integrations need
to have this information available during the application execution.

patch by Francisco Guerrero; reviewed by Dinesh Joshi, Yifan Cai for CASSANDRASC-59
diff --git a/CHANGES.txt b/CHANGES.txt
index f7626e1..15ac7f7 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 1.0.0
 -----
+ * Expose JMX host and port from JMXClient (CASSANDRASC-59)
  * Support retries in Sidecar Client on Invalid Checksum (CASSANDRASC-58)
  * Ignore unknown properties during Sidecar client deserialization (CASSANDRASC-53)
  * Create staging directory if it doesn't exists (CASSANDRASC-56)
diff --git a/common/src/main/java/org/apache/cassandra/sidecar/common/JmxClient.java b/common/src/main/java/org/apache/cassandra/sidecar/common/JmxClient.java
index b6182d0..b5db0a5 100644
--- a/common/src/main/java/org/apache/cassandra/sidecar/common/JmxClient.java
+++ b/common/src/main/java/org/apache/cassandra/sidecar/common/JmxClient.java
@@ -44,7 +44,8 @@
  */
 public class JmxClient implements NotificationListener, Closeable
 {
-    public static final String JMX_SERVICE_URL_FMT = "service:jmx:rmi://%s/jndi/rmi://%s:%d/jmxrmi";
+    public static final String JMX_PROTOCOL = "rmi";
+    public static final String JMX_URL_PATH_FORMAT = "/jndi/rmi://%s:%d/jmxrmi";
     public static final String REGISTRY_CONTEXT_SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
     private final JMXServiceURL jmxServiceURL;
     private MBeanServerConnection mBeanServerConnection;
@@ -188,19 +189,24 @@
         return connected;
     }
 
+    public String host()
+    {
+        return jmxServiceURL.getHost();
+    }
+
+    public int port()
+    {
+        return jmxServiceURL.getPort();
+    }
+
     private static JMXServiceURL buildJmxServiceURL(String host, int port)
     {
         if (host == null)
             return null;
 
-        if (host.contains(":"))
-        {
-            host = "[" + host + "]";
-            // Use square brackets to surround IPv6 addresses to fix CASSANDRA-7669 and CASSANDRA-17581
-        }
         try
         {
-            return new JMXServiceURL(String.format(JMX_SERVICE_URL_FMT, host, host, port));
+            return new JMXServiceURL(JMX_PROTOCOL, host, port, jmxUrlPath(host, port));
         }
         catch (MalformedURLException e)
         {
@@ -220,4 +226,21 @@
             connector.close();
         }
     }
+
+    private static String jmxUrlPath(String host, int port)
+    {
+        return String.format(JMX_URL_PATH_FORMAT, maybeAddSquareBrackets(host), port);
+    }
+
+    private static String maybeAddSquareBrackets(String host)
+    {
+        if (host == null // host is null
+            || host.isEmpty() // or host is empty
+            || host.charAt(0) == '[' // host already starts with square brackets
+            || !host.contains(":")) // or host doesn't contain ":" (not an IPv6 address)
+            return host;
+
+        // Use square brackets to surround IPv6 addresses to fix CASSANDRA-7669 and CASSANDRA-17581
+        return "[" + host + "]";
+    }
 }
diff --git a/src/test/integration/org/apache/cassandra/sidecar/common/JmxClientTest.java b/src/test/integration/org/apache/cassandra/sidecar/common/JmxClientTest.java
index dcfca90..846d1f8 100644
--- a/src/test/integration/org/apache/cassandra/sidecar/common/JmxClientTest.java
+++ b/src/test/integration/org/apache/cassandra/sidecar/common/JmxClientTest.java
@@ -39,19 +39,24 @@
     @CassandraIntegrationTest
     void testJmxConnectivity(CassandraTestContext context) throws IOException
     {
-        try (JmxClient jmxClient = getJmxClient(context))
+        try (JmxClient jmxClient = createJmxClient(context))
         {
             String opMode = jmxClient.proxy(SSProxy.class, SS_OBJ_NAME)
                                      .getOperationMode();
             assertThat(opMode).isNotNull();
             assertThat(opMode).isIn("LEAVING", "JOINING", "NORMAL", "DECOMMISSIONED", "CLIENT");
+
+            IUpgradeableInstance instance = context.cluster.getFirstRunningInstance();
+            IInstanceConfig config = instance.config();
+            assertThat(jmxClient.host()).isEqualTo(config.broadcastAddress().getAddress().getHostAddress());
+            assertThat(jmxClient.port()).isEqualTo(config.jmxPort());
         }
     }
 
     @CassandraIntegrationTest
     void testGossipInfo(CassandraTestContext context) throws IOException
     {
-        try (JmxClient jmxClient = getJmxClient(context))
+        try (JmxClient jmxClient = createJmxClient(context))
         {
             FailureDetector proxy = jmxClient.proxy(FailureDetector.class,
                                                     "org.apache.cassandra.net:type=FailureDetector");
@@ -66,7 +71,7 @@
     @CassandraIntegrationTest
     void testCorrectVersion(CassandraTestContext context) throws IOException
     {
-        try (JmxClient jmxClient = getJmxClient(context))
+        try (JmxClient jmxClient = createJmxClient(context))
         {
             jmxClient.proxy(SSProxy.class, SS_OBJ_NAME)
                      .refreshSizeEstimates();
@@ -94,11 +99,10 @@
     }
 
 
-    private static JmxClient getJmxClient(CassandraTestContext context)
+    private static JmxClient createJmxClient(CassandraTestContext context)
     {
         IUpgradeableInstance instance = context.cluster.getFirstRunningInstance();
         IInstanceConfig config = instance.config();
-        JmxClient client = new JmxClient(config.broadcastAddress().getAddress().getHostAddress(), config.jmxPort());
-        return client;
+        return new JmxClient(config.broadcastAddress().getAddress().getHostAddress(), config.jmxPort());
     }
 }