8219585: [TESTBUG] sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java passes trivially when it shouldn't
authorsgehwolf
Mon, 25 Feb 2019 15:41:24 +0100
changeset 54123 153419748bf8
parent 54122 4b1426ed1c44
child 54124 5d48ae032588
8219585: [TESTBUG] sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java passes trivially when it shouldn't Reviewed-by: dfuchs, sballal
test/jdk/sun/management/jmxremote/bootstrap/JMXAgentInterfaceBinding.java
test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java
--- a/test/jdk/sun/management/jmxremote/bootstrap/JMXAgentInterfaceBinding.java	Thu Mar 14 09:15:51 2019 +0100
+++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXAgentInterfaceBinding.java	Mon Feb 25 15:41:24 2019 +0100
@@ -28,10 +28,13 @@
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.management.remote.JMXConnector;
 import javax.management.remote.JMXConnectorFactory;
@@ -135,9 +138,9 @@
         private final int rmiPort;
         private final boolean useSSL;
         private final CountDownLatch latch;
-        private boolean failed;
-        private boolean jmxConnectWorked;
-        private boolean rmiConnectWorked;
+        private volatile boolean failed;
+        private volatile boolean jmxConnectWorked;
+        private volatile boolean rmiConnectWorked;
 
         private JMXConnectorThread(String addr,
                                    int jmxPort,
@@ -156,6 +159,7 @@
             try {
                 connect();
             } catch (IOException e) {
+                e.printStackTrace();
                 failed = true;
             }
         }
