diff --git a/jdk/src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java b/jdk/src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java index e08b11d6cb5..8dab8e040bf 100644 --- a/jdk/src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java +++ b/jdk/src/jdk.jcmd/share/classes/sun/tools/common/ProcessArgumentMatcher.java @@ -25,6 +25,7 @@ package sun.tools.common; +import java.lang.reflect.Module; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; @@ -45,13 +46,10 @@ import sun.jvmstat.monitor.VmIdentifier; * the process identifiers. */ public class ProcessArgumentMatcher { - private String excludeCls; - private String matchClass = null; - private String singlePid = null; - private boolean matchAll = false; + private String matchClass; + private String singlePid; - public ProcessArgumentMatcher(String pidArg, Class excludeClass) { - excludeCls = excludeClass.getName(); + public ProcessArgumentMatcher(String pidArg) { if (pidArg == null || pidArg.isEmpty()) { throw new IllegalArgumentException("Pid string is invalid"); } @@ -60,9 +58,7 @@ public class ProcessArgumentMatcher { } try { long pid = Long.parseLong(pidArg); - if (pid == 0) { - matchAll = true; - } else { + if (pid != 0) { singlePid = String.valueOf(pid); } } catch (NumberFormatException nfe) { @@ -70,7 +66,18 @@ public class ProcessArgumentMatcher { } } - private boolean check(VirtualMachineDescriptor vmd) { + private static String getExcludeStringFrom(Class excludeClass) { + if (excludeClass == null) { + return ""; + } + Module m = excludeClass.getModule(); + if (m.isNamed()) { + return m.getName() + "/" + excludeClass.getName(); + } + return excludeClass.getName(); + } + + private static boolean check(VirtualMachineDescriptor vmd, String excludeClass, String partialMatch) { String mainClass = null; try { VmIdentifier vmId = new VmIdentifier(vmd.id()); @@ -87,42 +94,55 @@ public class ProcessArgumentMatcher { // Handle this gracefully.... return false; } catch (MonitorException | URISyntaxException e) { - if (e.getMessage() != null) { - System.err.println(e.getMessage()); - } else { - Throwable cause = e.getCause(); - if ((cause != null) && (cause.getMessage() != null)) { - System.err.println(cause.getMessage()); - } else { - e.printStackTrace(); - } - } return false; } - if (mainClass.equals(excludeCls)) { + if (excludeClass != null && mainClass.equals(excludeClass)) { return false; } - if (matchAll || mainClass.indexOf(matchClass) != -1) { - return true; + if (partialMatch != null && mainClass.indexOf(partialMatch) == -1) { + return false; } - return false; + return true; } - public Collection getPids() { - Collection pids = new ArrayList<>(); - if (singlePid != null) { - pids.add(singlePid); - return pids; - } + private static Collection getSingleVMD(String pid) { + Collection vids = new ArrayList<>(); List vmds = VirtualMachine.list(); for (VirtualMachineDescriptor vmd : vmds) { - if (check(vmd)) { - pids.add(vmd.id()); + if (check(vmd, null, null)) { + if (pid.equals(vmd.id())) { + vids.add(vmd); + } } } - return pids; + return vids; } + + private static Collection getVMDs(Class excludeClass, String partialMatch) { + String excludeCls = getExcludeStringFrom(excludeClass); + Collection vids = new ArrayList<>(); + List vmds = VirtualMachine.list(); + for (VirtualMachineDescriptor vmd : vmds) { + if (check(vmd, excludeCls, partialMatch)) { + vids.add(vmd); + } + } + return vids; + } + + public Collection getVirtualMachineDescriptors(Class excludeClass) { + if (singlePid != null) { + return getSingleVMD(singlePid); + } else { + return getVMDs(excludeClass, matchClass); + } + } + + public Collection getVirtualMachineDescriptors() { + return this.getVirtualMachineDescriptors(null); + } + } diff --git a/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/Arguments.java b/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/Arguments.java index 80708f5aa37..d76b6f4728a 100644 --- a/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/Arguments.java +++ b/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/Arguments.java @@ -45,6 +45,8 @@ class Arguments { public Arguments(String[] args) { if (args.length == 0 || args[0].equals("-l")) { listProcesses = true; + /* list all processes */ + processString = "0"; return; } diff --git a/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java b/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java index ab77547a077..8a77c955fc8 100644 --- a/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java +++ b/jdk/src/jdk.jcmd/share/classes/sun/tools/jcmd/JCmd.java @@ -65,36 +65,37 @@ public class JCmd { System.exit(1); } + ProcessArgumentMatcher ap = null; + try { + ap = new ProcessArgumentMatcher(arg.getProcessString()); + } catch (IllegalArgumentException iae) { + System.err.println("Invalid pid '" + arg.getProcessString() + "' specified"); + System.exit(1); + } + if (arg.isListProcesses()) { - List vmds = VirtualMachine.list(); - for (VirtualMachineDescriptor vmd : vmds) { + for (VirtualMachineDescriptor vmd : ap.getVirtualMachineDescriptors(/* include jcmd in listing */)) { System.out.println(vmd.id() + " " + vmd.displayName()); } System.exit(0); } - Collection pids = Collections.emptyList(); - try { - ProcessArgumentMatcher ap = new ProcessArgumentMatcher(arg.getProcessString(), JCmd.class); - pids = ap.getPids(); - } catch (IllegalArgumentException iae) { - System.err.println("Invalid pid specified"); - System.exit(1); - } - if (pids.isEmpty()) { + Collection vids = ap.getVirtualMachineDescriptors(JCmd.class); + + if (vids.isEmpty()) { System.err.println("Could not find any processes matching : '" + arg.getProcessString() + "'"); System.exit(1); } boolean success = true; - for (String pid : pids) { - System.out.println(pid + ":"); + for (VirtualMachineDescriptor vid : vids) { + System.out.println(vid.id() + ":"); if (arg.isListCounters()) { - listCounters(pid); + listCounters(vid.id()); } else { try { - executeCommandForPid(pid, arg.getCommand()); + executeCommandForPid(vid.id(), arg.getCommand()); } catch(AttachOperationFailedException ex) { System.err.println(ex.getMessage()); success = false; diff --git a/jdk/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java b/jdk/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java index ca774122a17..2c955af2f4f 100644 --- a/jdk/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java +++ b/jdk/src/jdk.jcmd/share/classes/sun/tools/jinfo/JInfo.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.util.Collection; import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; @@ -50,6 +51,7 @@ final public class JInfo { boolean doFlag = false; boolean doFlags = false; boolean doSysprops = false; + int flag = -1; // Parse the options (arguments starting with "-" ) int optionCount = 0; @@ -67,65 +69,64 @@ final public class JInfo { if (arg.equals("-flag")) { doFlag = true; - continue; + // Consume the flag + if (optionCount < args.length) { + flag = optionCount++; + break; + } + usage(1); } if (arg.equals("-flags")) { doFlags = true; - continue; + break; } if (arg.equals("-sysprops")) { doSysprops = true; - continue; + break; } } - // Next we check the parameter count. -flag allows extra parameters int paramCount = args.length - optionCount; - if ((doFlag && paramCount != 2) || ((!doFlag && paramCount != 1))) { + if (paramCount != 1) { usage(1); } - if (!doFlag && !doFlags && !doSysprops) { - // Print flags and sysporps if no options given - ProcessArgumentMatcher ap = new ProcessArgumentMatcher(args[optionCount], JInfo.class); - Collection pids = ap.getPids(); - for (String pid : pids) { - if (pids.size() > 1) { - System.out.println("Pid:" + pid); - } - sysprops(pid); - System.out.println(); - flags(pid); - System.out.println(); - commandLine(pid); - } + String parg = args[optionCount]; + + ProcessArgumentMatcher ap = new ProcessArgumentMatcher(parg); + Collection vids = ap.getVirtualMachineDescriptors(JInfo.class); + + if (vids.isEmpty()) { + System.err.println("Could not find any processes matching : '" + parg + "'"); + System.exit(1); } - if (doFlag) { - ProcessArgumentMatcher ap = new ProcessArgumentMatcher(args[optionCount+1], JInfo.class); - Collection pids = ap.getPids(); - for (String pid : pids) { - if (pids.size() > 1) { - System.out.println("Pid:" + pid); - } - flag(pid, args[optionCount]); + for (VirtualMachineDescriptor vid : vids) { + if (vids.size() > 1) { + System.out.println("Pid:" + vid.id()); } - } - else if (doFlags || doSysprops) { - ProcessArgumentMatcher ap = new ProcessArgumentMatcher(args[optionCount], JInfo.class); - Collection pids = ap.getPids(); - for (String pid : pids) { - if (pids.size() > 1) { - System.out.println("Pid:" + pid); - } - if (doFlags) { - flags(pid); - } - else if (doSysprops) { - sysprops(pid); + if (!doFlag && !doFlags && !doSysprops) { + // Print flags and sysporps if no options given + sysprops(vid.id()); + System.out.println(); + flags(vid.id()); + System.out.println(); + commandLine(vid.id()); + } + if (doFlag) { + if (flag < 0) { + System.err.println("Missing flag"); + usage(1); } + flag(vid.id(), args[flag]); + } + if (doFlags) { + flags(vid.id()); + } + if (doSysprops) { + sysprops(vid.id()); } } } diff --git a/jdk/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java b/jdk/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java index 810a44eee6f..178d0a458a0 100644 --- a/jdk/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java +++ b/jdk/src/jdk.jcmd/share/classes/sun/tools/jmap/JMap.java @@ -32,6 +32,7 @@ import java.io.UnsupportedEncodingException; import java.util.Collection; import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; import com.sun.tools.attach.AttachNotSupportedException; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; @@ -89,10 +90,17 @@ public class JMap { // Here we handle the built-in options // As more options are added we should create an abstract tool class and // have a table to map the options - ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg, JMap.class); - Collection pids = ap.getPids(); - for (String pid : pids) { - if (pids.size() > 1) { + ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg); + Collection vids = ap.getVirtualMachineDescriptors(JMap.class); + + if (vids.isEmpty()) { + System.err.println("Could not find any processes matching : '" + pidArg + "'"); + System.exit(1); + } + + for (VirtualMachineDescriptor vid : vids) { + String pid = vid.id(); + if (vids.size() > 1) { System.out.println("Pid:" + pid); } if (option.equals("-histo")) { diff --git a/jdk/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java b/jdk/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java index 7d629e3854e..5e861cec69f 100644 --- a/jdk/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java +++ b/jdk/src/jdk.jcmd/share/classes/sun/tools/jstack/JStack.java @@ -29,6 +29,7 @@ import java.io.InputStream; import java.util.Collection; import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; import sun.tools.attach.HotSpotVirtualMachine; import sun.tools.common.ProcessArgumentMatcher; @@ -82,13 +83,19 @@ public class JStack { } else { params = new String[0]; } - ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg, JStack.class); - Collection pids = ap.getPids(); - for (String pid : pids) { - if (pids.size() > 1) { - System.out.println("Pid:" + pid); + ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg); + Collection vids = ap.getVirtualMachineDescriptors(JStack.class); + + if (vids.isEmpty()) { + System.err.println("Could not find any processes matching : '" + pidArg + "'"); + System.exit(1); + } + + for (VirtualMachineDescriptor vid : vids) { + if (vids.size() > 1) { + System.out.println("Pid:" + vid.id()); } - runThreadDump(pid, params); + runThreadDump(vid.id(), params); } } diff --git a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredVmUtil.java b/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredVmUtil.java index f2b6cd18684..7c1149bdc34 100644 --- a/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredVmUtil.java +++ b/jdk/src/jdk.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredVmUtil.java @@ -102,35 +102,42 @@ public class MonitoredVmUtil { */ public static String mainClass(MonitoredVm vm, boolean fullPath) throws MonitorException { - String commandLine = commandLine(vm); - String arg0 = commandLine; - - int firstSpace = commandLine.indexOf(' '); + String cmdLine = commandLine(vm); + int firstSpace = cmdLine.indexOf(' '); if (firstSpace > 0) { - arg0 = commandLine.substring(0, firstSpace); + cmdLine = cmdLine.substring(0, firstSpace); } - if (!fullPath) { + if (fullPath) { + return cmdLine; + } + /* + * Can't use File.separator() here because the separator for the target + * jvm may be different than the separator for the monitoring jvm. + * And we also strip embedded module e.g. "module/MainClass" + */ + int lastSlash = cmdLine.lastIndexOf("/"); + int lastBackslash = cmdLine.lastIndexOf("\\"); + int lastSeparator = lastSlash > lastBackslash ? lastSlash : lastBackslash; + if (lastSeparator > 0) { + cmdLine = cmdLine.substring(lastSeparator + 1); + } + + int lastPackageSeparator = cmdLine.lastIndexOf('.'); + if (lastPackageSeparator > 0) { + String lastPart = cmdLine.substring(lastPackageSeparator + 1); /* - * can't use File.separator() here because the separator - * for the target jvm may be different than the separator - * for the monitoring jvm. + * We could have a relative path "my.module" or + * a module called "my.module" and a jar file called "my.jar" or + * class named "jar" in package "my", e.g. "my.jar". + * We can never be sure here, but we assume *.jar is a jar file */ - int lastFileSeparator = arg0.lastIndexOf('/'); - if (lastFileSeparator > 0) { - return arg0.substring(lastFileSeparator + 1); - } - - lastFileSeparator = arg0.lastIndexOf('\\'); - if (lastFileSeparator > 0) { - return arg0.substring(lastFileSeparator + 1); - } - - int lastPackageSeparator = arg0.lastIndexOf('.'); - if (lastPackageSeparator > 0) { - return arg0.substring(lastPackageSeparator + 1); + if (lastPart.equals("jar")) { + return cmdLine; /* presumably a file name without path */ } + return lastPart; /* presumably a class name without package */ } - return arg0; + + return cmdLine; } /** diff --git a/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java b/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java index 6d8d59dd455..04701afbbd9 100644 --- a/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java +++ b/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java @@ -146,6 +146,16 @@ public class BasicLauncherTest { launch(expectedMessage, Arrays.asList(toolArgs)); } + public static void launchNotOSX(String expectedMessage, String... toolArgs) + throws IOException { + + if (Platform.isOSX()) { + // Coredump stackwalking is not implemented for Darwin + System.out.println("This test is not expected to work on OS X. Skipping"); + return; + } + } + public static void testHeapDump() throws IOException { File dump = new File("jhsdb.jmap.dump." + System.currentTimeMillis() + ".hprof"); @@ -172,7 +182,7 @@ public class BasicLauncherTest { launchCLHSDB(); - launch("No deadlocks found", "jstack"); + launchNotOSX("No deadlocks found", "jstack"); launch("compiler detected", "jmap"); launch("Java System Properties", "jinfo"); launch("java.threads", "jsnap"); diff --git a/jdk/test/sun/tools/jinfo/JInfoTest.java b/jdk/test/sun/tools/jinfo/JInfoTest.java new file mode 100644 index 00000000000..4710cfb974a --- /dev/null +++ b/jdk/test/sun/tools/jinfo/JInfoTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2005, 2016, 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.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.io.IOException; + +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.apps.LingeredApp; + +/* + * @test + * @summary Unit test for jinfo utility + * @modules java.base/jdk.internal.misc + * @library /test/lib/share/classes + * @build jdk.test.lib.* + * @build jdk.test.lib.apps.* + * @build jdk.test.lib.process.* + * @run main JInfoTest + */ +public class JInfoTest { + + private static ProcessBuilder processBuilder = new ProcessBuilder(); + + public static void main(String[] args) throws Exception { + classNameMatch(); + setMultipleFlags(); + setFlag(); + } + + private static void setFlag() throws Exception { + System.out.println("#### setFlag ####"); + LingeredApp app1 = new JInfoTestLingeredApp(); + LingeredApp app2 = new JInfoTestLingeredApp(); + try { + ArrayList params = new ArrayList(); + LingeredApp.startApp(params, app1); + LingeredApp.startApp(params, app2); + OutputAnalyzer output = jinfo("-flag", "MinHeapFreeRatio=1", "JInfoTestLingeredApp"); + output.shouldHaveExitValue(0); + output = jinfo("-flag", "MinHeapFreeRatio", "JInfoTestLingeredApp"); + output.shouldHaveExitValue(0); + documentMatch(output.getStdout(), ".*MinHeapFreeRatio=1.*MinHeapFreeRatio=1.*"); + } finally { + JInfoTestLingeredApp.stopApp(app1); + JInfoTestLingeredApp.stopApp(app2); + } + } + + private static void setMultipleFlags() throws Exception { + System.out.println("#### setMultipleFlags ####"); + OutputAnalyzer output = jinfo("-sysprops", "-flag", "MinHeapFreeRatio=1", "-flags", "JInfoTestLingeredApp"); + output.shouldHaveExitValue(1); + } + + private static void classNameMatch() throws Exception { + System.out.println("#### classNameMatch ####"); + LingeredApp app1 = new JInfoTestLingeredApp(); + LingeredApp app2 = new JInfoTestLingeredApp(); + try { + ArrayList params = new ArrayList(); + LingeredApp.startApp(params, app1); + LingeredApp.startApp(params, app2); + OutputAnalyzer output = jinfo("JInfoTestLingeredApp"); + output.shouldHaveExitValue(0); + // "HotSpot(TM)" written once per proc + documentMatch(output.getStdout(), ".*HotSpot\\(TM\\).*HotSpot\\(TM\\).*"); + } finally { + JInfoTestLingeredApp.stopApp(app1); + JInfoTestLingeredApp.stopApp(app2); + } + } + + private static void documentMatch(String data, String pattern){ + Matcher matcher = Pattern.compile(pattern, Pattern.DOTALL).matcher(data); + if (!matcher.find()) { + throw new RuntimeException("'" + pattern + "' missing from stdout \n"); + } + } + + private static OutputAnalyzer jinfo(String... toolArgs) throws Exception { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jinfo"); + if (toolArgs != null) { + for (String toolArg : toolArgs) { + launcher.addToolArg(toolArg); + } + } + + processBuilder.command(launcher.getCommand()); + OutputAnalyzer output = ProcessTools.executeProcess(processBuilder); + + return output; + } +} + +// Sometime there is LingeredApp's from other test still around +class JInfoTestLingeredApp extends LingeredApp { +} + diff --git a/jdk/test/sun/tools/jps/TestJpsSanity.java b/jdk/test/sun/tools/jps/TestJpsSanity.java index 1395abf8d20..457a667a251 100644 --- a/jdk/test/sun/tools/jps/TestJpsSanity.java +++ b/jdk/test/sun/tools/jps/TestJpsSanity.java @@ -23,15 +23,17 @@ import jdk.testlibrary.Asserts; import jdk.testlibrary.OutputAnalyzer; +import jdk.test.lib.apps.LingeredApp; /* * @test * @summary This test verifies jps usage and checks that appropriate error message is shown * when running jps with illegal arguments. - * @library /lib/testlibrary + * @library /lib/testlibrary /test/lib/share/classes * @modules jdk.jartool/sun.tools.jar * java.management - * @build jdk.testlibrary.* JpsHelper + * java.base/jdk.internal.misc + * @build jdk.testlibrary.* jdk.test.lib.apps.* JpsHelper * @run driver TestJpsSanity */ public class TestJpsSanity { @@ -40,6 +42,42 @@ public class TestJpsSanity { testJpsUsage(); testJpsVersion(); testJpsUnknownHost(); + testJpsShort(); + testJpsLong(); + testJpsShortPkg(); + testJpsLongPkg(); + } + + private static void testJpsShort() throws Exception { + OutputAnalyzer output = JpsHelper.jps(); + output.shouldMatch("^[0-9]+ Jps$"); + } + + private static void testJpsLong() throws Exception { + OutputAnalyzer output = JpsHelper.jps("-l"); + output.shouldMatch("^[0-9]+ jdk\\.jcmd/sun\\.tools\\.jps\\.Jps$"); + } + + private static void testJpsShortPkg() throws Exception { + LingeredApp app = null; + try { + app = LingeredApp.startApp(); + OutputAnalyzer output = JpsHelper.jps(); + output.shouldMatch("^[0-9]+ LingeredApp$"); + } finally { + LingeredApp.stopApp(app); + } + } + + private static void testJpsLongPkg() throws Exception { + LingeredApp app = null; + try { + app = LingeredApp.startApp(); + OutputAnalyzer output = JpsHelper.jps("-l"); + output.shouldMatch("^[0-9]+ jdk\\.test\\.lib\\.apps\\.LingeredApp$"); + } finally { + LingeredApp.stopApp(app); + } } private static void testJpsUsage() throws Exception {