1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import com.sun.tools.attach.AttachOperationFailedException;
  25 import com.sun.tools.attach.VirtualMachine;
  26 
  27 import java.io.File;
  28 import java.io.FileWriter;
  29 import java.util.Properties;
  30 import java.util.HashMap;
  31 
  32 import javax.management.remote.JMXServiceURL;
  33 import javax.management.remote.JMXConnector;
  34 import javax.management.remote.JMXConnectorFactory;
  35 
  36 import jdk.test.lib.thread.ProcessThread;
  37 import jdk.testlibrary.Utils;
  38 
  39 /*
  40  * @test
  41  * @summary Test for VirtualMachine.startManagementAgent and VirtualMachine.startLocalManagementAgent
  42  *
  43  * @library /lib/testlibrary
  44  * @library /test/lib
  45  * @modules java.management
  46  *          jdk.attach
  47  *          jdk.jartool/sun.tools.jar
  48  *
  49  * @run build Application SimpleProvider jdk.testlibrary.*
  50  * @run main/timeout=300 StartManagementAgent
  51  */
  52 
  53 /*
  54  * This test is not meant to test all possible configuration parameters to
  55  * the JMX agent, there are other tests for that. This test makes sure it is
  56  * possible to start the agent via attach.
  57  */
  58 public class StartManagementAgent {
  59     public static void main(String[] args) throws Throwable {
  60         ProcessThread processThread = null;
  61         try {
  62             System.out.println("Starting test application");
  63             processThread = RunnerUtil.startApplication();
  64             System.out.println("Application started");
  65             runTests(processThread.getPid());
  66         } catch (Throwable t) {
  67             System.out.println("StartManagementAgent got unexpected exception: " + t);
  68             t.printStackTrace();
  69             throw t;
  70         } finally {
  71             // Make sure the Application process is stopped.
  72             RunnerUtil.stopApplication(processThread);
  73         }
  74     }
  75 
  76     private static void basicTests(VirtualMachine vm) throws Exception {
  77 
  78         // Try calling with null argument
  79         boolean exception = false;
  80         try {
  81             System.out.println("Starting management agent with null");
  82             vm.startManagementAgent(null);
  83         } catch (NullPointerException e) {
  84             exception = true;
  85         }
  86         if (!exception) {
  87             throw new Exception("startManagementAgent(null) should throw NPE");
  88         }
  89 
  90         // Try calling with a property value with a space in it
  91         Properties p = new Properties();
  92         File f = new File("file with space");
  93         try (FileWriter fw = new FileWriter(f)) {
  94             fw.write("com.sun.management.jmxremote.port=apa");
  95         }
  96         p.put("com.sun.management.config.file", f.getAbsolutePath());
  97         try {
  98             System.out.println("Starting management agent with bogus port");
  99             vm.startManagementAgent(p);
 100         } catch(AttachOperationFailedException ex) {
 101             // We expect parsing of "apa" above to fail, but if the file path
 102             // can't be read we get a different exception message
 103             if (!ex.getMessage().contains("NumberFormatException: For input string: \"apa\"")) {
 104                 throw ex;
 105             }
 106             ex.printStackTrace(System.err);
 107         } catch (Throwable t) {
 108             t.printStackTrace(System.err);
 109             throw t;
 110         }
 111     }
 112 
 113     private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
 114         "com.sun.management.jmxremote.localConnectorAddress";
 115 
 116     private static final int MAX_RETRIES = 10;
 117 
 118     public static void runTests(long pid) throws Exception {
 119         VirtualMachine vm = VirtualMachine.attach(""+pid);
 120         try {
 121 
 122             basicTests(vm);
 123 
 124             testLocalAgent(vm);
 125 
 126             // we retry the remote case several times in case the error
 127             // was caused by a port conflict
 128             int i = 0;
 129             boolean success = false;
 130             do {
 131                 try {
 132                     System.err.println("Trying remote agent. Try #" + i);
 133                     testRemoteAgent(vm);
 134                     System.err.println("Successfully connected to remote agent");
 135                     success = true;
 136                 } catch(Exception ex) {
 137                     System.err.println("testRemoteAgent failed with exception:");
 138                     ex.printStackTrace();
 139                     System.err.println("Retrying.");
 140                 }
 141                 i++;
 142             } while(!success && i < MAX_RETRIES);
 143             if (!success) {
 144                 throw new Exception("testRemoteAgent failed after " + MAX_RETRIES + " tries");
 145             }
 146         } finally {
 147             System.err.println("Detaching from VM ...");
 148             vm.detach();
 149             System.err.println("Detached");
 150         }
 151     }
 152 
 153     public static void testLocalAgent(VirtualMachine vm) throws Exception {
 154         System.out.println("Getting VM properties");
 155         Properties agentProps = vm.getAgentProperties();
 156         String address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
 157         if (address != null) {
 158             throw new Exception("Local management agent already started");
 159         }
 160 
 161         System.out.println("Starting local agent");
 162 
 163         String result = vm.startLocalManagementAgent();
 164 
 165         System.out.println("Agent started");
 166 
 167         // try to parse the return value as a JMXServiceURL
 168         new JMXServiceURL(result);
 169 
 170         agentProps = vm.getAgentProperties();
 171         address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
 172         if (address == null) {
 173             throw new Exception("Local management agent could not be started");
 174         }
 175     }
 176 
 177     public static void testRemoteAgent(VirtualMachine vm) throws Exception {
 178         int port = Utils.getFreePort();
 179 
 180         // try to connect - should fail
 181         tryConnect(port, false);
 182 
 183         // start agent
 184         System.out.println("Starting agent on port: " + port);
 185         Properties mgmtProps = new Properties();
 186         mgmtProps.put("com.sun.management.jmxremote.port", port);
 187         mgmtProps.put("com.sun.management.jmxremote.authenticate", "false");
 188         mgmtProps.put("com.sun.management.jmxremote.ssl", "false");
 189 
 190         System.err.println("Starting management agent ...");
 191         vm.startManagementAgent(mgmtProps);
 192         System.err.println("Started");
 193 
 194         // try to connect - should work
 195         tryConnect(port, true);
 196 
 197         // try to start again - should fail
 198         boolean exception = false;
 199         try {
 200             System.err.println("Starting management agent second time ...");
 201             vm.startManagementAgent(mgmtProps);
 202             System.err.println("Started");
 203         } catch(AttachOperationFailedException ex) {
 204             // expected
 205             System.err.println("Got expected exception: " + ex.getMessage());
 206             exception = true;
 207         }
 208         if (!exception) {
 209             throw new Exception("Expected the second call to vm.startManagementAgent() to fail");
 210         }
 211     }
 212 
 213     private static void tryConnect(int port, boolean shouldSucceed) throws Exception {
 214         String jmxUrlStr =
 215             String.format(
 216                 "service:jmx:rmi:///jndi/rmi://localhost:%d/jmxrmi",
 217                 port);
 218         JMXServiceURL url = new JMXServiceURL(jmxUrlStr);
 219         HashMap<String, ?> env = new HashMap<>();
 220 
 221         boolean succeeded;
 222         try {
 223             System.err.println("Trying to connect to " + jmxUrlStr);
 224             JMXConnector c = JMXConnectorFactory.connect(url, env);
 225             System.err.println("Connected, getting MBeanServerConnection");
 226             c.getMBeanServerConnection();
 227             System.err.println("Success");
 228             succeeded = true;
 229         } catch(Exception ex) {
 230             ex.printStackTrace(System.err);
 231             succeeded = false;
 232         }
 233         if (succeeded && !shouldSucceed) {
 234             throw new Exception("Could connect to agent, but should not have been possible");
 235         }
 236         if (!succeeded && shouldSucceed) {
 237             throw new Exception("Could not connect to agent");
 238         }
 239     }
 240 }