@@ -223,14 +227,16 @@
 
     private static class MainThread extends Thread {
 
-        private static final int WAIT_FOR_JMX_AGENT_TIMEOUT_MS = 500;
+        private static final String EXP_TERM_MSG_REG = "Exit: ([0-9]+)";
+        private static final Pattern EXIT_PATTERN = Pattern.compile(EXP_TERM_MSG_REG);
+        private static final String COOP_EXIT = "MainThread: Cooperative Exit";
+        private static final int WAIT_FOR_JMX_AGENT_TIMEOUT_MS = 20_000;
         private final String addr;
         private final int jmxPort;
         private final int rmiPort;
         private final boolean useSSL;
-        private boolean terminated = false;
         private boolean jmxAgentStarted = false;
-        private Exception excptn;
+        private volatile Exception excptn;
 
         private MainThread(InetAddress bindAddress, int jmxPort, int rmiPort, boolean useSSL) {
             this.addr = wrapAddress(bindAddress.getHostAddress());
@@ -243,14 +249,16 @@
         public void run() {
             try {
                 waitUntilReadyForConnections();
-                // Do nothing, but wait for termination.
-                try {
-                    while (!terminated) {
-                        Thread.sleep(100);
-                    }
-                } catch (InterruptedException e) { // ignore
+
+                // Wait for termination message
+                String actualTerm = new String(System.in.readAllBytes(), StandardCharsets.UTF_8).trim();
+                System.err.println("DEBUG: MainThread: actualTerm: '" + actualTerm + "'");
+                Matcher matcher = EXIT_PATTERN.matcher(actualTerm);
+                if (matcher.matches()) {
+                    int expExitCode = Integer.parseInt(matcher.group(1));
+                    System.out.println(COOP_EXIT);
+                    System.exit(expExitCode); // The main test expects this exit value
                 }
-                System.out.println("MainThread: Thread stopped.");
             } catch (Exception e) {
                 this.excptn = e;
             }
--- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java	Thu Mar 14 09:15:51 2019 +0100
+++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java	Mon Feb 25 15:41:24 2019 +0100
@@ -24,6 +24,7 @@
 import java.io.File;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
+import java.net.UnknownHostException;
 import java.net.SocketException;
 import java.util.ArrayList;
 import java.util.List;
@@ -33,33 +34,32 @@
 import jdk.test.lib.process.ProcessTools;
 
 /**
- * NOTE:
- *    This test requires at least a setup similar to the following in
- *    /etc/hosts file (or the windows equivalent). I.e. it expects it to
- *    be multi-homed and not both being the loop-back interface.
- *    For example:
- *    ----->8-------- /etc/hosts ----------->8---
- *    127.0.0.1   localhost
- *    192.168.0.1 localhost
- *    ----->8-------- /etc/hosts ----------->8---
- *
  * @test
  * @bug     6425769
  * @summary Test JMX agent host address binding. Same ports but different
- *          interfaces to bind to (using plain sockets and SSL sockets).
+ *          interfaces to bind to (selecting plain or SSL sockets at random
+ * @key intermittent
  *
  * @library /test/lib
  * @modules java.management.rmi
  *
  * @build JMXAgentInterfaceBinding
- * @run main/timeout=5 JMXInterfaceBindingTest
+ * @run main/timeout=60 JMXInterfaceBindingTest
  */
 public class JMXInterfaceBindingTest {
 
     public static final int COMMUNICATION_ERROR_EXIT_VAL = 1;
-    public static final int STOP_PROCESS_EXIT_VAL = 143;
-    public static final int JMX_PORT = 9111;
-    public static final int RMI_PORT = 9112;
+    public static final int STOP_PROCESS_EXIT_VAL = 10;
+    public static final int JMX_PORT_RANGE_LOWER = 9100;
+    public static final int JMX_PORT_RANGE_UPPER = 9200;
+    public static final int JMX_PORT = getRandomPortInRange(JMX_PORT_RANGE_LOWER,
+                                                            JMX_PORT_RANGE_UPPER);
+    public static final int JMX_PORT_RANGE_LOWER_SSL = 9201; // 9200 might be RMI Port
+    public static final int JMX_PORT_RANGE_UPPER_SSL = 9300;
+    public static final int JMX_PORT_SSL = getRandomPortInRange(JMX_PORT_RANGE_LOWER_SSL,
+                                                                JMX_PORT_RANGE_UPPER_SSL);
+    public static final int RMI_PORT = JMX_PORT + 1;
+    public static final int RMI_PORT_SSL = JMX_PORT_SSL + 1;
     public static final String READY_MSG = "MainThread: Ready for connections";
     public static final String TEST_CLASS = JMXAgentInterfaceBinding.class.getSimpleName();
     public static final String KEYSTORE_LOC = System.getProperty("test.src", ".") +
@@ -89,8 +89,8 @@
             System.out.println();
             String msg = String.format("DEBUG: Launching java tester for triplet (HOSTNAME,JMX_PORT,RMI_PORT) == (%s,%d,%d)",
                     address,
-                    JMX_PORT,
-                    RMI_PORT);
+                    useSSL ? JMX_PORT_SSL : JMX_PORT,
+                    useSSL ? RMI_PORT_SSL : RMI_PORT);
             System.out.println(msg);
             ProcessThread jvm = runJMXBindingTest(address, useSSL);
             jvms.add(jvm);
@@ -100,9 +100,9 @@
         int failedProcesses = 0;
         for (ProcessThread pt: jvms) {
             try {
-                pt.stopProcess();
+                pt.sendMessage("Exit: " + STOP_PROCESS_EXIT_VAL);
                 pt.join();
-            } catch (InterruptedException e) {
+            } catch (Throwable e) {
                 System.err.println("Failed to stop process: " + pt.getName());
                 throw new RuntimeException("Test failed", e);
             }
@@ -131,10 +131,12 @@
         args.add("-classpath");
         args.add(TEST_CLASSPATH);
         args.add("-Dcom.sun.management.jmxremote.host=" + address);
-        args.add("-Dcom.sun.management.jmxremote.port=" + JMX_PORT);
-        args.add("-Dcom.sun.management.jmxremote.rmi.port=" + RMI_PORT);
+        args.add("-Dcom.sun.management.jmxremote.port=" + (useSSL ? JMX_PORT_SSL : JMX_PORT));
+        args.add("-Dcom.sun.management.jmxremote.rmi.port=" + (useSSL ? RMI_PORT_SSL : RMI_PORT));
         args.add("-Dcom.sun.management.jmxremote.authenticate=false");
         args.add("-Dcom.sun.management.jmxremote.ssl=" + Boolean.toString(useSSL));
+        // This is needed for testing on loopback
+        args.add("-Djava.rmi.server.hostname=" + address);
         if (useSSL) {
             args.add("-Dcom.sun.management.jmxremote.registry.ssl=true");
             args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC);
@@ -144,8 +146,8 @@
         }
         args.add(TEST_CLASS);
         args.add(address);
-        args.add(Integer.toString(JMX_PORT));
-        args.add(Integer.toString(RMI_PORT));
+        args.add(Integer.toString(useSSL ? JMX_PORT_SSL : JMX_PORT));
+        args.add(Integer.toString(useSSL ? RMI_PORT_SSL : RMI_PORT));
         args.add(Boolean.toString(useSSL));
         try {
             ProcessBuilder builder = ProcessTools.createJavaProcessBuilder(args.toArray(new String[] {}));
@@ -175,35 +177,42 @@
         }
     }
 
+    private static int getRandomPortInRange(int lower, int upper) {
+        if (upper <= lower) {
+            throw new IllegalArgumentException("upper <= lower");
+        }
+        int range = upper - lower;
+        int randPort = lower + (int)(Math.random() * range);
+        return randPort;
+    }
+
     public static void main(String[] args) {
-        List<InetAddress> addrs = getAddressesForLocalHost();
-        if (addrs.size() < 2) {
-            System.out.println("Ignoring manual test since no more than one IPs are configured for 'localhost'");
+        List<InetAddress> addrs = getNonLoopbackAddressesForLocalHost();
+        if (addrs.isEmpty()) {
+            System.out.println("Ignoring test since no non-loopback IPs are available to bind to " +
+                               "in addition to the loopback interface.");
             return;
         }
         JMXInterfaceBindingTest test = new JMXInterfaceBindingTest();
+        // Add loopback interface too as we'd like to verify whether it's
+        // possible to bind to multiple addresses on the same host. This
+        // wasn't possible prior JDK-6425769. It used to bind to *all* local
+        // interfaces. We add loopback here, since that eases test setup.
+        addrs.add(InetAddress.getLoopbackAddress());
         test.run(addrs);
         System.out.println("All tests PASSED.");
     }
 
-    private static List<InetAddress> getAddressesForLocalHost() {
-
+    private static List<InetAddress> getNonLoopbackAddressesForLocalHost() {
+        List<InetAddress> addrs = new ArrayList<>();
         try {
-            return NetworkInterface.networkInterfaces()
-                .flatMap(NetworkInterface::inetAddresses)
-                .filter(JMXInterfaceBindingTest::isNonloopbackLocalhost)
-                .collect(Collectors.toList());
-        } catch (SocketException e) {
+            InetAddress localHost = InetAddress.getLocalHost();
+            if (!localHost.isLoopbackAddress()) {
+                addrs.add(localHost);
+            }
+            return addrs;
+        } catch (UnknownHostException e) {
             throw new RuntimeException("Test failed", e);
         }
     }
-
-    // we need 'real' localhost addresses only (eg. not loopback ones)
-    // so we can bind the remote JMX connector to them
-    private static boolean isNonloopbackLocalhost(InetAddress i) {
-        if (!i.isLoopbackAddress()) {
-            return i.getHostName().toLowerCase().equals("localhost");
-        }
-        return false;
-    }
 }