1/*
2 * Copyright (c) 2014, 2015, 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
24import com.sun.tools.attach.AttachOperationFailedException;
25import com.sun.tools.attach.VirtualMachine;
26
27import java.io.File;
28import java.io.FileWriter;
29import java.util.Properties;
30import java.util.HashMap;
31
32import javax.management.remote.JMXServiceURL;
33import javax.management.remote.JMXConnector;
34import javax.management.remote.JMXConnectorFactory;
35
36import jdk.testlibrary.ProcessThread;
37import jdk.testlibrary.Utils;
38
39/*
40 * @test
41 * @summary Test for VirtualMachine.startManagementAgent and VirtualMachine.startLocalManagementAgent
42 *
43 * @library /lib/testlibrary
44 * @modules java.management
45 *          jdk.attach
46 *          jdk.jartool/sun.tools.jar
47 *
48 * @run build Application SimpleProvider jdk.testlibrary.*
49 * @run main/timeout=300 StartManagementAgent
50 */
51
52/*
53 * This test is not meant to test all possible configuration parameters to
54 * the JMX agent, there are other tests for that. This test makes sure it is
55 * possible to start the agent via attach.
56 */
57public class StartManagementAgent {
58    public static void main(String[] args) throws Throwable {
59        ProcessThread processThread = null;
60        try {
61            System.out.println("Starting test application");
62            processThread = RunnerUtil.startApplication();
63            System.out.println("Application started");
64            runTests(processThread.getPid());
65        } catch (Throwable t) {
66            System.out.println("StartManagementAgent got unexpected exception: " + t);
67            t.printStackTrace();
68            throw t;
69        } finally {
70            // Make sure the Application process is stopped.
71            RunnerUtil.stopApplication(processThread);
72        }
73    }
74
75    private static void basicTests(VirtualMachine vm) throws Exception {
76
77        // Try calling with null argument
78        boolean exception = false;
79        try {
80            System.out.println("Starting management agent with null");
81            vm.startManagementAgent(null);
82        } catch (NullPointerException e) {
83            exception = true;
84        }
85        if (!exception) {
86            throw new Exception("startManagementAgent(null) should throw NPE");
87        }
88
89        // Try calling with a property value with a space in it
90        Properties p = new Properties();
91        File f = new File("file with space");
92        try (FileWriter fw = new FileWriter(f)) {
93            fw.write("com.sun.management.jmxremote.port=apa");
94        }
95        p.put("com.sun.management.config.file", f.getAbsolutePath());
96        try {
97            System.out.println("Starting management agent with bogus port");
98            vm.startManagementAgent(p);
99        } catch(AttachOperationFailedException ex) {
100            // We expect parsing of "apa" above to fail, but if the file path
101            // can't be read we get a different exception message
102            if (!ex.getMessage().contains("Invalid com.sun.management.jmxremote.port number")) {
103                throw ex;
104            }
105            ex.printStackTrace(System.err);
106        } catch (Throwable t) {
107            t.printStackTrace(System.err);
108            throw t;
109        }
110    }
111
112    private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
113        "com.sun.management.jmxremote.localConnectorAddress";
114
115    private static final int MAX_RETRIES = 10;
116
117    public static void runTests(long pid) throws Exception {
118        VirtualMachine vm = VirtualMachine.attach(""+pid);
119        try {
120
121            basicTests(vm);
122
123            testLocalAgent(vm);
124
125            // we retry the remote case several times in case the error
126            // was caused by a port conflict
127            int i = 0;
128            boolean success = false;
129            do {
130                try {
131                    System.err.println("Trying remote agent. Try #" + i);
132                    testRemoteAgent(vm);
133                    System.err.println("Successfully connected to remote agent");
134                    success = true;
135                } catch(Exception ex) {
136                    System.err.println("testRemoteAgent failed with exception:");
137                    ex.printStackTrace();
138                    System.err.println("Retrying.");
139                }
140                i++;
141            } while(!success && i < MAX_RETRIES);
142            if (!success) {
143                throw new Exception("testRemoteAgent failed after " + MAX_RETRIES + " tries");
144            }
145        } finally {
146            System.err.println("Detaching from VM ...");
147            vm.detach();
148            System.err.println("Detached");
149        }
150    }
151
152    public static void testLocalAgent(VirtualMachine vm) throws Exception {
153        System.out.println("Getting VM properties");
154        Properties agentProps = vm.getAgentProperties();
155        String address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
156        if (address != null) {
157            throw new Exception("Local management agent already started");
158        }
159
160        System.out.println("Starting local agent");
161
162        String result = vm.startLocalManagementAgent();
163
164        System.out.println("Agent started");
165
166        // try to parse the return value as a JMXServiceURL
167        new JMXServiceURL(result);
168
169        agentProps = vm.getAgentProperties();
170        address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
171        if (address == null) {
172            throw new Exception("Local management agent could not be started");
173        }
174    }
175
176    public static void testRemoteAgent(VirtualMachine vm) throws Exception {
177        int port = Utils.getFreePort();
178
179        // try to connect - should fail
180        tryConnect(port, false);
181
182        // start agent
183        System.out.println("Starting agent on port: " + port);
184        Properties mgmtProps = new Properties();
185        mgmtProps.put("com.sun.management.jmxremote.port", port);
186        mgmtProps.put("com.sun.management.jmxremote.authenticate", "false");
187        mgmtProps.put("com.sun.management.jmxremote.ssl", "false");
188
189        System.err.println("Starting management agent ...");
190        vm.startManagementAgent(mgmtProps);
191        System.err.println("Started");
192
193        // try to connect - should work
194        tryConnect(port, true);
195
196        // try to start again - should fail
197        boolean exception = false;
198        try {
199            System.err.println("Starting management agent second time ...");
200            vm.startManagementAgent(mgmtProps);
201            System.err.println("Started");
202        } catch(AttachOperationFailedException ex) {
203            // expected
204            System.err.println("Got expected exception: " + ex.getMessage());
205            exception = true;
206        }
207        if (!exception) {
208            throw new Exception("Expected the second call to vm.startManagementAgent() to fail");
209        }
210    }
211
212    private static void tryConnect(int port, boolean shouldSucceed) throws Exception {
213        String jmxUrlStr =
214            String.format(
215                "service:jmx:rmi:///jndi/rmi://localhost:%d/jmxrmi",
216                port);
217        JMXServiceURL url = new JMXServiceURL(jmxUrlStr);
218        HashMap<String, ?> env = new HashMap<>();
219
220        boolean succeeded;
221        try {
222            System.err.println("Trying to connect to " + jmxUrlStr);
223            JMXConnector c = JMXConnectorFactory.connect(url, env);
224            System.err.println("Connected, getting MBeanServerConnection");
225            c.getMBeanServerConnection();
226            System.err.println("Success");
227            succeeded = true;
228        } catch(Exception ex) {
229            ex.printStackTrace(System.err);
230            succeeded = false;
231        }
232        if (succeeded && !shouldSucceed) {
233            throw new Exception("Could connect to agent, but should not have been possible");
234        }
235        if (!succeeded && shouldSucceed) {
236            throw new Exception("Could not connect to agent");
237        }
238    }
239}
240