8004926: sun/management/jmxremote/bootstrap/CustomLauncherTest.sh oftenly times out
authorjbachorik
Fri, 25 Oct 2013 13:01:11 +0200
changeset 21367 679b312e3c5b
parent 21366 4564a4d5d03d
child 21368 7b48f137e525
8004926: sun/management/jmxremote/bootstrap/CustomLauncherTest.sh oftenly times out Summary: Improve reliability by converting the test to Java Reviewed-by: dsamersoff, dholmes
jdk/test/TEST.groups
jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java
jdk/test/lib/testlibrary/jdk/testlibrary/StreamPumper.java
jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java
jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.sh
jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.java
jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.sh
jdk/test/sun/management/jmxremote/bootstrap/TestApplication.java
jdk/test/sun/management/jmxremote/bootstrap/TestManager.java
jdk/test/sun/management/jmxremote/bootstrap/linux-amd64/launcher
--- a/jdk/test/TEST.groups	Fri Oct 25 11:01:29 2013 +0100
+++ b/jdk/test/TEST.groups	Fri Oct 25 13:01:11 2013 +0200
@@ -325,7 +325,8 @@
   jdk/lambda/separate/Compiler.java \
   sun/management/jdp/JdpTest.sh \
   sun/management/jmxremote/bootstrap/JvmstatCountersTest.java \
-  sun/management/jmxremote/bootstrap/LocalManagementTest.sh \
+  sun/management/jmxremote/bootstrap/LocalManagementTest.java \
+  sun/management/jmxremote/bootstrap/CustomLauncherTest.java \
   sun/misc/JarIndex/metaInfFilenames/Basic.java \
   sun/misc/JarIndex/JarIndexMergeForClassLoaderTest.java \
   sun/reflect/CallerSensitive/CallerSensitiveFinder.java \
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Fri Oct 25 11:01:29 2013 +0100
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Fri Oct 25 13:01:11 2013 +0200
@@ -25,24 +25,127 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
 
 import sun.management.VMManagement;
 
 public final class ProcessTools {
+    private static final class LineForwarder extends StreamPumper.LinePump {
+        private final PrintStream ps;
+        private final String prefix;
+        LineForwarder(String prefix, PrintStream os) {
+            this.ps = os;
+            this.prefix = prefix;
+        }
+        @Override
+        protected void processLine(String line) {
+            ps.println("[" + prefix + "] " + line);
+        }
+    }
 
     private ProcessTools() {
     }
 
     /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @return Returns the initialized process
+     * @throws IOException
+     */
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder)
+    throws IOException {
+        Process p = null;
+        try {
+            p = startProcess(name, processBuilder, null, -1, TimeUnit.NANOSECONDS);
+        } catch (InterruptedException | TimeoutException e) {
+            // can't ever happen
+        }
+        return p;
+    }
+
+    /**
+     * <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
+     * </p>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @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
+     * @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 Predicate<String> linePredicate,
+                                       long timeout,
+                                       TimeUnit unit)
+    throws IOException, InterruptedException, TimeoutException {
+        Process p = processBuilder.start();
+        StreamPumper stdout = new StreamPumper(p.getInputStream());
+        StreamPumper stderr = new StreamPumper(p.getErrorStream());
+
+        stdout.addPump(new LineForwarder(name, System.out));
+        stderr.addPump(new LineForwarder(name, System.err));
+        final Phaser phs = new Phaser(1);
+        if (linePredicate != null) {
+            stdout.addPump(new StreamPumper.LinePump() {
+                @Override
+                protected void processLine(String line) {
+                    if (linePredicate.test(line)) {
+                        if (phs.getRegisteredParties() > 0) {
+                            phs.arriveAndDeregister();
+                        }
+                    }
+                }
+            });
+        }
+        Future<Void> stdoutTask = stdout.process();
+        Future<Void> stderrTask = stderr.process();
+
+        try {
+            if (timeout > -1) {
+                phs.awaitAdvanceInterruptibly(0, timeout, unit);
+            }
+        } catch (TimeoutException | InterruptedException e) {
+            stdoutTask.cancel(true);
+            stderrTask.cancel(true);
+            throw e;
+        }
+
+        return p;
+    }
+
+    /**
      * Pumps stdout and stderr from running the process into a String.
      *
-     * @param processHandler
+     * @param processBuilder
      *            ProcessHandler to run.
      * @return Output from process.
      * @throws IOException
@@ -69,22 +172,19 @@
                 stdoutBuffer);
         StreamPumper errPumper = new StreamPumper(process.getErrorStream(),
                 stderrBuffer);
-        Thread outPumperThread = new Thread(outPumper);
-        Thread errPumperThread = new Thread(errPumper);
 
-        outPumperThread.setDaemon(true);
-        errPumperThread.setDaemon(true);
-
-        outPumperThread.start();
-        errPumperThread.start();
+        Future<Void> outTask = outPumper.process();
+        Future<Void> errTask = errPumper.process();
 
         try {
             process.waitFor();
-            outPumperThread.join();
-            errPumperThread.join();
+            outTask.get();
+            errTask.get();
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             return null;
+        } catch (ExecutionException e) {
+            throw new IOException(e);
         }
 
         return new OutputBuffer(stdoutBuffer.toString(),
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/StreamPumper.java	Fri Oct 25 11:01:29 2013 +0100
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/StreamPumper.java	Fri Oct 25 13:01:11 2013 +0200
@@ -23,16 +23,65 @@
 
 package jdk.testlibrary;
 
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
 import java.io.InputStream;
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 public final class StreamPumper implements Runnable {
 
     private static final int BUF_SIZE = 256;
 
-    private final OutputStream out;
+    /**
+     * Pump will be called by the StreamPumper to process the incoming data
+     */
+    abstract public static class Pump {
+        abstract void register(StreamPumper d);
+    }
+
+    /**
+     * OutputStream -> Pump adapter
+     */
+    final public static class StreamPump extends Pump {
+        private final OutputStream out;
+        public StreamPump(OutputStream out) {
+            this.out = out;
+        }
+
+        @Override
+        void register(StreamPumper sp) {
+            sp.addOutputStream(out);
+        }
+    }
+
+    /**
+     * Used to process the incoming data line-by-line
+     */
+    abstract public static class LinePump extends Pump {
+        @Override
+        final void register(StreamPumper sp) {
+            sp.addLineProcessor(this);
+        }
+
+        abstract protected void processLine(String line);
+    }
+
     private final InputStream in;
