6461635: [TESTBUG] BasicTests.sh test fails intermittently
authorsla
Tue, 03 Dec 2013 17:06:23 +0100 (2013-12-03)
changeset 21968 619035df6988
parent 21967 f09a60fb5967
child 21969 ba91ccb012e6
6461635: [TESTBUG] BasicTests.sh test fails intermittently Summary: Transform dummy class instead of BigInteger to avoid complication by -Xshare. Ported from script to java. Reviewed-by: alanb Contributed-by: mattias.tobiasson@oracle.com
jdk/test/ProblemList.txt
jdk/test/com/sun/tools/attach/AgentSetup.sh
jdk/test/com/sun/tools/attach/Application.java
jdk/test/com/sun/tools/attach/ApplicationSetup.sh
jdk/test/com/sun/tools/attach/BasicTests.java
jdk/test/com/sun/tools/attach/BasicTests.sh
jdk/test/com/sun/tools/attach/CommonSetup.sh
jdk/test/com/sun/tools/attach/PermissionTest.java
jdk/test/com/sun/tools/attach/PermissionTests.sh
jdk/test/com/sun/tools/attach/ProviderTest.java
jdk/test/com/sun/tools/attach/ProviderTests.sh
jdk/test/com/sun/tools/attach/RedefineAgent.java
jdk/test/com/sun/tools/attach/RedefineDummy.java
jdk/test/com/sun/tools/attach/RunnerUtil.java
jdk/test/lib/testlibrary/jdk/testlibrary/ProcessThread.java
jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java
jdk/test/lib/testlibrary/jdk/testlibrary/Utils.java
jdk/test/sun/tools/jstatd/JstatdTest.java
--- a/jdk/test/ProblemList.txt	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/ProblemList.txt	Tue Dec 03 17:06:23 2013 +0100
@@ -266,9 +266,6 @@
 
 # jdk_tools
 
-# 6461635
-com/sun/tools/attach/BasicTests.sh                              generic-all
-
 # 7132203
 sun/jvmstat/monitor/MonitoredVm/CR6672135.java                  generic-all
 
--- a/jdk/test/com/sun/tools/attach/AgentSetup.sh	Tue Dec 03 23:09:17 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2005, 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.
-#
-
-
-#
-#
-# Agent set - creates Agent.jar, BadAgent.jar and RedefineAgent.jar in ${TESTCLASSES}
- 
-$JAVAC -d "${TESTCLASSES}" "${TESTSRC}"/Agent.java "${TESTSRC}"/BadAgent.java "${TESTSRC}"/RedefineAgent.java
-
-$JAR -cfm "${TESTCLASSES}"/Agent.jar "${TESTSRC}"/agent.mf \
-  -C "${TESTCLASSES}" Agent.class
-
-$JAR -cfm "${TESTCLASSES}"/BadAgent.jar "${TESTSRC}"/badagent.mf \
-  -C "${TESTCLASSES}" BadAgent.class
-
-$JAR -cfm "${TESTCLASSES}"/RedefineAgent.jar "${TESTSRC}"/redefineagent.mf \
-  -C "${TESTCLASSES}" RedefineAgent.class
-
-agent="${TESTCLASSES}${FS}Agent.jar"
-badagent="${TESTCLASSES}${FS}BadAgent.jar"
-redefineagent="${TESTCLASSES}${FS}RedefineAgent.jar"
-
--- a/jdk/test/com/sun/tools/attach/Application.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/com/sun/tools/attach/Application.java	Tue Dec 03 17:06:23 2013 +0100
@@ -22,25 +22,39 @@
  */
 
 /*
- *
- *
  * A simple "Application" used by the Attach API unit tests. This application is
  * launched by the test. It binds to a random port and shuts down when somebody
  * connects to that port.
+ * Used port and pid are written both to stdout and to a specified file.
  */
 import java.net.Socket;
 import java.net.ServerSocket;
