blob: 9876a54f093536ed35796f198252715353adba3e [file] [log] [blame]
ykantser472e0452013-10-30 13:44:40 +01001/*
2 * Copyright (c) 2013, 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 java.io.File;
25import java.net.UnknownHostException;
26import java.rmi.RemoteException;
27import java.rmi.registry.LocateRegistry;
28import java.rmi.registry.Registry;
29import java.util.Arrays;
30
31import static jdk.testlibrary.Asserts.*;
32import jdk.testlibrary.JDKToolLauncher;
33import jdk.testlibrary.OutputAnalyzer;
34import jdk.testlibrary.ProcessThread;
35import jdk.testlibrary.TestThread;
36import jdk.testlibrary.Utils;
37
38/**
39 * The base class for tests of jstatd.
40 *
41 * The test sequence for TestJstatdDefaults for example is:
42 * <pre>
43 * {@code
44 * // start jstatd process
45 * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy
46 *
47 * // run jps and verify its output
48 * jps -J-XX:+UsePerfData hostname
49 *
50 * // run jstat and verify its output
51 * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname 250 5
52 *
53 * // stop jstatd process and verify that no unexpected exceptions have been thrown
54 * }
55 * </pre>
56 */
57public final class JstatdTest {
58
59 /**
60 * jstat gcutil option: takes JSTAT_GCUTIL_SAMPLES samples at
61 * JSTAT_GCUTIL_INTERVAL_MS millisecond intervals
62 */
63 private static final int JSTAT_GCUTIL_SAMPLES = 5;
64 private static final int JSTAT_GCUTIL_INTERVAL_MS = 250;
65 private static final String JPS_OUTPUT_REGEX = "^\\d+\\s*.*";
66
67 private boolean useDefaultPort = true;
68 private String port;
69 private String serverName;
70 private String jstatdPid;
71 private boolean withExternalRegistry = false;
72
73 public void setServerName(String serverName) {
74 this.serverName = serverName;
75 }
76
77 public void setUseDefaultPort(boolean useDefaultPort) {
78 this.useDefaultPort = useDefaultPort;
79 }
80
81 public void setWithExternalRegistry(boolean withExternalRegistry) {
82 this.withExternalRegistry = withExternalRegistry;
83 }
84
85 /**
86 * Parse pid from jps output
87 */
88 private String parsePid(String tool, OutputAnalyzer output) throws Exception {
89 String[] lines = output.getOutput().split(Utils.NEW_LINE);
90 String pid = null;
91 int count = 0;
92 String processName = tool;
93 if (tool == "rmiregistry") {
94 processName = "registryimpl";
95 }
96 for (String line : lines) {
97 if (line.toLowerCase().matches("^\\d+\\s{1}" + processName + "$")) {
98 pid = line.split(" ")[0];
99 count++;
100 }
101 }
102 if (count > 1) {
103 throw new Exception("Expected one " + tool
104 + " process, got " + count + ". Test will be canceled.");
105 }
106
107 return pid;
108 }
109
110 private String getToolPid(String tool)
111 throws Exception {
112 OutputAnalyzer output = runJps();
113 return parsePid(tool, output);
114 }
115
116 private String waitOnTool(String tool, TestThread thread) throws Throwable {
117 while (true) {
118 String pid = getToolPid(tool);
119
120 if (pid != null) {
121 System.out.println(tool + " pid: " + pid);
122 return pid;
123 }
124
125 Throwable t = thread.getUncaught();
126 if (t != null) {
127 if (t.getMessage().contains(
128 "java.rmi.server.ExportException: Port already in use")) {
129 System.out.println("Port already in use. Trying to restart with a new one...");
130 Thread.sleep(100);
131 return null;
132 } else {
133 // Something unexpected has happened
134 throw new Throwable(t);
135 }
136 }
137
138 System.out.println("Waiting until " + tool + " is running...");
139 Thread.sleep(100);
140 }
141 }
142
143 private void log(String caption, String... cmd) {
144 System.out.println(Utils.NEW_LINE + caption + ":");
145 System.out.println(Arrays.toString(cmd).replace(",", ""));
146 }
147
148 private String getDestination() throws UnknownHostException {
149 String option = Utils.getHostname();
150 if (port != null) {
151 option += ":" + port;
152 }
153 if (serverName != null) {
154 option += "/" + serverName;
155 }
156 return option;
157 }
158
159 /**
160 * Depending on test settings command line can look like:
161 *
162 * jps -J-XX:+UsePerfData hostname
163 * jps -J-XX:+UsePerfData hostname:port
164 * jps -J-XX:+UsePerfData hostname/serverName
165 * jps -J-XX:+UsePerfData hostname:port/serverName
166 */
167 private OutputAnalyzer runJps() throws Exception {
168 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jps");
169 launcher.addVMArg("-XX:+UsePerfData");
170 launcher.addToolArg(getDestination());
171
172 String[] cmd = launcher.getCommand();
173 log("Start jps", cmd);
174
175 ProcessBuilder processBuilder = new ProcessBuilder(cmd);
176 OutputAnalyzer output = new OutputAnalyzer(processBuilder.start());
177 System.out.println(output.getOutput());
178
179 return output;
180 }
181
182 /**
183 * Verifies output form jps contains pids and programs' name information.
184 * The function will discard any lines that come before the first line with pid.
185 * This can happen if the JVM outputs a warning message for some reason
186 * before running jps.
187 *
188 * The output can look like:
189 * 35536 Jstatd
190 * 35417 Main
191 * 31103 org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
192 */
193 private void verifyJpsOutput(OutputAnalyzer output) throws Exception {
194 output.shouldHaveExitValue(0);
195 assertFalse(output.getOutput().isEmpty(), "Output should not be empty");
196
197 boolean foundFirstLineWithPid = false;
198 String[] lines = output.getOutput().split(Utils.NEW_LINE);
199 for (String line : lines) {
200 if (!foundFirstLineWithPid) {
201 foundFirstLineWithPid = line.matches(JPS_OUTPUT_REGEX);
202 continue;
203 }
204 assertTrue(line.matches(JPS_OUTPUT_REGEX),
205 "Output does not match the pattern" + Utils.NEW_LINE + line);
206 }
207 assertTrue(foundFirstLineWithPid, "Invalid output");
208 }
209
210 /**
211 * Depending on test settings command line can look like:
212 *
213 * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname 250 5
214 * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname:port 250 5
215 * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname/serverName 250 5
216 * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname:port/serverName 250 5
217 */
218 private OutputAnalyzer runJstat() throws Exception {
219 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstat");
220 launcher.addVMArg("-XX:+UsePerfData");
221 launcher.addVMArg("-Duser.language=en");
222 launcher.addToolArg("-gcutil");
223 launcher.addToolArg(jstatdPid + "@" + getDestination());
224 launcher.addToolArg(Integer.toString(JSTAT_GCUTIL_INTERVAL_MS));
225 launcher.addToolArg(Integer.toString(JSTAT_GCUTIL_SAMPLES));
226
227 String[] cmd = launcher.getCommand();
228 log("Start jstat", cmd);
229
230 ProcessBuilder processBuilder = new ProcessBuilder(cmd);
231 OutputAnalyzer output = new OutputAnalyzer(processBuilder.start());
232 System.out.println(output.getOutput());
233
234 return output;
235 }
236
237 private void verifyJstatOutput(OutputAnalyzer output)
238 throws Exception {
239 output.shouldHaveExitValue(0);
240 assertFalse(output.getOutput().isEmpty(), "Output should not be empty");
241
242 JstatGCUtilParser gcUtilParser = new JstatGCUtilParser(
243 output.getOutput());
244 gcUtilParser.parse(JSTAT_GCUTIL_SAMPLES);
245 }
246
247 private void runToolsAndVerify() throws Exception {
248 OutputAnalyzer output = runJps();
249 verifyJpsOutput(output);
250
251 output = runJstat();
252 verifyJstatOutput(output);
253 }
254
255 private Registry startRegistry()
256 throws InterruptedException, RemoteException {
257 Registry registry = null;
258 try {
259 System.out.println("Start rmiregistry on port " + port);
260 registry = LocateRegistry
261 .createRegistry(Integer.parseInt(port));
262 } catch (RemoteException e) {
263 if (e.getMessage().contains("Port already in use")) {
264 System.out.println("Port already in use. Trying to restart with a new one...");
265 Thread.sleep(100);
266 return null;
267 } else {
268 throw e;
269 }
270 }
271 return registry;
272 }
273
274 private void cleanUpThread(ProcessThread thread) throws Throwable {
275 if (thread != null) {
276 thread.stopProcess();
277 thread.joinAndThrow();
278 }
279 }
280
281 /**
282 * Depending on test settings command line can look like:
283 *
284 * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy
285 * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -p port
286 * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -n serverName
287 * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -p port -n serverName
288 */
289 private String[] getJstatdCmd() throws UnknownHostException {
290 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstatd");
291 launcher.addVMArg("-XX:+UsePerfData");
292 String testSrc = System.getProperty("test.src");
293 File policy = new File(testSrc, "all.policy");
294 launcher.addVMArg("-Djava.security.policy=" + policy.getAbsolutePath());
295 if (port != null) {
296 launcher.addToolArg("-p");
297 launcher.addToolArg(port);
298 }
299 if (serverName != null) {
300 launcher.addToolArg("-n");
301 launcher.addToolArg(serverName);
302 }
303
304 String[] cmd = launcher.getCommand();
305 log("Start jstatd", cmd);
306 return cmd;
307 }
308
309 private ProcessThread tryToSetupJstatdProcess() throws Throwable {
310 ProcessThread jstatdThread = new ProcessThread("Jstatd-Thread",
311 getJstatdCmd());
312 try {
313 jstatdThread.start();
314 // Make sure jstatd is up and running
315 jstatdPid = waitOnTool("jstatd", jstatdThread);
316 if (jstatdPid == null) {
317 // The port is already in use. Cancel and try with new one.
318 jstatdThread.stopProcess();
319 jstatdThread.join();
320 return null;
321 }
322 } catch (Throwable t) {
323 // Something went wrong in the product - clean up!
324 cleanUpThread(jstatdThread);
325 throw t;
326 }
327
328 return jstatdThread;
329 }
330
331 public void doTest() throws Throwable {
332 ProcessThread jstatdThread = null;
333 try {
334 while (jstatdThread == null) {
335 if (!useDefaultPort || withExternalRegistry) {
336 port = Integer.toString(Utils.getFreePort());
337 }
338
339 if (withExternalRegistry) {
340 Registry registry = startRegistry();
341 if (registry == null) {
342 // The port is already in use. Cancel and try with new one.
343 continue;
344 }
345 }
346
347 jstatdThread = tryToSetupJstatdProcess();
348 }
349
350 runToolsAndVerify();
351 } finally {
352 cleanUpThread(jstatdThread);
353 }
354 }
355
356}