+    private final Set<OutputStream> outStreams = new HashSet<>();
+    private final Set<LinePump> linePumps = new HashSet<>();
+
+    private final AtomicBoolean processing = new AtomicBoolean(false);
+    private final FutureTask<Void> processingTask = new FutureTask(this, null);
+
+    public StreamPumper(InputStream in) {
+        this.in = in;
+    }
 
     /**
      * Create a StreamPumper that reads from in and writes to out.
@@ -43,8 +92,8 @@
      *            The stream to write to.
      */
     public StreamPumper(InputStream in, OutputStream out) {
-        this.in = in;
-        this.out = out;
+        this(in);
+        this.addOutputStream(out);
     }
 
     /**
@@ -54,25 +103,97 @@
      */
     @Override
     public void run() {
-        int length;
-        InputStream localIn = in;
-        OutputStream localOut = out;
-        byte[] buffer = new byte[BUF_SIZE];
+        try (BufferedInputStream is = new BufferedInputStream(in)) {
+            ByteArrayOutputStream lineBos = new ByteArrayOutputStream();
+            byte[] buf = new byte[BUF_SIZE];
+            int len = 0;
+            int linelen = 0;
+
+            while ((len = is.read(buf)) > 0 && !Thread.interrupted()) {
+                for(OutputStream out : outStreams) {
+                    out.write(buf, 0, len);
+                }
+                if (!linePumps.isEmpty()) {
+                    int i = 0;
+                    int lastcrlf = -1;
+                    while (i < len) {
+                        if (buf[i] == '\n' || buf[i] == '\r') {
+                            int bufLinelen = i - lastcrlf - 1;
+                            if (bufLinelen > 0) {
+                                lineBos.write(buf, lastcrlf + 1, bufLinelen);
+                            }
+                            linelen += bufLinelen;
 
-        try {
-            while ((length = localIn.read(buffer)) > 0 && !Thread.interrupted()) {
-                localOut.write(buffer, 0, length);
+                            if (linelen > 0) {
+                                lineBos.flush();
+                                final String line = lineBos.toString();
+                                linePumps.stream().forEach((lp) -> {
+                                    lp.processLine(line);
+                                });
+                                lineBos.reset();
+                                linelen = 0;
+                            }
+                            lastcrlf = i;
+                        }
+
+                        i++;
+                    }
+                    if (lastcrlf == -1) {
+                        lineBos.write(buf, 0, len);
+                        linelen += len;
+                    } else if (lastcrlf < len - 1) {
+                        lineBos.write(buf, lastcrlf + 1, len - lastcrlf - 1);
+                        linelen += len - lastcrlf - 1;
+                    }
+                }
             }
+
         } catch (IOException e) {
-            // Just abort if something like this happens.
             e.printStackTrace();
         } finally {
+            for(OutputStream out : outStreams) {
+                try {
+                    out.flush();
+                } catch (IOException e) {}
+            }
             try {
-                localOut.flush();
                 in.close();
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
+            } catch (IOException e) {}
         }
     }
+
+    final void addOutputStream(OutputStream out) {
+        outStreams.add(out);
+    }
+
+    final void addLineProcessor(LinePump lp) {
+        linePumps.add(lp);
+    }
+
+    final public StreamPumper addPump(Pump ... pump) {
+        if (processing.get()) {
+            throw new IllegalStateException("Can not modify pumper while " +
+                                            "processing is in progress");
+        }
+        for(Pump p : pump) {
+            p.register(this);
+        }
+        return this;
+    }
+
+    final public Future<Void> process() {
+        if (!processing.compareAndSet(false, true)) {
+            throw new IllegalStateException("Can not re-run the processing");
+        }
+        Thread t = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                processingTask.run();
+            }
+        });
+        t.setDaemon(true);
+        t.start();
+
+        return processingTask;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java	Fri Oct 25 13:01:11 2013 +0200
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jdk.testlibrary.JdkFinder;
+import jdk.testlibrary.ProcessTools;
+
+/**
+ * @test
+ * @bug 6434402 8004926
+ * @library ../../../../lib/testlibrary
+ * @build TestManager TestApplication CustomLauncherTest
+ * @run main CustomLauncherTest
+ * @author Jaroslav Bachorik
+ */
+public class CustomLauncherTest {
+    private static final  String TEST_CLASSES = System.getProperty("test.classes");
+    private static final  String TEST_JDK = System.getProperty("test.jdk");
+
+    private static final  String TEST_SRC = System.getProperty("test.src");
+    private static final  String OSNAME = System.getProperty("os.name");
+    private static final  String ARCH;
+    private static final  String LIBARCH;
+
+    static {
+        // magic with os.arch
+        String osarch = System.getProperty("os.arch");
+        switch (osarch) {
+            case "i386":
+            case "i486":
+            case "i586":
+            case "i686":
+            case "i786":
+            case "i886":
+            case "i986": {
+                ARCH = "i586";
+                break;
+            }
+            case "x86_64":
+            case "amd64": {
+                ARCH = "amd64";
+                break;
+            }
+            default: {
+                ARCH = osarch;
+            }
+        }
+        LIBARCH = ARCH.equals("i586") ? "i386" : ARCH;
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (TEST_CLASSES == null || TEST_CLASSES.isEmpty()) {
+            System.out.println("Test is designed to be run from jtreg only");
+            return;
+        }
+
+        String PLATFORM = "";
+        switch (OSNAME.toLowerCase()) {
+            case "linux": {
+                PLATFORM = "linux";
+                break;
+            }
+            case "sunos": {
+                PLATFORM = "solaris";
+                break;
+            }
+            default: {
+                System.out.println("Test not designed to run on this operating " +
+                                   "system (" + OSNAME + "), skipping...");
+                return;
+            }
+        }
+
+        String LAUNCHER = TEST_SRC + File.separator + PLATFORM + "-" + ARCH +
+                          File.separator + "launcher";
+
+        final FileSystem FS = FileSystems.getDefault();
+        final boolean hasLauncher = Files.isExecutable(FS.getPath(LAUNCHER));
+        if (!hasLauncher) {
+            System.out.println("Launcher [" + LAUNCHER + "] does not exist. Skipping the test.");
+            return;
+        }
+
+        Path libjvmPath = findLibjvm(FS);
+        if (libjvmPath == null) {
+            throw new Error("Unable to locate 'libjvm.so' in " + TEST_JDK);
+        }
+
+        Process serverPrc = null, clientPrc = null;
+
+        try {
+            System.out.println("Starting custom launcher:");
+            System.out.println("=========================");
+            System.out.println("  launcher  : " + LAUNCHER);
+            System.out.println("  libjvm    : " + libjvmPath.toString());
+            System.out.println("  classpath : " + TEST_CLASSES);
+            ProcessBuilder server = new ProcessBuilder(LAUNCHER, libjvmPath.toString(), TEST_CLASSES, "TestApplication");
+
+            final AtomicReference<String> port = new AtomicReference<>();
+            final AtomicReference<String> pid = new AtomicReference<>();
+
+            serverPrc = ProcessTools.startProcess(
+                "Launcher",
+                server,
+                (String line) -> {
+                    if (line.startsWith("port:")) {
+                         port.set(line.split("\\:")[1]);
+                     } else  if (line.startsWith("pid:")) {
+                         pid.set(line.split("\\:")[1]);
+                     } else if (line.startsWith("waiting")) {
+                         return true;
+                     }
+                     return false;
+                },
+                5,
+                TimeUnit.SECONDS
+            );
+
+            System.out.println("Attaching test manager:");
+            System.out.println("=========================");
+            System.out.println("  PID           : " + pid.get());
+            System.out.println("  shutdown port : " + port.get());
+
+            ProcessBuilder client = ProcessTools.createJavaProcessBuilder(
+                "-cp",
+                TEST_CLASSES +
+                    File.pathSeparator +
+                    TEST_JDK +
+                    File.separator +
+                    "lib" +
+                    File.separator +
+                    "tools.jar",
+                "TestManager",
+                pid.get(),
+                port.get(),
+                "true"
+            );
+
+            clientPrc = ProcessTools.startProcess(
+                "TestManager",
+                client,
+                (String line) -> line.startsWith("Starting TestManager for PID"),
+                10,
+                TimeUnit.SECONDS
+            );
+
+            int clientExitCode = clientPrc.waitFor();
+            int serverExitCode = serverPrc.waitFor();
+
+            if (clientExitCode != 0 || serverExitCode != 0) {
+                throw new Error("Test failed");
+            }
+        } finally {
+            if (clientPrc != null) {
+                clientPrc.destroy();
+                clientPrc.waitFor();
+            }
+            if (serverPrc != null) {
+                serverPrc.destroy();
+                serverPrc.waitFor();
+            }
+        }
+    }
+
+    private static Path findLibjvm(FileSystem FS) {
+        Path libjvmPath = findLibjvm(FS.getPath(TEST_JDK, "jre", "lib", LIBARCH));
+        if (libjvmPath == null) {
+            libjvmPath = findLibjvm(FS.getPath(TEST_JDK, "lib", LIBARCH));
+        }
+        return libjvmPath;
+    }
+
+    private static Path findLibjvm(Path libPath) {
+        // ARCH/libjvm.so -> ARCH/server/libjvm.so -> ARCH/client/libjvm.so
+        Path libjvmPath = libPath.resolve("libjvm.so");
+        if (isFileOk(libjvmPath)) {
+            return libjvmPath;
+        }
+        libjvmPath = libPath.resolve("server/libjvm.so");
+        if (isFileOk(libjvmPath)) {
+            return libjvmPath;
+        }
+        libjvmPath = libPath.resolve("client/libjvm.so");
+        if (isFileOk(libPath)) {
+            return libjvmPath;
+        }
+
+        return null;
+    }
+
+    private static boolean isFileOk(Path path) {
+        return Files.isRegularFile(path) && Files.isReadable(path);
+    }
+}
--- a/jdk/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.sh	Fri Oct 25 11:01:29 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-
-# @test
-# @bug 6434402
-# @summary Start an application using a custom launcher and check that
-#          a management tool can connect.
-#
-# @build TestManager TestApplication
-# @run shell CustomLauncherTest.sh
-
-#
-# Check we are run from jtreg
-#
-if [ -z "${TESTCLASSES}" ]; then
-    echo "Test is designed to be run from jtreg only"
-    exit 0
-fi
-
-#
-# For now this test passes silently on Windows - this means the test only
-# has to locate libjvm.so. Also $! is not reliable on some releases of MKS.
-#{
-OS=`uname -s`
-if [ "$OS" != "Linux" -a "$OS" != "SunOS" ]; then
-    echo "Test not designed to run on this operating system, skipping..."
-    exit 0
-fi
-
-#
-# Locate the custom launcher for this platform
-#
-PLATFORM=unknown
-ARCH=unknown
-if [ "$OS" = "SunOS" ]; then
-    PLATFORM=solaris
-    case "`uname -p`" in
-	i[3-9]86)
-	    ARCH=i586
-	    ;;
-	sparc)
-	    ARCH=sparc
-	    ;;
-    esac
-else
-    PLATFORM=linux
-    case "`uname -m`" in
-	i[3-6]86)
-	    ARCH=i586
-	    ;;
-	x86_64)
-	    ARCH=amd64
-	    ;;
-    esac
-fi
-
-
-#
-# On x86 the native libraries are in lib/i386 for
-# compatability reasons
-#
-if [ "$ARCH" = "i586" ]; then
-    LIBARCH="i386"
-else
-    LIBARCH=$ARCH
-fi
-
-
-#
-# Check that a custom launcher exists for this platform
-#
-LAUNCHER="${TESTSRC}/${PLATFORM}-${ARCH}/launcher"
-if [ ! -x "${LAUNCHER}" ]; then
-    echo "${LAUNCHER} not found"
-    exit 0
-fi
-
-# 
-# Locate the libjvm.so library 
-#
-JVMLIB="${TESTJAVA}/jre/lib/${LIBARCH}/client/libjvm.so"
-if [ ! -f "${JVMLIB}" ]; then
-    JVMLIB="${TESTJAVA}/jre/lib/${LIBARCH}/server/libjvm.so"
-    if [ ! -f "${JVMLIB}" ]; then
-	JVMLIB="${TESTJAVA}/lib/${LIBARCH}/client/libjvm.so"
-	if [ ! -f "${JVMLIB}" ]; then
-	    JVMLIB="${TESTJAVA}/lib/${LIBARCH}/serevr/libjvm.so"
-	    if [ ! -f "${JVMLIB}" ]; then
-		echo "Unable to locate libjvm.so in ${TESTJAVA}"
-		exit 1
-	    fi
-	fi
-    fi
-fi
-
-#
-# Start the VM
-#
-outputfile=${TESTCLASSES}/Test.out
-rm -f ${outputfile}
-
-echo ''
-echo "Starting custom launcher..."
-echo " launcher: ${LAUNCHER}"
-echo "   libjvm: ${JVMLIB}"
-echo "classpath: ${TESTCLASSES}"
-
-
-${LAUNCHER} ${JVMLIB} ${TESTCLASSES} TestApplication > ${outputfile} &
-pid=$!
-
-# Wait for managed VM to startup (although this looks like a potentially
-# infinate loop, the framework will eventually kill it)
-echo "Waiting for TestAppication to test..."
-attempts=0
-while true; do
-    sleep 1
-    port=`tail -1 ${outputfile}`
-    if [ ! -z "$port" ]; then
-	# In case of errors wait time for output to be flushed
-	sleep 1
-	cat ${outputfile}
-	break
-    fi
-    attempts=`expr $attempts + 1`
-    echo "Waiting $attempts second(s) ..."
-done
-
-# Start the manager - this should connect to VM
-${TESTJAVA}/bin/java ${TESTVMOPTS} -classpath ${TESTCLASSES}:${TESTJAVA}/lib/tools.jar \
-  TestManager $pid $port true
-if [ $? != 0 ]; then 
-    echo "Test failed"
-    exit 1
-fi
-exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.java	Fri Oct 25 13:01:11 2013 +0200
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @test
+ * @library ../../../../lib/testlibrary
+ * @bug 5016507 6173612 6319776 6342019 6484550 8004926
+ * @summary Start a managed VM and test that a management tool can connect
+ *          without connection or username/password details.
+ *          TestManager will attempt a connection to the address obtained from
+ *          both agent properties and jvmstat buffer.
+ * @build TestManager TestApplication
+ * @run main/timeout=300 LocalManagementTest
+ */
+
+import jdk.testlibrary.ProcessTools;
+
+public class LocalManagementTest {
+    private static final  String TEST_CLASSES = System.getProperty("test.classes");
+    private static final  String TEST_JDK = System.getProperty("test.jdk");
+
+    public static void main(String[] args) throws Exception {
+        int failures = 0;
+        for(Method m : LocalManagementTest.class.getDeclaredMethods()) {
+            if (Modifier.isStatic(m.getModifiers()) &&
+                m.getName().startsWith("test")) {
+                m.setAccessible(true);
+                try {
+                    System.out.println(m.getName());
+                    System.out.println("==========");
+                    Boolean rslt = (Boolean)m.invoke(null);
+                    if (!rslt) {
+                        System.err.println(m.getName() + " failed");
+                        failures++;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    failures++;
+                }
+            }
+        }
+        if (failures > 0) {
+            throw new Error("Test failed");
+        }
+    }
+
+    private static boolean test1() throws Exception {
+        return doTest("-Dcom.sun.management.jmxremote");
+    }
+
+    private static boolean test2() throws Exception {
+        Path agentPath = findAgent();
+        if (agentPath != null) {
+            String agent = agentPath.toString();
+            return doTest("-javaagent:" + agent);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * no args (blank) - manager should attach and start agent
+     */
+    private static boolean test3() throws Exception {
+        return doTest(null);
+    }
+
+    /**
+     * sanity check arguments to management-agent.jar
+     */
+    private static boolean test4() throws Exception {
+        Path agentPath = findAgent();
+        if (agentPath != null) {
+            ProcessBuilder builder = ProcessTools.createJavaProcessBuilder(
+                "-javaagent:" + agentPath.toString() +
+                "=com.sun.management.jmxremote.port=7775," +
+                "com.sun.management.jmxremote.authenticate=false," +
+                "com.sun.management.jmxremote.ssl=false",
+                "TestApplication",
+                "-exit"
+            );
+
+            Process prc = null;
+            try {
+                prc = ProcessTools.startProcess(
+                    "TestApplication",
+                    builder
+                );
+                int exitCode = prc.waitFor();
+                return exitCode == 0;
+            } finally {
+                if (prc != null) {
+                    prc.destroy();
+                    prc.waitFor();
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * use DNS-only name service
+     */
+    private static boolean test5() throws Exception {
+        return doTest("-Dsun.net.spi.namservice.provider.1=\"dns,sun\"");
+    }
+
+    private static Path findAgent() {
+        FileSystem FS = FileSystems.getDefault();
+        Path agentPath = FS.getPath(
+            TEST_JDK, "jre", "lib", "management-agent.jar"
+        );
+        if (!isFileOk(agentPath)) {
+            agentPath = FS.getPath(
+                TEST_JDK, "lib", "management-agent.jar"
+            );
+        }
+        if (!isFileOk(agentPath)) {
+            System.err.println("Can not locate management-agent.jar");
+            return null;
+        }
+        return agentPath;
+    }
+
+    private static boolean isFileOk(Path path) {
+        return Files.isRegularFile(path) && Files.isReadable(path);
+    }
+
+    private static boolean doTest(String arg) throws Exception {
+        List<String> args = new ArrayList<>();
+        if (arg != null) {
+            args.add(arg);
+        }
+        args.add("TestApplication");
+        ProcessBuilder server = ProcessTools.createJavaProcessBuilder(
+            args.toArray(new String[args.size()])
+        );
+
+        Process serverPrc = null, clientPrc = null;
+        try {
+            final AtomicReference<String> port = new AtomicReference<>();
+            final AtomicReference<String> pid = new AtomicReference<>();
+
+            serverPrc = ProcessTools.startProcess(
+                "TestApplication",
+                server,
+                (String line) -> {
+                    if (line.startsWith("port:")) {
+                         port.set(line.split("\\:")[1]);
+                     } else  if (line.startsWith("pid:")) {
+                         pid.set(line.split("\\:")[1]);
+                     } else if (line.startsWith("waiting")) {
+                         return true;
+                     }
+                     return false;
+                },
+                5,
+                TimeUnit.SECONDS
+            );
+
+            System.out.println("Attaching test manager:");
+            System.out.println("=========================");
+            System.out.println("  PID           : " + pid.get());
+            System.out.println("  shutdown port : " + port.get());
+
+            ProcessBuilder client = ProcessTools.createJavaProcessBuilder(
+                "-cp",
+                TEST_CLASSES +
+                    File.pathSeparator +
+                    TEST_JDK +
+                    File.separator +
+                    "lib" +
+                    File.separator +
+                    "tools.jar",
+                "TestManager",
+                pid.get(),
+                port.get(),
+                "true"
+            );
+
+            clientPrc = ProcessTools.startProcess(
+                "TestManager",
+                client,
+                (String line) -> line.startsWith("Starting TestManager for PID"),
+                10,
+                TimeUnit.SECONDS
+            );
+
+            int clientExitCode = clientPrc.waitFor();
+            int serverExitCode = serverPrc.waitFor();
+            return clientExitCode == 0 && serverExitCode == 0;
+        } finally {
+            if (clientPrc != null) {
+                System.out.println("Stopping process " + clientPrc);
+                clientPrc.destroy();
+                clientPrc.waitFor();
+            }
+            if (serverPrc != null) {
+                System.out.println("Stopping process " + serverPrc);
+                serverPrc.destroy();
+                serverPrc.waitFor();
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/jdk/test/sun/management/jmxremote/bootstrap/LocalManagementTest.sh	Fri Oct 25 11:01:29 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-
-# @test
-# @bug 5016507 6173612 6319776 6342019 6484550
-# @summary Start a managed VM and test that a management tool can connect
-#          without connection or username/password details.
-#          TestManager will attempt a connection to the address obtained from 
-#          both agent properties and jvmstat buffer.
-#
-# @build TestManager TestApplication
-# @run shell/timeout=300 LocalManagementTest.sh
-
-
-doTest()
-{
-    echo ''
-
-    outputfile=${TESTCLASSES}/Test.out
-    rm -f ${outputfile}
-
-    # Start VM with given options
-    echo "+ $JAVA ${TESTVMOPTS} $1 Test"
-    $JAVA ${TESTVMOPTS} $1 TestApplication > ${outputfile}&
-    pid=$!
- 
-    # Wait for managed VM to startup
-    echo "Waiting for VM to startup..."
-    attempts=0
-    while true; do
-        sleep 1
-  	port=`tail -1 ${outputfile}`
-  	if [ ! -z "$port" ]; then
-     	    # In case of errors wait time for output to be flushed
-     	    sleep 1
-     	    cat ${outputfile}
-     	    break
-	fi
-      attempts=`expr $attempts + 1`
-      echo "Waiting $attempts second(s) ..."
-    done
-
-    # Start the manager - this should connect to VM
-    sh -xc "$JAVA ${TESTVMOPTS} -classpath ${TESTCLASSES}:${TESTJAVA}/lib/tools.jar \
-        TestManager $pid $port"  2>&1
-    if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
-}
-
-
-# Check we are run from jtreg
-if [ -z "${TESTCLASSES}" ]; then
-    echo "Test is designed to be run from jtreg only"
-    exit 0
-fi
-
-# For now this test passes silently on Windows - there are 2 reasons
-# to skip it :-
-#
-# 1. No jstat instrumentation buffers if FAT32 so need
-#    -XX:+PerfBypassFileSystemCheck 
-# 2. $! is used to get the pid of the created process but it's not
-#    reliable on older versions of MKS. Also negative pids are returned
-#    on Windows 98.
-
-os=`uname -s`
-if [ "$os" != "Linux" -a "$os" != "SunOS" ]; then
-    echo "Test not designed to run on this operating system, skipping..."
-    exit 0
-fi
-
-JAVA=${TESTJAVA}/bin/java
-CLASSPATH=${TESTCLASSES}
-export CLASSPATH
-
-failures=0
-
-# Test 1 
-doTest "-Dcom.sun.management.jmxremote" 
-
-# Test 2
-AGENT="${TESTJAVA}/jre/lib/management-agent.jar"
-if [ ! -f ${AGENT} ]; then
-  AGENT="${TESTJAVA}/lib/management-agent.jar"
-fi
-doTest "-javaagent:${AGENT}" 
-
-# Test 3 - no args (blank) - manager should attach and start agent
-doTest " " 
-
-# Test 4 - sanity check arguments to management-agent.jar
-echo ' '
-sh -xc "${JAVA} ${TESTVMOPTS} -javaagent:${AGENT}=com.sun.management.jmxremote.port=7775,\
-com.sun.management.jmxremote.authenticate=false,com.sun.management.jmxremote.ssl=false \
-  TestApplication -exit" 2>&1
-if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
-
-# Test 5 - use DNS-only name service
-doTest "-Dsun.net.spi.namservice.provider.1=\"dns,sun\"" 
-
-#
-# Results
-#
-echo ''
-if [ $failures -gt 0 ];
-  then echo "$failures test(s) failed";
-  else echo "All test(s) passed"; fi
-exit $failures
-
--- a/jdk/test/sun/management/jmxremote/bootstrap/TestApplication.java	Fri Oct 25 11:01:29 2013 +0100
+++ b/jdk/test/sun/management/jmxremote/bootstrap/TestApplication.java	Fri Oct 25 13:01:11 2013 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,14 +24,18 @@
 /*
  *
  *
- * A test "application" used by unit test LocalManagementTest.sh. This
- * application binds to some random port, prints the port number to
- * standard output, waits for somebody to connect, and then shuts down.
+ * A test "application" used by unit tests -
+ *   LocalManagementTest.java, CustomLauncherTest.java.
+ * This application binds to some random port, prints its pid and
+ * the port number to standard output, waits for somebody to connect,
+ * and then shuts down.
  */
 import java.io.IOException;
 import java.net.ServerSocket;
 import java.net.Socket;
 
+import jdk.testlibrary.ProcessTools;
+
 public class TestApplication {
     public static void main(String[] args) throws IOException {
         // Some tests require the application to exit immediately
@@ -43,8 +47,17 @@
         ServerSocket ss = new ServerSocket(0);
         int port = ss.getLocalPort();
 
-        // signal test that we are started - do not remove this line!!
-        System.out.println(port);
+        int pid = -1;
+        try {
+            pid = ProcessTools.getProcessId();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // signal test that we are started - do not remove these lines!!
+        System.out.println("port:" + port);
+        System.out.println("pid:" + pid);
+        System.out.println("waiting for the manager ...");
         System.out.flush();
 
         // wait for manager to connect
--- a/jdk/test/sun/management/jmxremote/bootstrap/TestManager.java	Fri Oct 25 11:01:29 2013 +0100
+++ b/jdk/test/sun/management/jmxremote/bootstrap/TestManager.java	Fri Oct 25 13:01:11 2013 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,8 @@
 /*
  *
  *
- * A test "management tool" used by unit test LocalManagementTest.sh.
+ * A test "management tool" used by unit tests -
+ *   LocalManagementTest.java, CustomLauncherTest.java
  *
  * Usage:    java TestManager <pid> <port>
  *
@@ -32,8 +33,6 @@
  * TCP port is used to shutdown the application.
  */
 import javax.management.MBeanServerConnection;
-import javax.management.MBeanServerInvocationHandler;
-import javax.management.ObjectName;
 import javax.management.remote.JMXServiceURL;
 import javax.management.remote.JMXConnectorFactory;
 import javax.management.remote.JMXConnector;
@@ -43,7 +42,6 @@
 import java.net.InetSocketAddress;
 import java.io.File;
 import java.io.IOException;
-import java.util.Properties;
 
 // Sun specific
 import com.sun.tools.attach.VirtualMachine;
@@ -111,6 +109,8 @@
         "com.sun.management.jmxremote.localConnectorAddress";
     public static void main(String[] args) throws Exception {
         String pid = args[0]; // pid as a string
+        System.out.println("Starting TestManager for PID = " + pid);
+        System.out.flush();
         VirtualMachine vm = VirtualMachine.attach(pid);
 
         String agentPropLocalConnectorAddress = (String)
@@ -140,7 +140,6 @@
         System.out.println("Testing the connector address from jvmstat buffer");
         connect(pid, jvmstatLocalConnectorAddress);
 
-
         // Shutdown application
         int port = Integer.parseInt(args[1]);
         System.out.println("Shutdown process via TCP port: " + port);
Binary file jdk/test/sun/management/jmxremote/bootstrap/linux-amd64/launcher has changed