8023093: Add ManagementAgent.status diagnostic command
authorjbachorik
Wed, 25 Mar 2015 18:33:17 +0100
changeset 30334 22a5f09f4da9
parent 30333 e8999e87feae
child 30335 b7b1fab1fb2f
8023093: Add ManagementAgent.status diagnostic command Reviewed-by: sla
jdk/src/java.management/share/classes/sun/management/Agent.java
jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java
jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java
jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java
jdk/test/sun/management/jmxremote/startstop/ManagementAgentJcmd.java
jdk/test/sun/management/jmxremote/startstop/PortAllocator.java
jdk/test/sun/management/jmxremote/startstop/REMOTE_TESTING.txt
jdk/test/sun/management/jmxremote/startstop/TestApp.java
--- a/jdk/src/java.management/share/classes/sun/management/Agent.java	Mon Mar 30 10:10:19 2015 +0200
+++ b/jdk/src/java.management/share/classes/sun/management/Agent.java	Wed Mar 25 18:33:17 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, 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
@@ -34,6 +34,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
+import java.net.MalformedURLException;
 import java.net.UnknownHostException;
 import java.text.MessageFormat;
 import java.util.MissingResourceException;
@@ -55,6 +56,125 @@
  * system class loader. Also jmx framework could be started by jcmd
  */
 public class Agent {
+    /**
+     * Agent status collector strategy class
+     */
+    private static abstract class StatusCollector {
+        final protected StringBuilder sb = new StringBuilder();
+        final public String collect() {
+            Properties agentProps = VMSupport.getAgentProperties();
+            String localConnAddr = (String)agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
+            if (localConnAddr != null || jmxServer != null) {
+                addAgentStatus(true);
+                appendConnections(localConnAddr);
+            } else {
+                addAgentStatus(false);
+            }
+            return sb.toString();
+        }
+
+        private void appendConnections(String localConnAddr) {
+            appendConnectionsHeader();
+            if (localConnAddr != null) {
+                try {
+                    JMXServiceURL u = new JMXServiceURL(localConnAddr);
+                    addConnection(false, u);
+                } catch (MalformedURLException e) {
+                    // will never happen
+                }
+
+            }
+            if (jmxServer != null) {
+                addConnection(true, jmxServer.getAddress());
+            }
+            appendConnectionsFooter();
+        }
+
+        private void addConnection(boolean remote, JMXServiceURL u) {
+            appendConnectionHeader(remote);
+            addConnectionDetails(u);
+            if (remote) {
+                addConfigProperties();
+            }
+            appendConnectionFooter(remote);
+        }
+
+        private void addConfigProperties() {
+            appendConfigPropsHeader();
+            boolean[] first = new boolean[] {true};
+            configProps.entrySet().stream().forEach((e) -> {
+                String key = (String)e.getKey();
+                if (key.startsWith("com.sun.management.")) {
+                    addConfigProp(key, e.getValue(), first[0]);
+                    first[0] = false;
+                }
+            });
+            appendConfigPropsFooter();
+        }
+
+        abstract protected void addAgentStatus(boolean enabled);
+        abstract protected void appendConnectionsHeader();
+        abstract protected void appendConnectionsFooter();
+        abstract protected void addConnectionDetails(JMXServiceURL u);
+        abstract protected void appendConnectionHeader(boolean remote);
+        abstract protected void appendConnectionFooter(boolean remote);
+        abstract protected void appendConfigPropsHeader();
+        abstract protected void appendConfigPropsFooter();
+        abstract protected void addConfigProp(String key, Object value, boolean first);
+    }
+
+    /**
+     * Free-text status collector strategy implementation
+     */
+    final private static class TextStatusCollector extends StatusCollector {
+
+        @Override
+        protected void addAgentStatus(boolean enabled) {
+            sb.append("Agent: ").append(enabled ? "enabled" : "disabled").append('\n');
+        }
+
+        @Override
+        protected void appendConnectionsHeader() {
+            sb.append('\n');
+        }
+
+        @Override
+        protected void addConnectionDetails(JMXServiceURL u) {
+            sb.append("Protocol       : ").append(u.getProtocol()).append('\n')
+              .append("Host           : ").append(u.getHost()).append('\n')
+              .append("URL            : ").append(u).append('\n');
+        }
+
+        @Override
+        protected void appendConnectionHeader(boolean remote) {
+            sb.append("Connection Type: ").append(remote ? "remote" : "local").append('\n');
+        }
+
+        @Override
+        protected void appendConfigPropsHeader() {
+            sb.append("Properties     :\n");
+        }
+
+        @Override
+        protected void addConfigProp(String key, Object value, boolean first) {
+            if (!first) {
+                sb.append('\n');
+            }
+            sb.append("  ").append(key).append(" = ").append(value);
+        }
+
+        @Override
+        protected void appendConnectionsFooter() {}
+
+        @Override
+        protected void appendConnectionFooter(boolean remote) {
+            sb.append('\n');
+        }
+
+        @Override
+        protected void appendConfigPropsFooter() {}
+    }
+
     // management properties
 
     private static Properties mgmtProps;
@@ -81,6 +201,8 @@
 
     // The only active agent allowed
     private static JMXConnectorServer jmxServer = null;
+    // The properties used to configure the server
+    private static Properties configProps = null;
 
     // Parse string com.sun.management.prop=xxx,com.sun.management.prop=yyyy
     // and return property set if args is null or empty
@@ -161,7 +283,7 @@
 
         try {
             Properties argProps = parseString(args);
-            Properties configProps = new Properties();
+            configProps = new Properties();
 
             // Load the management properties from the config file
             // if config file is not specified readConfiguration implicitly
@@ -228,9 +350,14 @@
             // Don't cause any errors.
             jmxServer.stop();
             jmxServer = null;
+            configProps = null;
         }
     }
 
