8049696: com/sun/jdi/RunToExit fails with "ConnectException: Connection refused"
authorjbachorik
Tue, 10 Mar 2015 20:25:48 +0100
changeset 29496 728ed9492e63
parent 29385 b3c34101dd61
child 29497 5e00b82f08cd
8049696: com/sun/jdi/RunToExit fails with "ConnectException: Connection refused" Reviewed-by: sla
jdk/test/com/sun/jdi/RunToExit.java
jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java
--- a/jdk/test/com/sun/jdi/RunToExit.java	Fri Mar 06 04:58:53 2015 -0800
+++ b/jdk/test/com/sun/jdi/RunToExit.java	Tue Mar 10 20:25:48 2015 +0100
@@ -24,74 +24,29 @@
 /* @test
  * @bug 4997445
  * @summary Test that with server=y, when VM runs to System.exit() no error happens
- *
- * @build VMConnection RunToExit Exit0
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.* VMConnection RunToExit Exit0
  * @run driver RunToExit
  */
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.File;
-import java.io.BufferedInputStream;
 import java.net.ServerSocket;
 import com.sun.jdi.Bootstrap;
 import com.sun.jdi.VirtualMachine;
 import com.sun.jdi.event.*;
 import com.sun.jdi.connect.Connector;
 import com.sun.jdi.connect.AttachingConnector;
