Java - 如何从新进程线程调用 Java main()
Java - how to call Java main() from a new process thread
我正在尝试为应用程序构建集成测试 (IT)。该应用程序的中心是一个用 Java 编写的服务器,它设置一个消息队列,从中轮询在特定端口号上发送给它的消息。我想编写一个集成测试,它在此 server/port-number 处触发一些消息并测试响应。
下面是我在 Intellij 中手动启动服务器时 运行 的 VM 参数的完整列表。我可以通过这种方式启动服务器,然后向其发送测试消息,但我想将其转换为 IT 测试,以便我可以 start/stop 在测试开始和结束时以编程方式启动服务器。
我遇到的问题是我不知道如何从我的测试中启动服务器应用程序 class。所以问的再直白一点,一个class的Javamain()如何在自己的进程线程中启动。我在 Intellij (2019.1) 和 Java 中工作 8. 我应该使用 ProcessBuilder 还是 ExecutorService?
我想我可以对某些 VM 参数使用 System.setProperty
,但不确定如何指定 -XX 参数...所以这将是这个问题的第二部分。
-Djava.endorsed.dirs=/Users/xxx/dev/src/repo/xxx/myapp/target/classes/lib/endorsed
-Dmyapp.home=/private/var/tmp/myapp
-Dmyapp.log.dir=/private/var/tmp
-Dmyapp.env=/Users/xxx/dev/src/repo/xxx/myapp/target/classes/etc/examples/environment-apa.sh
-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties
-server
-XX:CompileThreshold=2500
-XX:+UseFastAccessorMethods
-Xss256k
-Xmx1g
-Xms512m
我试过使用 ExecutorService
来实现这个
public class ServerTest {
@Test
public void shouldStartServerOk() {
try{
startServer();
}catch(Exception e){
e.printStackTrace();
fail();
}
}
private void startServer(){
ExecutorService executor = Executors.newFixedThreadPool(1);
Runnable runnableTask = new Runnable() {
@Override
public void run() {
String [] args = new String[0];
try {
System.setProperty("java.endorsed.dirs", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed");
System.setProperty("myapp.home", "/private/var/tmp/myapp");
System.setProperty("myapp.log.dir", "/private/var/tmp");
System.setProperty("myapp.env", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh");
System.setProperty("simplelogger.properties", "/private/var/tmp/myapp/etc/simplelogger.properties");
System.setProperty("-server", "TRUE");
MyApp.main(args);
} catch (Exception e) {
e.printStackTrace();
}
}
};
executor.execute(runnableTask);
// shut down the executor manually
//executor.shutdown();
}
但这似乎不起作用,尽管测试确实完全绿色。当我调试进程时,流程不会进入 MyApp.main(args)。奇怪的是,当我只是在 ExecutorService 之外单独尝试 运行ning MyApp.main(args) 然后它开始并且 运行 很好,直到我在我的 IDE 中点击停止。这是我想要 Start/Stop 流程的额外能力。
更新一:
根据@dmitrievanthony 和@RealSkeptic 的评论,我尝试根据 SO 问题Executing a Java application in a separate process/636367、
public final class JavaProcess {
private JavaProcess() {}
public static int exec(Class klass) throws IOException,
InterruptedException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getName();
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
className
);
Map<String, String> env = processBuilder.environment();
env.put("java.endorsed.dirs", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed");
env.put("myapp.home", "/private/var/tmp/myapp");
env.put("myapp.log.dir", "/private/var/tmp");
env.put("myapp.env", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh");
env.put("simplelogger.properties", "/private/var/tmp/myapp/etc/simplelogger.properties");
env.put("-XX:CompileThreshold", "2500");
env.put("-XX:+UseFastAccessorMethods", "");
env.put("-Xss256k", "");
env.put("-Xmx1g", "");
env.put("-Xms512m", "");
Process process = processBuilder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
并在我的 myAppIT 测试中调用它 class 作为 int status = JavaProcess.exec(MyAapp.class);
我现在可以看到我的 class "MyApp" 开始了 - 并且可以确认流程正在 运行 进入我的 MyApp.main() class .现在的问题是我在 ProcessBuilder 中设置的 System.env 变量在被调用程序中似乎不可用,即。当我打印到日志时 System.getProperty("myapp.home") 它返回空值,即使我可以确认它是按照代码所示设置的 - 有人对此有任何想法吗?
UPDATE-2:我正在尝试实施@RealSkeptic 的建议,并以与传递命令行参数类似的方式传递参数,如下面的代码片段所示。现在我得到一个例外
Error: Could not find or load main class xxx.xxx.xxx.xxx.MyApp -Djava.endorsed.dirs=.Users.xxx.dev.src.gitlab.myapp.myapp.target.classes.lib.endorsed
我看到的一个问题是路径的正斜杠已被翻译为“.”。路径应该是 Djava.endorsed.dirs=/Users/xxx/dev/src/gitlab/myapp/myapp/target/classes/lib/endorsed
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
className + " " +
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed " +
"-Dmyapp.home=/private/var/tmp/myapp " +
"-Dmyapp.log.dir=/private/var/tmp" +
"-Dmyapp.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh " +
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties " +
"-server " +
"-XX:CompileThreshold=2500 " +
"-XX:+UseFastAccessorMethods " +
"-Xss256k " +
"-Xmx1g " +
"-Xms512m"
);
Update-3 在@RealSkeptic 的最后评论之后我修改了我的代码(见下文),现在可以使用了。
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed",
"-Dmyapp.home=/private/var/tmp/myapp",
"-Dmyapp.log.dir=/private/var/tmp",
"-Dmyapp.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh",
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties ",
"-server",
"-XX:CompileThreshold=2500",
"-XX:+UseFastAccessorMethods",
"-Xss256k",
"-Xmx1g",
"-Xms512m",
className
);
以下是从我作为答案发布的 UPDATE-3 复制而来的。感谢那些做出回应的人,尤其是@RealSkeptic。
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed",
"-Drrc.home=/private/var/tmp/myapp",
"-Drrc.log.dir=/private/var/tmp",
"-Drrc.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh",
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties ",
"-server",
"-XX:CompileThreshold=2500",
"-XX:+UseFastAccessorMethods",
"-Xss256k",
"-Xmx1g",
"-Xms512m",
className
);
我重构了上面的内容,将每个参数放入一个列表中,因此对 ProcessBuilder 的调用减少为,
ProcessBuilder processBuilder = new ProcessBuilder(arguments);
Process process = processBuilder.inheritIO().start();
要停止您只需要调用的过程
process.destroy();
我正在尝试为应用程序构建集成测试 (IT)。该应用程序的中心是一个用 Java 编写的服务器,它设置一个消息队列,从中轮询在特定端口号上发送给它的消息。我想编写一个集成测试,它在此 server/port-number 处触发一些消息并测试响应。
下面是我在 Intellij 中手动启动服务器时 运行 的 VM 参数的完整列表。我可以通过这种方式启动服务器,然后向其发送测试消息,但我想将其转换为 IT 测试,以便我可以 start/stop 在测试开始和结束时以编程方式启动服务器。
我遇到的问题是我不知道如何从我的测试中启动服务器应用程序 class。所以问的再直白一点,一个class的Javamain()如何在自己的进程线程中启动。我在 Intellij (2019.1) 和 Java 中工作 8. 我应该使用 ProcessBuilder 还是 ExecutorService?
我想我可以对某些 VM 参数使用 System.setProperty
,但不确定如何指定 -XX 参数...所以这将是这个问题的第二部分。
-Djava.endorsed.dirs=/Users/xxx/dev/src/repo/xxx/myapp/target/classes/lib/endorsed
-Dmyapp.home=/private/var/tmp/myapp
-Dmyapp.log.dir=/private/var/tmp
-Dmyapp.env=/Users/xxx/dev/src/repo/xxx/myapp/target/classes/etc/examples/environment-apa.sh
-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties
-server
-XX:CompileThreshold=2500
-XX:+UseFastAccessorMethods
-Xss256k
-Xmx1g
-Xms512m
我试过使用 ExecutorService
public class ServerTest {
@Test
public void shouldStartServerOk() {
try{
startServer();
}catch(Exception e){
e.printStackTrace();
fail();
}
}
private void startServer(){
ExecutorService executor = Executors.newFixedThreadPool(1);
Runnable runnableTask = new Runnable() {
@Override
public void run() {
String [] args = new String[0];
try {
System.setProperty("java.endorsed.dirs", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed");
System.setProperty("myapp.home", "/private/var/tmp/myapp");
System.setProperty("myapp.log.dir", "/private/var/tmp");
System.setProperty("myapp.env", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh");
System.setProperty("simplelogger.properties", "/private/var/tmp/myapp/etc/simplelogger.properties");
System.setProperty("-server", "TRUE");
MyApp.main(args);
} catch (Exception e) {
e.printStackTrace();
}
}
};
executor.execute(runnableTask);
// shut down the executor manually
//executor.shutdown();
}
但这似乎不起作用,尽管测试确实完全绿色。当我调试进程时,流程不会进入 MyApp.main(args)。奇怪的是,当我只是在 ExecutorService 之外单独尝试 运行ning MyApp.main(args) 然后它开始并且 运行 很好,直到我在我的 IDE 中点击停止。这是我想要 Start/Stop 流程的额外能力。
更新一: 根据@dmitrievanthony 和@RealSkeptic 的评论,我尝试根据 SO 问题Executing a Java application in a separate process/636367、
public final class JavaProcess {
private JavaProcess() {}
public static int exec(Class klass) throws IOException,
InterruptedException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getName();
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
className
);
Map<String, String> env = processBuilder.environment();
env.put("java.endorsed.dirs", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed");
env.put("myapp.home", "/private/var/tmp/myapp");
env.put("myapp.log.dir", "/private/var/tmp");
env.put("myapp.env", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh");
env.put("simplelogger.properties", "/private/var/tmp/myapp/etc/simplelogger.properties");
env.put("-XX:CompileThreshold", "2500");
env.put("-XX:+UseFastAccessorMethods", "");
env.put("-Xss256k", "");
env.put("-Xmx1g", "");
env.put("-Xms512m", "");
Process process = processBuilder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
并在我的 myAppIT 测试中调用它 class 作为 int status = JavaProcess.exec(MyAapp.class);
我现在可以看到我的 class "MyApp" 开始了 - 并且可以确认流程正在 运行 进入我的 MyApp.main() class .现在的问题是我在 ProcessBuilder 中设置的 System.env 变量在被调用程序中似乎不可用,即。当我打印到日志时 System.getProperty("myapp.home") 它返回空值,即使我可以确认它是按照代码所示设置的 - 有人对此有任何想法吗?
UPDATE-2:我正在尝试实施@RealSkeptic 的建议,并以与传递命令行参数类似的方式传递参数,如下面的代码片段所示。现在我得到一个例外
Error: Could not find or load main class xxx.xxx.xxx.xxx.MyApp -Djava.endorsed.dirs=.Users.xxx.dev.src.gitlab.myapp.myapp.target.classes.lib.endorsed
我看到的一个问题是路径的正斜杠已被翻译为“.”。路径应该是 Djava.endorsed.dirs=/Users/xxx/dev/src/gitlab/myapp/myapp/target/classes/lib/endorsed
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
className + " " +
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed " +
"-Dmyapp.home=/private/var/tmp/myapp " +
"-Dmyapp.log.dir=/private/var/tmp" +
"-Dmyapp.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh " +
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties " +
"-server " +
"-XX:CompileThreshold=2500 " +
"-XX:+UseFastAccessorMethods " +
"-Xss256k " +
"-Xmx1g " +
"-Xms512m"
);
Update-3 在@RealSkeptic 的最后评论之后我修改了我的代码(见下文),现在可以使用了。
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed",
"-Dmyapp.home=/private/var/tmp/myapp",
"-Dmyapp.log.dir=/private/var/tmp",
"-Dmyapp.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh",
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties ",
"-server",
"-XX:CompileThreshold=2500",
"-XX:+UseFastAccessorMethods",
"-Xss256k",
"-Xmx1g",
"-Xms512m",
className
);
以下是从我作为答案发布的 UPDATE-3 复制而来的。感谢那些做出回应的人,尤其是@RealSkeptic。
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed",
"-Drrc.home=/private/var/tmp/myapp",
"-Drrc.log.dir=/private/var/tmp",
"-Drrc.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh",
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties ",
"-server",
"-XX:CompileThreshold=2500",
"-XX:+UseFastAccessorMethods",
"-Xss256k",
"-Xmx1g",
"-Xms512m",
className
);
我重构了上面的内容,将每个参数放入一个列表中,因此对 ProcessBuilder 的调用减少为,
ProcessBuilder processBuilder = new ProcessBuilder(arguments);
Process process = processBuilder.inheritIO().start();
要停止您只需要调用的过程
process.destroy();