+    private static synchronized String getManagementAgentStatus() throws Exception {
+        return new TextStatusCollector().collect();
+    }
+
     private static void startAgent(Properties props) throws Exception {
         String snmpPort = props.getProperty(SNMP_PORT);
         String jmxremote = props.getProperty(JMXREMOTE);
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java	Mon Mar 30 10:10:19 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 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.
- */
-
-import java.io.IOException;
-import jdk.testlibrary.ProcessTools;
-
-public class JMXStartStopDoSomething {
-    public static void doSomething() throws IOException{
-        int r = System.in.read();
-        System.out.println("read: " + r);
-    }
-
-    public static void main(String args[]) throws Exception {
-        System.out.println("main enter");
-        System.out.flush();
-        doSomething();
-        System.out.println("main exit");
-    }
-}
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java	Mon Mar 30 10:10:19 2015 +0200
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java	Wed Mar 25 18:33:17 2015 +0100
@@ -35,20 +35,16 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 import javax.management.*;
 import javax.management.remote.*;
 import javax.net.ssl.SSLHandshakeException;
 
 import jdk.testlibrary.ProcessTools;
-import jdk.testlibrary.JDKToolLauncher;
 import sun.management.Agent;
 import sun.management.AgentConfigurationError;
 
@@ -56,45 +52,19 @@
  * @test
  * @bug 7110104
  * @library /lib/testlibrary
- * @build jdk.testlibrary.* JMXStartStopTest JMXStartStopDoSomething
+ * @build jdk.testlibrary.* JMXStartStopTest PortAllocator TestApp ManagementAgentJcmd
  * @run main/othervm/timeout=600 -XX:+UsePerfData JMXStartStopTest
  * @summary Makes sure that enabling/disabling the management agent through JCMD
  *          achieves the desired results
  */
 public class JMXStartStopTest {
+    private static final String TEST_APP_NAME = "TestApp";
 
     private static final String TEST_SRC = System.getProperty("test.src");
 
     private static final boolean verbose = false;
 
-    /**
-     * Dynamically allocates distinct ports from the ephemeral range 49152-65535
-     */
-    private static class PortAllocator {
-
-        private final static int LOWER_BOUND = 49152;
-        private final static int UPPER_BOUND = 65535;
-
-        private final static Random RND = new Random(System.currentTimeMillis());
-
-        private static int[] allocatePorts(final int numPorts) {
-            int[] ports = new int[numPorts];
-            for (int i = 0; i < numPorts; i++) {
-                int port = -1;
-                while (port == -1) {
-                    port = RND.nextInt(UPPER_BOUND - LOWER_BOUND + 1) + LOWER_BOUND;
-                    for (int j = 0; j < i; j++) {
-                        if (ports[j] == port) {
-                            port = -1;
-                            break;
-                        }
-                    }
-                }
-                ports[i] = port;
-            }
-            return ports;
-        }
-    }
+    private static ManagementAgentJcmd jcmd = new ManagementAgentJcmd(TEST_APP_NAME, verbose);
 
     private static void dbg_print(String msg) {
         if (verbose) {
@@ -317,14 +287,14 @@
         }
     }
 
-    private static class Something {
+    private static class TestAppRun {
         private Process p;
         private final ProcessBuilder pb;
         private final String name;
         private final AtomicBoolean started = new AtomicBoolean(false);
         private volatile long pid = -1;
 
-        public Something(ProcessBuilder pb, String name) {
+        public TestAppRun(ProcessBuilder pb, String name) {
             this.pb = pb;
             this.name = name;
         }
@@ -334,7 +304,7 @@
                 try {
                     AtomicBoolean error = new AtomicBoolean(false);
                     p = ProcessTools.startProcess(
-                            "JMXStartStopDoSomething{" + name + "}",
+                            TEST_APP_NAME + "{" + name + "}",
                             pb,
                             (line) -> {
                                 boolean ok = line.equals("main enter");
@@ -381,105 +351,31 @@
     }
 
     /**
-     * Runs the test application "JMXStartStopDoSomething"
+     * Runs the test application "TestApp"
      * @param name Test run name
      * @param args Additional arguments
-     * @return Returns a {@linkplain Something} instance representing the run
+     * @return Returns a {@linkplain TestAppRun} instance representing the run
      * @throws IOException
      * @throws InterruptedException
      * @throws TimeoutException
      */
-    private static Something doSomething(String name, String ... args)
+    private static TestAppRun doTest(String name, String ... args)
             throws Exception {
         List<String> pbArgs = new ArrayList<>(Arrays.asList(
                 "-cp",
                 System.getProperty("test.class.path")
         ));
         pbArgs.addAll(Arrays.asList(args));
-        pbArgs.add("JMXStartStopDoSomething");
+        pbArgs.add(TEST_APP_NAME);
 
         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
                 pbArgs.toArray(new String[pbArgs.size()])
         );
-        Something s = new Something(pb, name);
+        TestAppRun s = new TestAppRun(pb, name);
         s.start();
         return s;
     }
 
-    /**
-     * Run the "jcmd" command
-     *
-     * @param command Command with parameters; space separated string
-     * @throws IOException
-     * @throws InterruptedException
-     */
-    private static void jcmd(String ... command) throws IOException, InterruptedException {
-        if (command.length == 0) {
-            jcmd(null, c->{});
-        } else {
-            jcmd(null, command);
-        }
-    }
-
-    /**
-     * Run the "jcmd" command
-     *
-     * @param c {@linkplain Consumer} instance
-     * @param command Command with parameters; space separated string
-     * @throws IOException
-     * @throws InterruptedException
-     */
-    private static void jcmd(Consumer<String> c, String ... command) throws IOException, InterruptedException {
-        jcmd("JMXStartStopDoSomething", c, command);
-    }
-
-    /**
-     * Run the "jcmd" command
-     *
-     * @param target The target application name (or PID)
-     * @param c {@linkplain Consumer} instance
-     * @param command Command with parameters; space separated string
-     * @throws IOException
-     * @throws InterruptedException
-     */
-    private static void jcmd(String target, final Consumer<String> c, String ... command) throws IOException, InterruptedException {
-        dbg_print("[jcmd] " + (command.length > 0 ? command[0] : "list"));
-
-        JDKToolLauncher l = JDKToolLauncher.createUsingTestJDK("jcmd");
-        l.addToolArg(target);
-        for (String cmd : command) {
-            l.addToolArg(cmd);
-        }
-
-        AtomicBoolean portUnavailable = new AtomicBoolean(false);
-        Process p = ProcessTools.startProcess(
-            "jcmd",
-            new ProcessBuilder(l.getCommand()),
-            line -> {
-                if (line.contains("BindException") ||
-                    line.contains(Agent.getText(AgentConfigurationError.CONNECTOR_SERVER_IO_ERROR))) {
-                    portUnavailable.set(true);
-                } else {
-                    c.accept(line);
-                }
-            }
-        );
-
-        p.waitFor();
-        dbg_print("[jcmd] --------");
-        if (portUnavailable.get()) {
-            String cmd = Arrays.asList(l.getCommand()).stream()
-                    .collect(
-                            Collectors.joining(" ", "", ": Unable to bind address")
-                    );
-            throw new BindException(cmd);
-        }
-    }
-
-    private static final String CMD_STOP = "ManagementAgent.stop";
-    private static final String CMD_START = "ManagementAgent.start";
-    private static final String CMD_START_LOCAL = "ManagementAgent.start_local";
-
     static void test_01() throws Exception {
         // Run an app with JMX enabled stop it and
         // restart on other port
@@ -487,7 +383,7 @@
         System.out.println("**** Test one ****");
         int ports[] = PortAllocator.allocatePorts(2);
 
-        Something s = doSomething(
+        TestAppRun s = doTest(
                 "test_01",
                 "-Dcom.sun.management.jmxremote.port=" + ports[0],
                 "-Dcom.sun.management.jmxremote.authenticate=false",
@@ -496,10 +392,10 @@
         try {
             testConnect(ports[0]);
 
-            jcmd(CMD_STOP);
+            jcmd.stop();
             testNoConnect(ports[0]);
 
-            jcmd(CMD_START, "jmxremote.port=" + ports[1]);
+            jcmd.start("jmxremote.port=" + ports[1]);
             testConnect(ports[1]);
         } finally {
             s.stop();
@@ -513,12 +409,13 @@
         System.out.println("**** Test two ****");
 
         int[] ports = PortAllocator.allocatePorts(1);
-        Something s = doSomething("test_02");
+        TestAppRun s = doTest("test_02");
         try {
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[0],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             testConnect(ports[0]);
         } finally {
@@ -534,18 +431,20 @@
         System.out.println("**** Test three ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething("test_03");
+        TestAppRun s = doTest("test_03");
         try {
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[0],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             // Second agent shouldn't start
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[1],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[1],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             // First agent should connect
             testConnect(ports[0]);
@@ -564,13 +463,14 @@
         System.out.println("**** Test four ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething("test_04");
+        TestAppRun s = doTest("test_04");
         try {
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.rmi.port=" + ports[1],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[0],
+                "jmxremote.rmi.port=" + ports[1],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             testConnect(ports[0], ports[1]);
         } finally {
@@ -584,9 +484,9 @@
 
         System.out.println("**** Test five ****");
         int[] ports = PortAllocator.allocatePorts(1);
-        Something s = doSomething("test_05");
+        TestAppRun s = doTest("test_05");
         try {
-            jcmd(CMD_START_LOCAL);
+            jcmd.startLocal();
 
             testNoConnect(ports[0]);
             testConnectLocal(s.getPid());
@@ -604,26 +504,27 @@
         System.out.println("**** Test six ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething("test_06");
+        TestAppRun s = doTest("test_06");
         try {
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[0],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             testConnect(ports[0], ports[1]);
 
             final AtomicBoolean checks = new AtomicBoolean(false);
-            jcmd(
-                    line -> {
-                        if (line.contains("java.lang.RuntimeException: Invalid agent state")) {
-                            checks.set(true);
-                        }
-                    },
-                    CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                line -> {
+                    if (line.contains("java.lang.RuntimeException: Invalid agent state")) {
+                        checks.set(true);
+                    }
+                },
+                "jmxremote.port=" + ports[0],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             if (!checks.get()) {
                 throw new Exception("Starting agent on port " + ports[0] + " should "
@@ -643,27 +544,28 @@
         System.out.println("**** Test seven ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething("test_07");
+        TestAppRun s = doTest("test_07");
         try {
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[0],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             testConnect(ports[0], ports[1]);
 
             final AtomicBoolean checks = new AtomicBoolean(false);
 
-            jcmd(
-                    line -> {
-                        if (line.contains("java.lang.RuntimeException: Invalid agent state")) {
-                            checks.set(true);
-                        }
-                    },
-                    CMD_START,
-                    "jmxremote.port=" + ports[1],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                line -> {
+                    if (line.contains("java.lang.RuntimeException: Invalid agent state")) {
+                        checks.set(true);
+                    }
+                },
+                "jmxremote.port=" + ports[1],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             if (!checks.get()) {
                 throw new Exception("Starting agent on poprt " + ports[1] + " should "
@@ -683,17 +585,18 @@
         System.out.println("**** Test eight ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething("test_08");
+        TestAppRun s = doTest("test_08");
         try {
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false");
+            jcmd.start(
+                "jmxremote.port=" + ports[0],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
+            );
 
             testConnect(ports[0], ports[1]);
 
-            jcmd(CMD_STOP);
-            jcmd(CMD_STOP);
+            jcmd.stop();
+            jcmd.stop();
         } finally {
             s.stop();
         }
@@ -706,7 +609,7 @@
 
         System.out.println("**** Test nine ****");
 
-        Something s = doSomething("test_09");
+        TestAppRun s = doTest("test_09");
 
         try (ServerSocket ss = new ServerSocket(0)) {
             int localPort = ss.getLocalPort();
@@ -722,13 +625,12 @@
                 final AtomicBoolean retry = new AtomicBoolean(false);
 
                 try {
-                    jcmd(
+                    jcmd.start(
                         line -> {
                             if (line.contains(Agent.getText(AgentConfigurationError.AGENT_EXCEPTION))) {
                                 retry.set(true);
                             }
                         },
-                        CMD_START,
                         "jmxremote.port=" + ports[0],
                         "jmxremote.rmi.port=" + localPort,
                         "jmxremote.authenticate=false",
@@ -763,18 +665,17 @@
         System.out.println("**** Test ten ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething(
+        TestAppRun s = doTest(
                 "test_10",
                 "-Dcom.sun.management.jmxremote.authenticate=false",
                 "-Dcom.sun.management.jmxremote.ssl=true");
 
         try {
             testNoConnect(ports[0]);
-            jcmd(
-                    CMD_START,
-                    "jmxremote.port=" + ports[1],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false"
+            jcmd.start(
+                "jmxremote.port=" + ports[1],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
             );
             testConnect(ports[1]);
         } finally {
@@ -790,7 +691,7 @@
 
         System.out.println("**** Test eleven ****");
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething(
+        TestAppRun s = doTest(
                 "test_11",
                 "-Dcom.sun.management.jmxremote.port=" + ports[0],
                 "-Dcom.sun.management.jmxremote.authenticate=false",
@@ -799,15 +700,14 @@
         try {
             testNoConnect(ports[0]);
 
-            jcmd(CMD_STOP);
+            jcmd.stop();
 
             testNoConnect(ports[0]);
 
-            jcmd(
-                    CMD_START,
-                    "jmxremote.port=" + ports[1],
-                    "jmxremote.authenticate=false",
-                    "jmxremote.ssl=false"
+            jcmd.start(
+                "jmxremote.port=" + ports[1],
+                "jmxremote.authenticate=false",
+                "jmxremote.ssl=false"
             );
 
             testConnect(ports[1]);
@@ -827,7 +727,7 @@
         System.out.println("**** Test twelve ****");
 
         int[] ports = PortAllocator.allocatePorts(2);
-        Something s = doSomething("test_12",
+        TestAppRun s = doTest("test_12",
                 "-Dcom.sun.management.config.file="
                 + TEST_SRC + File.separator + "management_cl.properties",
                 "-Dcom.sun.management.jmxremote.authenticate=false"
@@ -836,15 +736,15 @@
         try {
             testNoConnect(ports[0]);
 
-            jcmd(CMD_STOP);
+            jcmd.stop();
 
             testNoConnect(ports[0]);
 
-            jcmd(CMD_START,
-                    "config.file=" + TEST_SRC + File.separator
-                    + "management_jcmd.properties",
-                    "jmxremote.authenticate=false",
-                    "jmxremote.port=" + ports[1]
+            jcmd.start(
+                "config.file=" + TEST_SRC + File.separator
+                + "management_jcmd.properties",
+                "jmxremote.authenticate=false",
+                "jmxremote.port=" + ports[1]
             );
 
             testConnect(ports[1]);
@@ -862,7 +762,7 @@
 
         System.out.println("**** Test thirteen ****");
         int[] ports = PortAllocator.allocatePorts(1);
-        Something s = doSomething(
+        TestAppRun s = doTest(
                 "test_13",
                 "-Dcom.sun.management.jmxremote.port=" + ports[0],
                 "-Dcom.sun.management.jmxremote.authenticate=false",
@@ -871,16 +771,16 @@
         try {
             testNoConnect(ports[0]);
 
-            jcmd(CMD_STOP);
-            jcmd(CMD_START,
-                    "jmxremote.ssl=false",
-                    "jmxremote.port=" + ports[0]
+            jcmd.stop();
+            jcmd.start(
+                "jmxremote.ssl=false",
+                "jmxremote.port=" + ports[0]
             );
             testConnect(ports[0]);
 
-            jcmd(CMD_STOP);
-            jcmd(CMD_START,
-                    "jmxremote.port=" + ports[0]
+            jcmd.stop();
+            jcmd.start(
+                "jmxremote.port=" + ports[0]
             );
 
             testNoConnect(ports[0]);
@@ -896,14 +796,14 @@
 
         System.out.println("**** Test fourteen ****");
         int[] ports = PortAllocator.allocatePorts(1);
-        Something s = doSomething(
+        TestAppRun s = doTest(
                 "test_14",
                 "-Dcom.sun.management.jmxremote.port=" + ports[0],
                 "-Dcom.sun.management.jmxremote.authenticate=false",
                 "-Dcom.sun.management.jmxremote.ssl=false");
         try {
             testConnect(ports[0]);
-            jcmd(CMD_STOP);
+            jcmd.stop();
             testConnectLocal(s.getPid());
         } finally {
             s.stop();
@@ -917,11 +817,11 @@
         System.out.println("**** Test fifteen ****");
 
         int[] ports = PortAllocator.allocatePorts(1);
-        Something s = doSomething("test_15");
+        TestAppRun s = doTest("test_15");
 
         try {
             testNoConnect(ports[0]);
-            jcmd(CMD_START + "_local");
+            jcmd.startLocal();
 
             testConnectLocal(s.getPid());
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java	Wed Mar 25 18:33:17 2015 +0100
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2015, 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.net.BindException;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import org.testng.annotations.*;
+import static org.testng.Assert.*;
+
+import jdk.testlibrary.ProcessTools;
+
+/**
+ * @test
+ * @bug 8023093
+ * @summary Performs a sanity test for the ManagementAgent.status diagnostic command.
+ *          Management agent may be disable, started (only local connections) and started.
+ *          The test asserts that the expected text is being printed.
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.* PortAllocator TestApp ManagementAgentJcmd
+ * @run testng JMXStatusTest
+ */
+public class JMXStatusTest {
+    private final static String TEST_APP_NAME = "TestApp";
+
+    private final static Pattern DISABLE_AGENT_STATUS = Pattern.compile(
+        "Agent\\s*\\: disabled$"
+    );
+
+    private final static Pattern LOCAL_AGENT_STATUS = Pattern.compile(
+        "Agent\\s*\\:\\s*enabled\\n+" +
+        "Connection Type\\s*\\:\\s*local\\n+" +
+        "Protocol\\s*\\:\\s*[a-z]+\\n+" +
+        "Host\\s*\\:\\s*.+\\n+" +
+        "URL\\s*\\:\\s*service\\:jmx\\:.+",
+        Pattern.MULTILINE
+    );
+
+    private final static Pattern REMOTE_AGENT_STATUS = Pattern.compile(
+        "Agent\\s*\\: enabled\\n+" +
+        "Connection Type\\s*\\: remote\\n+" +
+        "Protocol\\s*\\: [a-z]+\\n+" +
+        "Host\\s*\\: .+\\n+" +
+        "URL\\s*\\: service\\:jmx\\:.+\\n+" +
+        "Properties\\s*\\:\\n+(\\s*\\S+\\s*=\\s*\\S+\\n*)+",
+        Pattern.MULTILINE
+    );
+
+    private static ProcessBuilder testAppPb;
+    private Process testApp;
+
+    private ManagementAgentJcmd jcmd;
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        testAppPb = ProcessTools.createJavaProcessBuilder(
+            "-cp", System.getProperty("test.class.path"),
+            TEST_APP_NAME
+        );
+    }
+
+    @BeforeTest
+    public void setup() {
+        jcmd = new ManagementAgentJcmd(TEST_APP_NAME, false);
+    }
+
+    @BeforeMethod
+    public void startTestApp() throws Exception {
+        testApp = ProcessTools.startProcess(
+            TEST_APP_NAME, testAppPb,
+            (Predicate<String>)l->l.trim().equals("main enter")
+        );
+    }
+
+    @AfterMethod
+    public void stopTestApp() throws Exception {
+        testApp.getOutputStream().write(1);
+        testApp.getOutputStream().flush();
+        testApp.waitFor();
+        testApp = null;
+    }
+
+    @Test
+    public void testAgentDisabled() throws Exception {
+        String status = jcmd.status();
+        assertStatusMatches(DISABLE_AGENT_STATUS, status);
+    }
+
+    @Test
+    public void testAgentLocal() throws Exception {
+        jcmd.startLocal();
+        String status = jcmd.status();
+
+        assertStatusMatches(LOCAL_AGENT_STATUS, status);
+    }
+
+    @Test
+    public void testAgentRemote() throws Exception {
+        while (true) {
+            try {
+                int[] ports = PortAllocator.allocatePorts(1);
+                jcmd.start(
+                    "jmxremote.port=" + ports[0],
+                    "jmxremote.authenticate=false",
+                    "jmxremote.ssl=false"
+                );
+                String status = jcmd.status();
+
+                assertStatusMatches(REMOTE_AGENT_STATUS, status);
+                return;
+            } catch (BindException e) {
+                System.out.println("Failed to allocate ports. Retrying ...");
+            }
+        }
+    }
+
+    private void assertStatusMatches(Pattern expected, String value) {
+        assertStatusMatches(expected, value, "");
+    }
+
+    private void assertStatusMatches(Pattern expected, String value, String msg) {
+        int idx = value.indexOf('\n');
+        if (idx > -1) {
+            value = value.substring(idx + 1).trim();
+            assertTrue(expected.matcher(value).find(), msg);
+        } else {
+            fail("The management agent status must contain more then one line of text");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/management/jmxremote/startstop/ManagementAgentJcmd.java	Wed Mar 25 18:33:17 2015 +0100
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2015, 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.net.BindException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import sun.management.Agent;
+import sun.management.AgentConfigurationError;
+
+import jdk.testlibrary.JDKToolLauncher;
+import jdk.testlibrary.ProcessTools;
+
+/**
+ * A helper class for issuing ManagementAgent.* diagnostic commands and capturing
+ * their output.
+ */
+final class ManagementAgentJcmd {
+    private static final String CMD_STOP = "ManagementAgent.stop";
+    private static final String CMD_START = "ManagementAgent.start";
+    private static final String CMD_START_LOCAL = "ManagementAgent.start_local";
+    private static final String CMD_STATUS = "ManagementAgent.status";
+
+    private final String id;
+    private final boolean verbose;
+
+    public ManagementAgentJcmd(String targetApp, boolean verbose) {
+        this.id = targetApp;
+        this.verbose = verbose;
+    }
+
+    /**
+     * `jcmd`
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public String list() throws IOException, InterruptedException {
+        return jcmd();
+    }
+
+    /**
+     * `jcmd <app> ManagementAgent.stop`
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public String stop() throws IOException, InterruptedException {
+        return jcmd(CMD_STOP);
+    }
+
+    /**
+     * `jcmd <app> ManagementAgent.start_local`
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public String startLocal() throws IOException, InterruptedException {
+        return jcmd(CMD_START_LOCAL);
+    }
+
+    /**
+     * `jcmd <app> ManagementAgent.start <args>`
+     * @return The JCMD output
+     * @param params The arguments to <b>ManagementAgent.start</b> command
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public String start(String ... params) throws IOException, InterruptedException {
+        return start(c->{}, params);
+    }
+
+    /**
+     * `jcmd <pp> ManagementAgent.start <args>`
+     * @param c A string consumer used to inspect the jcmd output line-by-line
+     * @param params The arguments to <b>ManagementAgent.start</b> command
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public String start(Consumer<String> c, String ... params) throws IOException, InterruptedException {
+        List<String> args = new ArrayList<>();
+        args.add(CMD_START);
+        args.addAll(Arrays.asList(params));
+        return jcmd(c, args.toArray(new String[args.size()]));
+    }
+
+    public String status() throws IOException, InterruptedException {
+        return jcmd(CMD_STATUS);
+    }
+
+    /**
+     * Run the "jcmd" command
+     *
+     * @param command Command + arguments
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    private String jcmd(String ... command) throws IOException, InterruptedException {
+        if (command.length == 0) {
+            return jcmd(null, c->{});
+        } else {
+            return jcmd(c->{}, command);
+        }
+    }
+
+    /**
+     * Run the "jcmd" command
+     *
+     * @param c {@linkplain Consumer} instance
+     * @param command Command + arguments
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    private String jcmd(Consumer<String> c, String ... command) throws IOException, InterruptedException {
+        return jcmd(id, c, command);
+    }
+
+    /**
+     * Run the "jcmd" command
+     *
+     * @param target The target application name (or PID)
+     * @param c {@linkplain Consumer} instance
+     * @param command Command + arguments
+     * @return The JCMD output
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    private String jcmd(String target, final Consumer<String> c, String ... command) throws IOException, InterruptedException {
+        dbg_print("[jcmd] " + (command.length > 0 ? command[0] : "list"));
+
+        JDKToolLauncher l = JDKToolLauncher.createUsingTestJDK("jcmd");
+        l.addToolArg(target);
+        for (String cmd : command) {
+            l.addToolArg(cmd);
+        }
+
+        StringBuilder output = new StringBuilder();
+
+        AtomicBoolean portUnavailable = new AtomicBoolean(false);
+        Process p = ProcessTools.startProcess(
+            "jcmd",
+            new ProcessBuilder(l.getCommand()),
+            line -> {
+                if (line.contains("BindException") ||
+                    line.contains(Agent.getText(AgentConfigurationError.CONNECTOR_SERVER_IO_ERROR))) {
+                    portUnavailable.set(true);
+                } else {
+                    output.append(line).append('\n');
+                    c.accept(line);
+                }
+            }
+        );
+
+        p.waitFor();
+        dbg_print("[jcmd] --------");
+        if (portUnavailable.get()) {
+            String cmd = Arrays.asList(l.getCommand()).stream()
+                    .collect(
+                            Collectors.joining(" ", "", ": Unable to bind address")
+                    );
+            throw new BindException(cmd);
+        }
+
+        return output.toString();
+    }
+
+    private void dbg_print(String msg) {
+        if (verbose) {
+            System.out.println("DBG: " + msg);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/management/jmxremote/startstop/PortAllocator.java	Wed Mar 25 18:33:17 2015 +0100
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 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.util.Random;
+
+/**
+ * Dynamically allocates distinct ports from the ephemeral range 49152-65535
+ */
+class PortAllocator {
+    private final static int LOWER_BOUND = 49152;
+    private final static int UPPER_BOUND = 65535;
+
+    private final static Random RND = new Random(System.currentTimeMillis());
+
+    static int[] allocatePorts(final int numPorts) {
+        int[] ports = new int[numPorts];
+        for (int i = 0; i < numPorts; i++) {
+            int port = -1;
+            while (port == -1) {
+                port = RND.nextInt(UPPER_BOUND - LOWER_BOUND + 1) + LOWER_BOUND;
+                for (int j = 0; j < i; j++) {
+                    if (ports[j] == port) {
+                        port = -1;
+                        break;
+                    }
+                }
+            }
+            ports[i] = port;
+        }
+        return ports;
+    }
+}
--- a/jdk/test/sun/management/jmxremote/startstop/REMOTE_TESTING.txt	Mon Mar 30 10:10:19 2015 +0200
+++ b/jdk/test/sun/management/jmxremote/startstop/REMOTE_TESTING.txt	Wed Mar 25 18:33:17 2015 +0100
@@ -4,7 +4,7 @@
    on host 1
 4. run 
 
-   ${TESTJAVA}/bin/java -server JMXStartStopDoSomething \
+   ${TESTJAVA}/bin/java -server TestApp \
    -Dcom.sun.management.jmxremote.port=50234 \
    -Dcom.sun.management.jmxremote.rmi.port=50235 \
    -Dcom.sun.management.jmxremote.authenticate=false \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/management/jmxremote/startstop/TestApp.java	Wed Mar 25 18:33:17 2015 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 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;
+
+public class TestApp {
+    public static void doSomething() throws IOException{
+        int r = System.in.read();
+        System.out.println("read: " + r);
+    }
+
+    public static void main(String args[]) throws Exception {
+        System.out.println("main enter");
+        System.out.flush();
+        doSomething();
+        System.out.println("main exit");
+    }
+}