+import java.net.ConnectException;
 import java.util.Map;
 import java.util.List;
 import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import jdk.testlibrary.ProcessTools;
 
 public class RunToExit {
 
     /* Increment this when ERROR: seen */
-    static int error_seen = 0;
+    static volatile int error_seen = 0;
     static volatile boolean ready = false;
-    /*
-     * Helper class to direct process output to a StringBuffer
-     */
-    static class IOHandler implements Runnable {
-        private String              name;
-        private BufferedInputStream in;
-        private StringBuffer        buffer;
-
-        IOHandler(String name, InputStream in) {
-            this.name = name;
-            this.in = new BufferedInputStream(in);
-            this.buffer = new StringBuffer();
-        }
-
-        static void handle(String name, InputStream in) {
-            IOHandler handler = new IOHandler(name, in);
-            Thread thr = new Thread(handler);
-            thr.setDaemon(true);
-            thr.start();
-        }
-
-        public void run() {
-            try {
-                byte b[] = new byte[100];
-                for (;;) {
-                    int n = in.read(b, 0, 100);
-                    // The first thing that will get read is
-                    //    Listening for transport dt_socket at address: xxxxx
-                    // which shows the debuggee is ready to accept connections.
-                    ready = true;
-                    if (n < 0) {
-                        break;
-                    }
-                    buffer.append(new String(b, 0, n));
-                }
-            } catch (IOException ioe) { }
-
-            String str = buffer.toString();
-            if ( str.contains("ERROR:") ) {
-                error_seen++;
-            }
-            System.out.println(name + ": " + str);
-        }
-
-    }
 
     /*
      * Find a connector by name
@@ -111,24 +66,40 @@
     /*
      * Launch a server debuggee with the given address
      */
-    private static Process launch(String address, String class_name) throws IOException {
-        String exe =   System.getProperty("java.home")
-                     + File.separator + "bin" + File.separator + "java";
-        String cmd = exe + " " + VMConnection.getDebuggeeVMOptions() +
-            " -agentlib:jdwp=transport=dt_socket" +
-            ",server=y" + ",suspend=y" + ",address=" + address +
-            " " + class_name;
+    private static Process launch(String address, String class_name) throws Exception {
+        String args[] = new String[]{
+            "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address="
+                + address,
+            class_name
+        };
+        args = VMConnection.insertDebuggeeVMOptions(args);
+
+        ProcessBuilder launcher = ProcessTools.createJavaProcessBuilder(args);
 
-        System.out.println("Starting: " + cmd);
+        System.out.println(launcher.command().stream().collect(Collectors.joining(" ", "Starting: ", "")));
 
-        Process p = Runtime.getRuntime().exec(cmd);
-
-        IOHandler.handle("Input Stream", p.getInputStream());
-        IOHandler.handle("Error Stream", p.getErrorStream());
+        Process p = ProcessTools.startProcess(
+            class_name,
+            launcher,
+            RunToExit::checkForError,
+            RunToExit::isTransportListening,
+            0,
+            TimeUnit.NANOSECONDS
+        );
 
         return p;
     }
 
+    private static boolean isTransportListening(String line) {
+        return line.startsWith("Listening for transport dt_socket");
+    }
+
+    private static void checkForError(String line) {
+        if (line.contains("ERROR:")) {
+            error_seen++;
+        }
+    }
+
     /*
      * - pick a TCP port
      * - Launch a server debuggee: server=y,suspend=y,address=${port}
@@ -146,15 +117,6 @@
         // launch the server debuggee
         Process process = launch(address, "Exit0");
 
-        // wait for the debugge to be ready
-        while (!ready) {
-            try {
-                Thread.sleep(1000);
-            } catch(Exception ee) {
-                throw ee;
-            }
-        }
-
         // attach to server debuggee and resume it so it can exit
         AttachingConnector conn = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach");
         Map conn_args = conn.defaultArguments();
@@ -164,7 +126,16 @@
 
         System.out.println("Connection arguments: " + conn_args);
 
-        VirtualMachine vm = conn.attach(conn_args);
+        VirtualMachine vm = null;
+        while (vm == null) {
+            try {
+                vm = conn.attach(conn_args);
+            } catch (ConnectException e) {
+                e.printStackTrace(System.out);
+                System.out.println("--- Debugee not ready. Retrying in 500ms. ---");
+                Thread.sleep(500);
+            }
+        }
 
         // The first event is always a VMStartEvent, and it is always in
         // an EventSet by itself.  Wait for it.
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Fri Mar 06 04:58:53 2015 -0800
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Tue Mar 10 20:25:48 2015 +0100
@@ -88,24 +88,12 @@
                                        ProcessBuilder processBuilder,
                                        Consumer<String> consumer)
     throws IOException {
-        Process p = null;
         try {
-            p = startProcess(
-                name,
-                processBuilder,
-                line -> {
-                    if (consumer != null) {
-                        consumer.accept(line);
-                    }
-                    return false;
-                },
-                -1,
-                TimeUnit.NANOSECONDS
-            );
+            return startProcess(name, processBuilder, consumer, null, -1, TimeUnit.NANOSECONDS);
         } catch (InterruptedException | TimeoutException e) {
-            // can't ever happen
+            // will never happen
+            throw new RuntimeException(e);
         }
-        return p;
     }
 
     /**
@@ -134,6 +122,38 @@
                                        long timeout,
                                        TimeUnit unit)
     throws IOException, InterruptedException, TimeoutException {
+        return startProcess(name, processBuilder, null, linePredicate, timeout, unit);
+    }
+
+    /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * <p>
+     * It is possible to wait for the process to get to a warmed-up state
+     * via {@linkplain Predicate} condition on the STDOUT and monitor the
+     * in-streams via the provided {@linkplain Consumer}
+     * </p>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @param lineConsumer  The {@linkplain Consumer} the lines will be forwarded to
+     * @param linePredicate The {@linkplain Predicate} to use on the STDOUT
+     *                      Used to determine the moment the target app is
+     *                      properly warmed-up.
+     *                      It can be null - in that case the warmup is skipped.
+     * @param timeout The timeout for the warmup waiting; -1 = no wait; 0 = wait forever
+     * @param unit The timeout {@linkplain TimeUnit}
+     * @return Returns the initialized {@linkplain Process}
+     * @throws IOException
+     * @throws InterruptedException
+     * @throws TimeoutException
+     */
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder,
+                                       final Consumer<String> lineConsumer,
+                                       final Predicate<String> linePredicate,
+                                       long timeout,
+                                       TimeUnit unit)
+    throws IOException, InterruptedException, TimeoutException {
         System.out.println("["+name+"]:" + processBuilder.command().stream().collect(Collectors.joining(" ")));
         Process p = processBuilder.start();
         StreamPumper stdout = new StreamPumper(p.getInputStream());
@@ -141,6 +161,18 @@
 
         stdout.addPump(new LineForwarder(name, System.out));
         stderr.addPump(new LineForwarder(name, System.err));
+        if (lineConsumer != null) {
+            StreamPumper.LinePump pump = new StreamPumper.LinePump() {
+                @Override
+                protected void processLine(String line) {
+                    lineConsumer.accept(line);
+                }
+            };
+            stdout.addPump(pump);
+            stderr.addPump(pump);
+        }
+
+
         CountDownLatch latch = new CountDownLatch(1);
         if (linePredicate != null) {
             StreamPumper.LinePump pump = new StreamPumper.LinePump() {