# HG changeset patch # User jbachorik # Date 1427304797 -3600 # Node ID 22a5f09f4da9c6d652e5af33f1dc81f921bb5c1b # Parent e8999e87feae063b79a5af4f5538c815d0f3dc10 8023093: Add ManagementAgent.status diagnostic command Reviewed-by: sla diff -r e8999e87feae -r 22a5f09f4da9 jdk/src/java.management/share/classes/sun/management/Agent.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); diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java --- 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"); - } -} diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java --- 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 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 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 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()); diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/JMXStatusTest.java --- /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)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"); + } + } +} diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/ManagementAgentJcmd.java --- /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 ManagementAgent.stop` + * @return The JCMD output + * @throws IOException + * @throws InterruptedException + */ + public String stop() throws IOException, InterruptedException { + return jcmd(CMD_STOP); + } + + /** + * `jcmd ManagementAgent.start_local` + * @return The JCMD output + * @throws IOException + * @throws InterruptedException + */ + public String startLocal() throws IOException, InterruptedException { + return jcmd(CMD_START_LOCAL); + } + + /** + * `jcmd ManagementAgent.start ` + * @return The JCMD output + * @param params The arguments to ManagementAgent.start command + * @throws IOException + * @throws InterruptedException + */ + public String start(String ... params) throws IOException, InterruptedException { + return start(c->{}, params); + } + + /** + * `jcmd ManagementAgent.start ` + * @param c A string consumer used to inspect the jcmd output line-by-line + * @param params The arguments to ManagementAgent.start command + * @return The JCMD output + * @throws IOException + * @throws InterruptedException + */ + public String start(Consumer c, String ... params) throws IOException, InterruptedException { + List 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 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 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); + } + } +} diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/PortAllocator.java --- /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; + } +} diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/REMOTE_TESTING.txt --- 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 \ diff -r e8999e87feae -r 22a5f09f4da9 jdk/test/sun/management/jmxremote/startstop/TestApp.java --- /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"); + } +}