+import java.io.PrintWriter;
+import jdk.testlibrary.ProcessTools;
 
 public class Application {
     public static void main(String args[]) throws Exception {
         // bind to a random port
+        if (args.length < 1) {
+            System.err.println("First argument should be path to output file.");
+        }
+        String outFileName = args[0];
+
         ServerSocket ss = new ServerSocket(0);
         int port = ss.getLocalPort();
+        int pid = ProcessTools.getProcessId();
 
-        // signal test that we are started - do not remove this line!!
-        System.out.println(port);
+        System.out.println("shutdownPort=" + port);
+        System.out.println("pid=" + pid);
         System.out.flush();
 
+        try (PrintWriter writer = new PrintWriter(outFileName)) {
+            writer.println("shutdownPort=" + port);
+            writer.println("pid=" + pid);
+            writer.println("done");
+            writer.flush();
+        }
+
         // wait for test harness to connect
         Socket s = ss.accept();
         s.close();
--- a/jdk/test/com/sun/tools/attach/ApplicationSetup.sh	Tue Dec 03 23:09:17 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2005, 2011, 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.
-#
-
-
-#
-#
-# Application Setup - creates ${TESTCLASSES}/Application.jar and the following
-# procedures:
-#	startApplication - starts target application
-#	stopApplication $1 - stops application via TCP shutdown port $1
-
-$JAVAC -d "${TESTCLASSES}" "${TESTSRC}"/Application.java "${TESTSRC}"/Shutdown.java
-$JAR -cfm "${TESTCLASSES}"/Application.jar "${TESTSRC}"/application.mf \
-  -C "${TESTCLASSES}" Application.class
-
-OUTPUTFILE=${TESTCLASSES}/Application.out
-rm -f ${OUTPUTFILE}
-
-startApplication() 
-{
-  # put all output from the app into ${OUTPUTFILE}
-  ${JAVA} ${TESTVMOPTS} $1 $2 $3 -jar "${TESTCLASSES}"/Application.jar > ${OUTPUTFILE} 2>&1 &
-  pid="$!"
-
-  # MKS creates an intermediate shell to launch ${JAVA} so
-  # ${pid} is not the actual pid. We have put in a small sleep
-  # to give the intermediate shell process time to launch the
-  # "java" process.
-  if [ "$OS" = "Windows" ]; then
-    sleep 2
-    if [ "${isCygwin}" = "true" ] ; then
-      realpid=`ps -p ${pid} | tail -1 | awk '{print $4;}'`
-    else
-      realpid=`ps -o pid,ppid,comm|grep ${pid}|grep "java"|cut -c1-6`
-    fi
-    pid=${realpid}
-  fi
-                                                                                                                  
-  echo "Waiting for Application to initialize..."
-  attempts=0
-  while true; do
-    sleep 1
-    port=`tail -1 ${OUTPUTFILE} | sed -e 's@\\r@@g' `
-    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
-  echo "Application is process $pid, shutdown port is $port"
-  return $port
-}
-
-stopApplication() 
-{
-  $JAVA ${TESTVMOPTS} -classpath "${TESTCLASSES}" Shutdown $1
-}
-
--- a/jdk/test/com/sun/tools/attach/BasicTests.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/com/sun/tools/attach/BasicTests.java	Tue Dec 03 17:06:23 2013 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2011, 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
@@ -21,160 +21,257 @@
  * questions.
  */
 
-/*
- *
- *
- * Unit test for Attach API. Attaches to the given VM and performs a number
- * unit tests.
- */
 import com.sun.tools.attach.*;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.io.IOException;
 import java.util.Properties;
 import java.util.List;
+import java.io.File;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.JDKToolLauncher;
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.ProcessThread;
 
+/*
+ * @test
+ * @bug 6173612 6273707 6277253 6335921 6348630 6342019 6381757
+ * @summary Basic unit tests for the VM attach mechanism.
+ * @library /lib/testlibrary
+ * @run build Agent BadAgent RedefineAgent Application Shutdown RedefineDummy
+ * @run main BasicTests
+ *
+ * This test will perform a number of basic attach tests.
+ */
 public class BasicTests {
-    public static void main(String args[]) throws Exception {
-        String pid = args[0];
-        String agent = args[1];
-        String badagent = args[2];
-        String redefineagent = args[3];
 
-        System.out.println(" - Attaching to application ...");
-        VirtualMachine vm = VirtualMachine.attach(pid);
+    /*
+     * The actual test is in the nested class TestMain.
+     * The responsibility of this class is to:
+     * 1. Build all needed jars.
+     * 2. Start the Application class in a separate process.
+     * 3. Find the pid and shutdown port of the running Application.
+     * 4. Launches the tests in nested class TestMain that will attach to the Application.
+     * 5. Shut down the Application.
+     */
+    public static void main(String args[]) throws Throwable {
+        final String pidFile = "TestsBasic.Application.pid";
+        ProcessThread processThread = null;
+        RunnerUtil.ProcessInfo info = null;
+        try {
+            buildJars();
+            processThread = RunnerUtil.startApplication(pidFile);
+            info = RunnerUtil.readProcessInfo(pidFile);
+            runTests(info.pid);
+        } catch (Throwable t) {
+            System.out.println("TestBasic got unexpected exception: " + t);
+            t.printStackTrace();
+            throw t;
+        } finally {
+            // Make sure the Application process is stopped.
+            RunnerUtil.stopApplication(info.shutdownPort, processThread);
+        }
+    }
 
-        // Test 1 - read the system properties from the target VM and
-        // check that property is set
-        System.out.println(" - Test: system properties in target VM");
-        Properties props = vm.getSystemProperties();
-        String value = props.getProperty("attach.test");
-        if (value == null || !value.equals("true")) {
-            throw new RuntimeException("attach.test property not set");
-        }
-        System.out.println(" - attach.test property set as expected");
+    /**
+     * Runs the actual tests in nested class TestMain.
+     * The reason for running the tests in a separate process
+     * is that we need to modify the class path.
+     */
+    private static void runTests(int pid) throws Throwable {
+        final String sep = File.separator;
 
-        // Test 1a - read the agent properties from the target VM.
-        // By default, the agent property contains "sun.java.command",
-        // "sun.jvm.flags", and "sun.jvm.args".
-        // Just sanity check - make sure not empty.
-        System.out.println(" - Test: agent properties in target VM");
-        props = vm.getAgentProperties();
-        if (props == null || props.size() == 0) {
-            throw new RuntimeException("Agent properties is empty");
-        }
-        System.out.println(" - agent properties non-empty as expected");
+        // Need to add jdk/lib/tools.jar to classpath.
+        String classpath =
+            System.getProperty("test.class.path", "") + File.pathSeparator +
+            System.getProperty("test.jdk", ".") + sep + "lib" + sep + "tools.jar";
+        String testClassDir = System.getProperty("test.classes", "") + sep;
+
+        // Argumenta : -classpath cp BasicTests$TestMain pid agent badagent redefineagent
+        String[] args = {
+            "-classpath",
+            classpath,
+            "BasicTests$TestMain",
+            Integer.toString(pid),
+            testClassDir + "Agent.jar",
+            testClassDir + "BadAgent.jar",
+            testClassDir + "RedefineAgent.jar" };
+        OutputAnalyzer output = ProcessTools.executeTestJvm(args);
+        output.shouldHaveExitValue(0);
+    }
 
-        // Test 2 - attempt to load an agent that does not exist
-        System.out.println(" - Test: Load an agent that does not exist");
-        try {
-            vm.loadAgent("SilverBullet.jar");
-        } catch (AgentLoadException x) {
-            System.out.println(" - AgentLoadException thrown as expected!");
+    /**
+     * Will build all jars needed by the tests.
+     */
+    private static void buildJars() throws Throwable {
+        String[] jars = {"Agent", "BadAgent", "RedefineAgent", "Application" };
+        for (String jar : jars) {
+            buildJar(jar);
         }
+    }
 
-        // Test 3 - load an "bad" agent (agentmain throws an exception)
-        System.out.println(" - Test: Load a bad agent");
-        System.out.println("INFO: This test will cause error messages "
-            + "to appear in the application log about SilverBullet.jar "
-            + "not being found and an agent failing to start.");
-        try {
-            vm.loadAgent(badagent);
-            throw new RuntimeException(
-                "AgentInitializationException not thrown as expected!");
-        } catch (AgentInitializationException x) {
-            System.out.println(
-                " - AgentInitializationException thrown as expected!");
-        }
+    /**
+     * Will build a jar with the given name.
+     * Class file and manifest must already exist.
+     * @param jarName Name of the jar.
+     */
+    private static void buildJar(String jarName) throws Throwable {
+        String testClasses = System.getProperty("test.classes", "?");
+        String testSrc = System.getProperty("test.src", "?");
+        String jar = String.format("%s/%s.jar", testClasses, jarName);
+        String manifest = String.format("%s/%s.mf", testSrc, jarName.toLowerCase());
+        String clazz = String.format("%s.class", jarName);
+
+        // Arguments to the jar command has this format:
+        // "-cfm TESTCLASSES/Agent.jar TESTSRC/agent.mf -C TESTCLASSES Agent.class"
+        RunnerUtil.createJar("-cfm", jar, manifest, "-C", testClasses, clazz);
+    }
+
+    /**
+     * This is the actual test. It will attach to the running Application
+     * and perform a number of basic attach tests.
+     */
+    public static class TestMain {
+        public static void main(String args[]) throws Exception {
+            String pid = args[0];
+            String agent = args[1];
+            String badagent = args[2];
+            String redefineagent = args[3];
 
-        // Test 4 - detach from the VM and attempt a load (should throw IOE)
-        System.out.println(" - Test: Detach from VM");
-        System.out.println("INFO: This test will cause error messages "
-            + "to appear in the application log about a BadAgent including "
-            + "a RuntimeException and an InvocationTargetException.");
-        vm.detach();
-        try {
-            vm.loadAgent(agent);
-            throw new RuntimeException("loadAgent did not throw an exception!!");
-        } catch (IOException ioe) {
-            System.out.println(" - IOException as expected");
-        }
+            System.out.println(" - Attaching to application ...");
+            VirtualMachine vm = VirtualMachine.attach(pid);
+
+            // Test 1 - read the system properties from the target VM and
+            // check that property is set
+            System.out.println(" - Test: system properties in target VM");
+            Properties props = vm.getSystemProperties();
+            String value = props.getProperty("attach.test");
+            if (value == null || !value.equals("true")) {
+                throw new RuntimeException("attach.test property not set");
+            }
+            System.out.println(" - attach.test property set as expected");
 
-        // Test 5 - functional "end-to-end" test.
-        // Create a listener socket. Load Agent.jar into the target VM passing
-        // it the port number of our listener. When agent loads it should connect
-        // back to the tool.
-
-        System.out.println(" - Re-attaching to application ...");
-        vm = VirtualMachine.attach(pid);
+            // Test 1a - read the agent properties from the target VM.
+            // By default, the agent property contains "sun.java.command",
+            // "sun.jvm.flags", and "sun.jvm.args".
+            // Just sanity check - make sure not empty.
+            System.out.println(" - Test: agent properties in target VM");
+            props = vm.getAgentProperties();
+            if (props == null || props.size() == 0) {
+                throw new RuntimeException("Agent properties is empty");
+            }
+            System.out.println(" - agent properties non-empty as expected");
 
-        System.out.println(" - Test: End-to-end connection with agent");
+            // Test 2 - attempt to load an agent that does not exist
+            System.out.println(" - Test: Load an agent that does not exist");
+            try {
+                vm.loadAgent("SilverBullet.jar");
+            } catch (AgentLoadException x) {
+                System.out.println(" - AgentLoadException thrown as expected!");
+            }
 
-        ServerSocket ss = new ServerSocket(0);
-        int port = ss.getLocalPort();
-
-        System.out.println(" - Loading Agent.jar into target VM ...");
-        vm.loadAgent(agent, Integer.toString(port));
+            // Test 3 - load an "bad" agent (agentmain throws an exception)
+            System.out.println(" - Test: Load a bad agent");
+            System.out.println("INFO: This test will cause error messages "
+                + "to appear in the application log about SilverBullet.jar "
+                + "not being found and an agent failing to start.");
+            try {
+                vm.loadAgent(badagent);
+                throw new RuntimeException(
+                    "AgentInitializationException not thrown as expected!");
+            } catch (AgentInitializationException x) {
+                System.out.println(
+                    " - AgentInitializationException thrown as expected!");
+            }
 
-        System.out.println(" - Waiting for agent to connect back to tool ...");
-        Socket s = ss.accept();
-        System.out.println(" - Connected to agent.");
-
-        // Test 5b - functional "end-to-end" test.
-        // Now with an agent that does redefine.
+            // Test 4 - detach from the VM and attempt a load (should throw IOE)
+            System.out.println(" - Test: Detach from VM");
+            System.out.println("INFO: This test will cause error messages "
+                + "to appear in the application log about a BadAgent including "
+                + "a RuntimeException and an InvocationTargetException.");
+            vm.detach();
+            try {
+                vm.loadAgent(agent);
+                throw new RuntimeException("loadAgent did not throw an exception!!");
+            } catch (IOException ioe) {
+                System.out.println(" - IOException as expected");
+            }
 
-        System.out.println(" - Re-attaching to application ...");
-        vm = VirtualMachine.attach(pid);
+            // Test 5 - functional "end-to-end" test.
+            // Create a listener socket. Load Agent.jar into the target VM passing
+            // it the port number of our listener. When agent loads it should connect
+            // back to the tool.
+
+            System.out.println(" - Re-attaching to application ...");
+            vm = VirtualMachine.attach(pid);
+
+            System.out.println(" - Test: End-to-end connection with agent");
 
-        System.out.println(" - Test: End-to-end connection with RedefineAgent");
+            ServerSocket ss = new ServerSocket(0);
+            int port = ss.getLocalPort();
 
-        ServerSocket ss2 = new ServerSocket(0);
-        int port2 = ss2.getLocalPort();
+            System.out.println(" - Loading Agent.jar into target VM ...");
+            vm.loadAgent(agent, Integer.toString(port));
+
+            System.out.println(" - Waiting for agent to connect back to tool ...");
+            Socket s = ss.accept();
+            System.out.println(" - Connected to agent.");
 
-        System.out.println(" - Loading RedefineAgent.jar into target VM ...");
-        vm.loadAgent(redefineagent, Integer.toString(port2));
+            // Test 5b - functional "end-to-end" test.
+            // Now with an agent that does redefine.
+
+            System.out.println(" - Re-attaching to application ...");
+            vm = VirtualMachine.attach(pid);
 
-        System.out.println(" - Waiting for RedefineAgent to connect back to tool ...");
-        Socket s2 = ss2.accept();
-        System.out.println(" - Connected to RedefineAgent.");
+            System.out.println(" - Test: End-to-end connection with RedefineAgent");
+
+            ServerSocket ss2 = new ServerSocket(0);
+            int port2 = ss2.getLocalPort();
+
+            System.out.println(" - Loading RedefineAgent.jar into target VM ...");
+            vm.loadAgent(redefineagent, Integer.toString(port2));
 
-        // Test 6 - list method should list the target VM
-        System.out.println(" - Test: VirtualMachine.list");
-        List<VirtualMachineDescriptor> l = VirtualMachine.list();
-        if (!l.isEmpty()) {
-            boolean found = false;
-            for (VirtualMachineDescriptor vmd: l) {
-                if (vmd.id().equals(pid)) {
-                    found = true;
-                    break;
+            System.out.println(" - Waiting for RedefineAgent to connect back to tool ...");
+            Socket s2 = ss2.accept();
+            System.out.println(" - Connected to RedefineAgent.");
+
+            // Test 6 - list method should list the target VM
+            System.out.println(" - Test: VirtualMachine.list");
+            List<VirtualMachineDescriptor> l = VirtualMachine.list();
+            if (!l.isEmpty()) {
+                boolean found = false;
+                for (VirtualMachineDescriptor vmd: l) {
+                    if (vmd.id().equals(pid)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (found) {
+                    System.out.println(" - " + pid + " found.");
+                } else {
+                    throw new RuntimeException(pid + " not found in VM list");
                 }
             }
-            if (found) {
-                System.out.println(" - " + pid + " found.");
-            } else {
-                throw new RuntimeException(pid + " not found in VM list");
-            }
-        }
 
-        // test 7 - basic hashCode/equals tests
-        System.out.println(" - Test: hashCode/equals");
+            // test 7 - basic hashCode/equals tests
+            System.out.println(" - Test: hashCode/equals");
 
-        VirtualMachine vm1 = VirtualMachine.attach(pid);
-        VirtualMachine vm2 = VirtualMachine.attach(pid);
-        if (!vm1.equals(vm2)) {
-            throw new RuntimeException("virtual machines are not equal");
-        }
-        if (vm.hashCode() != vm.hashCode()) {
-            throw new RuntimeException("virtual machine hashCodes not equal");
+            VirtualMachine vm1 = VirtualMachine.attach(pid);
+            VirtualMachine vm2 = VirtualMachine.attach(pid);
+            if (!vm1.equals(vm2)) {
+                throw new RuntimeException("virtual machines are not equal");
+            }
+            if (vm.hashCode() != vm.hashCode()) {
+                throw new RuntimeException("virtual machine hashCodes not equal");
+            }
+            System.out.println(" - hashCode/equals okay");
+
+            // ---
+            System.out.println(" - Cleaning up...");
+            s.close();
+            ss.close();
+            s2.close();
+            ss2.close();
         }
-        System.out.println(" - hashCode/equals okay");
-
-
-        // ---
-        System.out.println(" - Cleaning up...");
-        s.close();
-        ss.close();
-        s2.close();
-        ss2.close();
     }
 }
--- a/jdk/test/com/sun/tools/attach/BasicTests.sh	Tue Dec 03 23:09:17 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2005, 2011, 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 6173612 6273707 6277253 6335921 6348630 6342019 6381757
-# @summary Basic unit tests for the VM attach mechanism.
-#
-# @build BasicTests
-# @run shell BasicTests.sh
-
-if [ "${TESTSRC}" = "" ]
-then
-  echo "TESTSRC not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-# Windows 2000 is a problem here, so we skip it, see 6962615
-osrev=`uname -a`
-if [ "`echo ${osrev} | grep 'CYGWIN[^ ]*-5\.0'`" != "" ] ; then
-  echo "Treating as a pass, not testing Windows 2000"
-  exit 0
-fi
-if [ "`echo ${osrev} | grep 'Windows'`" != "" ] ; then
-  if [ "`echo ${osrev} | grep '5 00'`" != "" ] ; then
-     echo "Treating as a pass, not testing Windows 2000"
-     exit 0
-  fi
-fi
-
-. ${TESTSRC}/CommonSetup.sh
-. ${TESTSRC}/ApplicationSetup.sh
-. ${TESTSRC}/AgentSetup.sh
-
-startApplication -Dattach.test=true
-# pid = process-id, port = shutdown port
-
-failures=0
-
-echo "Running tests ..."
-
-$JAVA ${TESTVMOPTS} -classpath "${TESTCLASSES}${PS}${TESTJAVA}/lib/tools.jar" \
-  BasicTests $pid $agent $badagent $redefineagent 2>&1
-if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
-
-stopApplication $port
-
-# Add these info messages to $OUTPUTFILE just in case someone
-# looks at it and wonders about the failures. We have to do
-# this after the application is stopped because it is writing
-# to $OUTPUTFILE.
-(
-echo ""
-echo "INFO: Test 2 will cause error messages about SilverBullet.jar" \
-    "and an agent failing to start."
-echo "INFO: Test 3 will cause error messages about BadAgent" \
-    "including a RuntimeException and an InvocationTargetException."
-) >> ${OUTPUTFILE}
-
-if [ $failures = 0 ]; 
-  then echo "All tests passed.";
-  else echo "$failures test(s) failed:"; cat ${OUTPUTFILE};
-fi
-exit $failures
--- a/jdk/test/com/sun/tools/attach/CommonSetup.sh	Tue Dec 03 23:09:17 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2005, 2012, 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.
-#
-
-
-#
-#
-# Common setup for the Attach API unit tests. Setups up the following variables:
-#
-# PS - path sep.
-# FS - file sep.
-# JAVA - java cmd.
-# JAVAC - javac cmd.
-# JAR - jar cmd.
-
-OS=`uname -s`
-case "$OS" in
-  SunOS | Linux | Darwin )
-    PS=":"
-    FS="/"
-    ;;
-  Windows* )
-    PS=";"
-    OS="Windows"
-    FS="\\"
-    ;;
-  CYGWIN* )
-    PS=";"
-    OS="Windows"
-    FS="\\"
-    isCygwin=true
-    ;;
-  * )
-    echo "Unrecognized system!"
-    exit 1;
-    ;;
-esac
-
-if [ "${TESTJAVA}" = "" ]
-then
-  echo "TESTJAVA not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-if [ "${TESTSRC}" = "" ]
-then
-  echo "TESTSRC not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-if [ "${TESTCLASSES}" = "" ]
-then
-  echo "TESTCLASSES not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-JAVA="${TESTJAVA}/bin/java"
-JAVAC="${TESTJAVA}/bin/javac"
-JAR="${TESTJAVA}/bin/jar"
-
--- a/jdk/test/com/sun/tools/attach/PermissionTest.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/com/sun/tools/attach/PermissionTest.java	Tue Dec 03 17:06:23 2013 +0100
@@ -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
@@ -21,39 +21,119 @@
  * questions.
  */
 
-/*
- *
- *
- * Unit test for Attach API - this checks that a SecurityException is thrown as
- * expected.
- */
 import com.sun.tools.attach.VirtualMachine;
 import com.sun.tools.attach.AttachNotSupportedException;
 import java.util.Properties;
+import java.io.File;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.ProcessThread;
 
+/*
+ * @test
+ * @bug 6173612 6273707 6277253 6335921 6348630 6342019 6381757
+ * @summary Basic unit tests for the VM attach mechanism.
+ * @library /lib/testlibrary
+ * @run build Application Shutdown
+ * @run main PermissionTest
+ *
+ * Unit test for Attach API -
+ * this checks that a SecurityException is thrown as expected.
+ */
 public class PermissionTest {
-    public static void main(String args[]) throws Exception {
-        SecurityManager sm = System.getSecurityManager();
-        if (sm == null) {
-            throw new RuntimeException("Test configuration error - no security manager set");
-        }
 
-        String pid = args[0];
-        boolean shouldFail = Boolean.parseBoolean(args[1]);
-
+    /*
+     * The actual test is in the nested class TestMain.
+     * The responsibility of this class is to:
+     * 1. Start the Application class in a separate process.
+     * 2. Find the pid and shutdown port of the running Application.
+     * 3. Run the tests in TstMain that will attach to the Application.
+     * 4. Shut down the Application.
+     */
+    public static void main(String args[]) throws Throwable {
+        final String pidFile ="TestPermission.Application.pid";
+        ProcessThread processThread = null;
+        RunnerUtil.ProcessInfo info = null;
         try {
-            VirtualMachine.attach(pid).detach();
-            if (shouldFail) {
-                throw new RuntimeException("SecurityException should be thrown");
+            processThread = RunnerUtil.startApplication(pidFile);
+            info = RunnerUtil.readProcessInfo(pidFile);
+            runTests(info.pid);
+        } catch (Throwable t) {
+            System.out.println("TestPermission got unexpected exception: " + t);
+            t.printStackTrace();
+            throw t;
+        } finally {
+            // Make sure the Application process is stopped.
+            RunnerUtil.stopApplication(info.shutdownPort, processThread);
+        }
+    }
+
+    /**
+     * Runs the actual test the nested class TestMain.
+     * The test is run in a separate process because we need to add to the classpath.
+     */
+    private static void runTests(int pid) throws Throwable {
+        final String sep = File.separator;
+
+        // Need to add jdk/lib/tools.jar to classpath.
+        String classpath =
+            System.getProperty("test.class.path", "") + File.pathSeparator +
+            System.getProperty("test.jdk", ".") + sep + "lib" + sep + "tools.jar";
+        String testSrc = System.getProperty("test.src", "") + sep;
+
+        // Use a policy that will NOT allow attach. Test will verify exception.
+        String[] args = {
+            "-classpath",
+            classpath,
+            "-Djava.security.manager",
+            String.format("-Djava.security.policy=%sjava.policy.deny", testSrc),
+            "PermissionTest$TestMain",
+            Integer.toString(pid),
+            "true" };
+        OutputAnalyzer output = ProcessTools.executeTestJvm(args);
+        output.shouldHaveExitValue(0);
+
+        // Use a policy that will allow attach.
+        args = new String[] {
+            "-classpath",
+            classpath,
+            "-Djava.security.manager",
+            String.format("-Djava.security.policy=%sjava.policy.allow", testSrc),
+            "PermissionTest$TestMain",
+            Integer.toString(pid),
+            "false" };
+        output = ProcessTools.executeTestJvm(args);
+        output.shouldHaveExitValue(0);
+    }
+
+    /**
+     * This is the actual test code. It will attach to the Application and verify
+     * that we get a SecurityException when that is expected.
+     */
+    public static class TestMain {
+        public static void main(String args[]) throws Exception {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm == null) {
+                throw new RuntimeException("Test configuration error - no security manager set");
             }
-            System.out.println(" - attached to target VM as expected.");
-        } catch (Exception x) {
-            // AttachNotSupportedException thrown when no providers can be loaded
-            if (shouldFail && ((x instanceof AttachNotSupportedException) ||
-                (x instanceof SecurityException))) {
-                System.out.println(" - exception thrown as expected.");
-            } else {
-                throw x;
+
+            String pid = args[0];
+            boolean shouldFail = Boolean.parseBoolean(args[1]);
+
+            try {
+                VirtualMachine.attach(pid).detach();
+                if (shouldFail) {
+                    throw new RuntimeException("SecurityException should be thrown");
+                }
+                System.out.println(" - attached to target VM as expected.");
+            } catch (Exception x) {
+                // AttachNotSupportedException thrown when no providers can be loaded
+                if (shouldFail && ((x instanceof AttachNotSupportedException) ||
+                    (x instanceof SecurityException))) {
+                    System.out.println(" - exception thrown as expected.");
+                } else {
+                    throw x;
+                }
             }
         }
     }
--- a/jdk/test/com/sun/tools/attach/PermissionTests.sh	Tue Dec 03 23:09:17 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright (c) 2005, 2010, 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 6173612
-# @summary Security manager and permission tests for Attach API
-#
-# @build PermissionTest 
-# @run shell PermissionTests.sh
-
-if [ "${TESTSRC}" = "" ]
-then
-  echo "TESTSRC not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-. ${TESTSRC}/CommonSetup.sh
-. ${TESTSRC}/ApplicationSetup.sh
-
-failures=0
-
-# Start target VM
-startApplication
-# pid = process-id, port = shutdown port
-
-echo "Deny test"
-# deny 
-$JAVA ${TESTVMOPTS} -classpath "${TESTCLASSES}${PS}${TESTJAVA}/lib/tools.jar" \
-    -Djava.security.manager \
-    -Djava.security.policy=${TESTSRC}/java.policy.deny \
-    PermissionTest $pid true 2>&1
-if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
-
-# allow
-echo "Allow test"
-$JAVA ${TESTVMOPTS} -classpath "${TESTCLASSES}${PS}${TESTJAVA}/lib/tools.jar" \
-    -Djava.security.manager \
-    -Djava.security.policy=${TESTSRC}/java.policy.allow \
-    PermissionTest $pid false 2>&1 
-if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
-
-# Stop target VM
-stopApplication $port
-
-if [ $failures = 0 ]; 
-  then echo "All tests passed.";
-  else echo "$failures test(s) failed:"; cat ${OUTPUTFILE};
-fi
-exit $failures
--- a/jdk/test/com/sun/tools/attach/ProviderTest.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/com/sun/tools/attach/ProviderTest.java	Tue Dec 03 17:06:23 2013 +0100
@@ -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
@@ -21,24 +21,98 @@
  * questions.
  */
 
-/*
- *
- *
- * Unit test for Attach API. Attaches to the given VM and performs a number
- * unit tests.
- */
+import java.io.File;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.JDKToolLauncher;
+import jdk.testlibrary.ProcessTools;
 import com.sun.tools.attach.VirtualMachine;
 import com.sun.tools.attach.spi.AttachProvider;
 
+/*
+ * @test
+ * @bug 6173612 6273707 6277253 6335921 6348630 6342019 6381757
+ * @summary Basic unit tests for the VM attach mechanism.
+ * @library /lib/testlibrary
+ * @run build SimpleProvider
+ * @run main ProviderTest
+ *
+ * The test will attach and detach to/from the running Application.
+ */
 public class ProviderTest {
-    public static void main(String args[]) throws Exception {
-        // deal with internal builds where classes are loaded from the
-        // 'classes' directory rather than rt.jar
-        ClassLoader cl = AttachProvider.class.getClassLoader();
-        if (cl != ClassLoader.getSystemClassLoader()) {
-            System.out.println("Attach API not loaded by system class loader - test skipped");
-            return;
+
+    /*
+     * The actual tests are in the nested class TestMain below.
+     * The responsibility of this class is to:
+     * 1. Build the needed jar.
+     * 2. Run tests in ProviderTest.TestMain.
+     */
+    public static void main(String args[]) throws Throwable {
+        try {
+            buildJar();
+            runTests();
+        } catch (Throwable t) {
+            System.out.println("TestProvider got unexpected exception: " + t);
+            t.printStackTrace();
+            throw t;
         }
-        VirtualMachine.attach("simple:1234").detach();
+    }
+
+    /**
+     * Runs the actual tests in the nested class TestMain.
+     * We need to run the tests in a separate process,
+     * because we need to add to the classpath.
+     */
+    private static void runTests() throws Throwable {
+        final String sep = File.separator;
+        String testClassPath = System.getProperty("test.class.path", "");
+        String testClasses = System.getProperty("test.classes", "") + sep;
+        String jdkLib = System.getProperty("test.jdk", ".") + sep + "lib" + sep;
+
+        // Need to add SimpleProvider.jar and tools.jar to classpath.
+        String classpath =
+                testClassPath + File.pathSeparator +
+                testClasses + "SimpleProvider.jar" + File.pathSeparator +
+                jdkLib + "tools.jar";
+
+        String[] args = {
+                "-classpath",
+                classpath,
+                "ProviderTest$TestMain" };
+        OutputAnalyzer output = ProcessTools.executeTestJvm(args);
+        output.shouldHaveExitValue(0);
+    }
+
+    /**
+     * Will build the SimpleProvider.jar.
+     */
+    private static void buildJar() throws Throwable {
+        final String sep = File.separator;
+        String testClasses = System.getProperty("test.classes", "?") + sep;
+        String testSrc = System.getProperty("test.src", "?") + sep;
+        String serviceDir = "META-INF" + sep + "services" + sep;
+
+        RunnerUtil.createJar(
+            "-cf", testClasses + "SimpleProvider.jar",
+            "-C", testClasses, "SimpleProvider.class",
+            "-C", testClasses, "SimpleVirtualMachine.class",
+            "-C", testSrc,
+            serviceDir + "com.sun.tools.attach.spi.AttachProvider");
+    }
+
+    /**
+     * This is the actual test code that attaches to the running Application.
+     * This class is run in a separate process.
+     */
+    public static class TestMain {
+        public static void main(String args[]) throws Exception {
+            // deal with internal builds where classes are loaded from the
+            // 'classes' directory rather than rt.jar
+            ClassLoader cl = AttachProvider.class.getClassLoader();
+            if (cl != ClassLoader.getSystemClassLoader()) {
+                System.out.println("Attach API not loaded by system class loader - test skipped");
+                return;
+            }
+            VirtualMachine.attach("simple:1234").detach();
+        }
     }
 }
--- a/jdk/test/com/sun/tools/attach/ProviderTests.sh	Tue Dec 03 23:09:17 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# Copyright (c) 2005, 2010, 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 6173612
-# @summary AttachProvider unit tests
-#
-# @build ProviderTest SimpleProvider
-# @run shell ProviderTests.sh
-
-if [ "${TESTSRC}" = "" ]
-then
-  echo "TESTSRC not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-. ${TESTSRC}/CommonSetup.sh
-
-echo "Creating JAR file ..."
-
-$JAR -cf ${TESTCLASSES}/SimpleProvider.jar \
-    -C ${TESTCLASSES} SimpleProvider.class \
-    -C ${TESTCLASSES} SimpleVirtualMachine.class \
-    -C "${TESTSRC}" META-INF/services/com.sun.tools.attach.spi.AttachProvider
-
-echo "Running test ..."
-
-$JAVA ${TESTVMOPTS} -classpath \
-  "${TESTCLASSES}${PS}${TESTCLASSES}/SimpleProvider.jar${PS}${TESTJAVA}/lib/tools.jar" \
-  ProviderTest
-
--- a/jdk/test/com/sun/tools/attach/RedefineAgent.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/com/sun/tools/attach/RedefineAgent.java	Tue Dec 03 17:06:23 2013 +0100
@@ -43,15 +43,15 @@
 public class RedefineAgent implements ClassFileTransformer {
 
     static byte[] classfilebytes;
-    static final String targetName = "java.math.BigInteger";
-    static final String targetNameSlashes = "java/math/BigInteger";
+    static final String targetName = "RedefineDummy";
+    static final String targetNameSlashes = "RedefineDummy";
     static boolean gotRedefineTransform = false;
 
     // test transform and capture class bytes for redefine
     public byte[] transform(ClassLoader loader,
                             String className,
                             Class<?> classBeingRedefined,
-                            ProtectionDomain    protectionDomain,
+                            ProtectionDomain  protectionDomain,
                             byte[] classfileBuffer) {
         if (className.equals(targetNameSlashes)) {
             if (classBeingRedefined == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/RedefineDummy.java	Tue Dec 03 17:06:23 2013 +0100
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/*
+ * Simple dummy class used to test class retransform.
+ */
+public class RedefineDummy {
+    public String toString() {
+        return "RedefineDummy";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/RunnerUtil.java	Tue Dec 03 17:06:23 2013 +0100
@@ -0,0 +1,180 @@
+/*
+ * 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.IOException;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.JDKToolLauncher;
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.Utils;
+import jdk.testlibrary.ProcessThread;
+
+/*
+ * Utility functions for test runners.
+ * (Test runner = class that launch a test)
+ */
+public class RunnerUtil {
+    /**
+     * The Application process must be run concurrently with our tests since
+     * the tests will attach to the Application.
+     * We will run the Application process in a separate thread.
+     *
+     * The Application must be started with flag "-Xshare:off" for the Retransform
+     * test in TestBasics to pass on all platforms.
+     *
+     * The Application will write its pid and shutdownPort in the given outFile.
+     */
+    public static ProcessThread startApplication(String outFile) throws Throwable {
+        String classpath = System.getProperty("test.class.path", ".");
+        String[] args = Utils.addTestJavaOpts(
+            "-Dattach.test=true", "-classpath", classpath, "Application", outFile);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args);
+        ProcessThread pt = new ProcessThread("runApplication", pb);
+        pt.start();
+        return pt;
+    }
+
+    /**
+     * Will stop the running Application.
+     * First tries to shutdown nicely by connecting to the shut down port.
+     * If that fails, the process will be killed hard with stopProcess().
+     *
+     * If the nice shutdown fails, then an Exception is thrown and the test should fail.
+     *
+     * @param port The shut down port.
+     * @param processThread The process to stop.
+     */
+    public static void stopApplication(int port, ProcessThread processThread) throws Throwable {
+        if (processThread == null) {
+            System.out.println("RunnerUtil.stopApplication ignored since proc is null");
+            return;
+        }
+        try {
+            System.out.println("RunnerUtil.stopApplication waiting to for shutdown");
+            OutputAnalyzer output = ProcessTools.executeTestJvm(
+                    "-classpath",
+                    System.getProperty("test.class.path", "."),
+                    "Shutdown",
+                    Integer.toString(port));
+            // Verify that both the Shutdown command and the Application finished ok.
+            output.shouldHaveExitValue(0);
+            processThread.joinAndThrow();
+            processThread.getOutput().shouldHaveExitValue(0);
+        } catch (Throwable t) {
+            System.out.println("RunnerUtil.stopApplication failed. Will kill it hard: " + t);
+            processThread.stopProcess();
+            throw t;
+        }
+    }
+
+    /**
+     * Creates a jar file.
+     * @param args Command to the jar tool.
+     */
+    public static void createJar(String... args) {
+        System.out.println("Running: jar " + Arrays.toString(args));
+        sun.tools.jar.Main jar = new sun.tools.jar.Main(System.out, System.err, "jar");
+        if (!jar.run(args)) {
+            throw new RuntimeException("jar failed: args=" + Arrays.toString(args));
+        }
+    }
+
+    /**
+     * Read process info for the running Application.
+     * The Application writes its info to a file with this format:
+     * shutdownPort=42994
+     * pid=19597
+     * done
+     *
+     * The final "done" is used to make sure the complete file has been written
+     * before we try to read it.
+     * This function will wait until the file is available.
+     *
+     * @param filename Path to file to read.
+     * @return The ProcessInfo containing pid and shutdownPort.
+     */
+    public static ProcessInfo readProcessInfo(String filename) throws Throwable {
+        System.out.println("Reading port and pid from file: " + filename);
+        File file = new File(filename);
+        String content = null;
+
+        // Read file or wait for it to be created.
+        while (true) {
+            content = readFile(file);
+            if (content != null && content.indexOf("done") >= 0) {
+                break;
+            }
+            Thread.sleep(100);
+        }
+
+        ProcessInfo info = new ProcessInfo();
+        // search for a line with format: key=nnn
+        Pattern pattern = Pattern.compile("(\\w*)=([0-9]+)\\r?\\n");
+        Matcher matcher = pattern.matcher(content);
+        while (matcher.find()) {
+            String key = matcher.group(1);
+            int value  = Integer.parseInt(matcher.group(2));
+            if ("pid".equals(key)) {
+                info.pid = value;
+            } else if ("shutdownPort".equals(key)) {
+                info.shutdownPort = value;
+            }
+        }
+        System.out.println("processInfo.pid:" + info.pid);
+        System.out.println("processInfo.shutdownPort:" + info.shutdownPort);
+        return info;
+    }
+
+    /**
+     * Read the content of a file.
+     * @param file The file to read.
+     * @return The file content or null if file does not exists.
+     */
+    public static String readFile(File file) throws IOException {
+        if (!file.exists()) {
+            return null;
+        }
+        try {
+            byte[] bytes = Files.readAllBytes(file.toPath());
+            String content = new String(bytes);
+            return content;
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw e;
+        }
+    }
+
+    /**
+     * Helper class with info of the running Application.
+     */
+    public static class ProcessInfo {
+        public int pid = -1;
+        public int shutdownPort = -1;
+    }
+
+}
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessThread.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessThread.java	Tue Dec 03 17:06:23 2013 +0100
@@ -37,39 +37,21 @@
     /**
      * Creates a new {@code ProcessThread} object.
      *
-     * @param cmd The list of program and its arguments to pass to {@link ProcessBuilder}
+     * @param threadName The name of thread
+     * @param cmd The string array of program and its arguments to pass to {@link ProcessBuilder}
      */
-    public ProcessThread(List<String> cmd) {
-        super(new ProcessRunnable(cmd));
+    public ProcessThread(String threadName, String... cmd) {
+        super(new ProcessRunnable(new ProcessBuilder(cmd)), threadName);
     }
 
     /**
      * Creates a new {@code ProcessThread} object.
      *
-     * @param cmd The string array of program and its arguments to pass to {@link ProcessBuilder}
-     */
-    public ProcessThread(String... cmd) {
-        super(new ProcessRunnable(cmd));
-    }
-
-    /**
-     * Creates a new {@code ProcessThread} object.
-     *
-     * @param threadName The name of thread
-     * @param cmd The list of program and its arguments to pass to {@link ProcessBuilder}
+     * @param threadName The name of thread.
+     * @param pb The ProcessBuilder to execute.
      */
-    public ProcessThread(String threadName, List<String> cmd) {
-        super(new ProcessRunnable(cmd), threadName);
-    }
-
-    /**
-     * Creates a new {@code ProcessThread} object.
-     *
-     * @param threadName The name of thread
-     * @param cmd The string array of program and its arguments to pass to {@link ProcessBuilder}
-     */
-    public ProcessThread(String threadName, String... cmd) {
-        super(new ProcessRunnable(cmd), threadName);
+    public ProcessThread(String threadName, ProcessBuilder pb) {
+        super(new ProcessRunnable(pb), threadName);
     }
 
     /**
@@ -82,6 +64,13 @@
     }
 
     /**
+     * @return The process output, or null if the process has not yet completed.
+     */
+    public OutputAnalyzer getOutput() {
+        return ((ProcessRunnable) getRunnable()).getOutput();
+    }
+
+    /**
      * {@link Runnable} interface for starting and stopping {@link Process}.
      */
     static class ProcessRunnable extends XRun {
@@ -89,26 +78,16 @@
         private final ProcessBuilder processBuilder;
         private final CountDownLatch latch;
         private volatile Process process;
+        private volatile OutputAnalyzer output;
 
         /**
          * Creates a new {@code ProcessRunnable} object.
          *
-         * @param cmd The list of program and its arguments to to pass to {@link ProcessBuilder}
+         * @param pb The {@link ProcessBuilder} to run.
          */
-        public ProcessRunnable(List<String> cmd) {
+        public ProcessRunnable(ProcessBuilder pb) {
             super();
-            this.processBuilder = new ProcessBuilder(cmd);
-            this.latch = new CountDownLatch(1);
-        }
-
-        /**
-         * Creates a new {@code ProcessRunnable} object.
-         *
-         * @param cmd The string array of program and its arguments to to pass to {@link ProcessBuilder}
-         */
-        public ProcessRunnable(String... cmd) {
-            super();
-            this.processBuilder = new ProcessBuilder(cmd);
+            this.processBuilder = pb;
             this.latch = new CountDownLatch(1);
         }
 
@@ -125,12 +104,16 @@
             latch.countDown();
 
             // Will block...
-            OutputAnalyzer output = new OutputAnalyzer(this.process);
-
-            assertTrue(output.getOutput().isEmpty(), "Should get an empty output, got: "
-                        + Utils.NEW_LINE + output.getOutput());
-            assertNotEquals(output.getExitValue(), 0,
-                    "Process exited with unexpected exit code");
+            try {
+                output = new OutputAnalyzer(this.process);
+            } catch (Throwable t) {
+                String name = Thread.currentThread().getName();
+                System.out.println(String.format("ProcessThread[%s] failed: %s", name, t.toString()));
+                throw t;
+            } finally {
+                String logMsg = ProcessTools.getProcessLog(processBuilder, output);
+                System.out.println(logMsg);
+            }
         }
 
         /**
@@ -142,10 +125,19 @@
             // Wait until process is started
             latch.await();
             if (this.process != null) {
+                System.out.println("ProcessThread.stopProcess() will kill process");
                 this.process.destroy();
             }
         }
 
+        /**
+         * Returns the OutputAnalyzer with stdout/stderr from the process.
+         * @return The process output, or null if process not completed.
+         * @throws InterruptedException
+         */
+        public OutputAnalyzer getOutput() {
+            return output;
+        }
     }
 
 }
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Tue Dec 03 17:06:23 2013 +0100
@@ -266,4 +266,79 @@
         }
     }
 
+    /**
+     * Executes a test jvm process, waits for it to finish and returns the process output.
+     * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added.
+     * The java from the test.jdk is used to execute the command.
+     *
+     * The command line will be like:
+     * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds
+     *
+     * @param cmds User specifed arguments.
+     * @return The output from the process.
+     */
+    public static OutputAnalyzer executeTestJvm(String... cmds) throws Throwable {
+        ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(cmds));
+        return executeProcess(pb);
+    }
+
+    /**
+     * Executes a process, waits for it to finish and returns the process output.
+     * @param pb The ProcessBuilder to execute.
+     * @return The output from the process.
+     */
+    public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Throwable {
+        OutputAnalyzer output = null;
+        try {
+            output = new OutputAnalyzer(pb.start());
+            return output;
+        } catch (Throwable t) {
+            System.out.println("executeProcess() failed: " + t);
+            throw t;
+        } finally {
+            System.out.println(getProcessLog(pb, output));
+        }
+    }
+
+    /**
+     * Executes a process, waits for it to finish and returns the process output.
+     * @param cmds The command line to execute.
+     * @return The output from the process.
+     */
+    public static OutputAnalyzer executeProcess(String... cmds) throws Throwable {
+        return executeProcess(new ProcessBuilder(cmds));
+    }
+
+    /**
+     * Used to log command line, stdout, stderr and exit code from an executed process.
+     * @param pb The executed process.
+     * @param output The output from the process.
+     */
+    public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) {
+        String stderr = output == null ? "null" : output.getStderr();
+        String stdout = output == null ? "null" : output.getStdout();
+        String exitValue = output == null ? "null": Integer.toString(output.getExitValue());
+        StringBuilder logMsg = new StringBuilder();
+        final String nl = System.getProperty("line.separator");
+        logMsg.append("--- ProcessLog ---" + nl);
+        logMsg.append("cmd: " + getCommandLine(pb) + nl);
+        logMsg.append("exitvalue: " + exitValue + nl);
+        logMsg.append("stderr: " + stderr + nl);
+        logMsg.append("stdout: " + stdout + nl);
+        return logMsg.toString();
+    }
+
+    /**
+     * @return The full command line for the ProcessBuilder.
+     */
+    public static String getCommandLine(ProcessBuilder pb) {
+        if (pb == null) {
+            return "null";
+        }
+        StringBuilder cmd = new StringBuilder();
+        for (String s : pb.command()) {
+            cmd.append(s).append(" ");
+        }
+        return cmd.toString().trim();
+    }
 }
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/Utils.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/Utils.java	Tue Dec 03 17:06:23 2013 +0100
@@ -33,6 +33,8 @@
 import java.util.List;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
 
 /**
  * Common library for various test helper functions.
@@ -170,4 +172,61 @@
         return hostName;
     }
 
+    /**
+     * Uses "jcmd -l" to search for a jvm pid. This function will wait
+     * forever (until jtreg timeout) for the pid to be found.
+     * @param key Regular expression to search for
+     * @return The found pid.
+     */
+    public static int waitForJvmPid(String key) throws Throwable {
+        final long iterationSleepMillis = 250;
+        System.out.println("waitForJvmPid: Waiting for key '" + key + "'");
+        System.out.flush();
+        while (true) {
+            int pid = tryFindJvmPid(key);
+            if (pid >= 0) {
+                return pid;
+            }
+            Thread.sleep(iterationSleepMillis);
+        }
+    }
+
+    /**
+     * Searches for a jvm pid in the output from "jcmd -l".
+     *
+     * Example output from jcmd is:
+     * 12498 sun.tools.jcmd.JCmd -l
+     * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar
+     *
+     * @param key A regular expression to search for.
+     * @return The found pid, or -1 if Enot found.
+     * @throws Exception If multiple matching jvms are found.
+     */
+    public static int tryFindJvmPid(String key) throws Throwable {
+        ProcessBuilder pb = null;
+        OutputAnalyzer output = null;
+        try {
+            JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd");
+            jcmdLauncher.addToolArg("-l");
+            output = ProcessTools.executeProcess(jcmdLauncher.getCommand());
+            output.shouldHaveExitValue(0);
+
+            // Search for a line starting with numbers (pid), follwed by the key.
+            Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n");
+            Matcher matcher = pattern.matcher(output.getStdout());
+
+            int pid = -1;
+            if (matcher.find()) {
+                pid = Integer.parseInt(matcher.group(1));
+                System.out.println("findJvmPid.pid: " + pid);
+                if (matcher.find()) {
+                    throw new Exception("Found multiple JVM pids for key: " + key);
+                }
+            }
+            return pid;
+        } catch (Throwable t) {
+            System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t));
+            throw t;
+        }
+    }
 }
--- a/jdk/test/sun/tools/jstatd/JstatdTest.java	Tue Dec 03 23:09:17 2013 -0800
+++ b/jdk/test/sun/tools/jstatd/JstatdTest.java	Tue Dec 03 17:06:23 2013 +0100
@@ -353,6 +353,14 @@
         } finally {
             cleanUpThread(jstatdThread);
         }
+
+        // Verify output from jstatd
+        OutputAnalyzer output = jstatdThread.getOutput();
+        assertTrue(output.getOutput().isEmpty(),
+                "jstatd should get an empty output, got: "
+                + Utils.NEW_LINE + output.getOutput());
+        assertNotEquals(output.getExitValue(), 0,
+                "jstatd process exited with unexpected exit code");
     }
 
 }