【问题标题】:Java - how to call Java main() from a new process threadJava - 如何从新进程线程调用 Java main()
【发布时间】:2019-04-08 13:49:43
【问题描述】:

我正在尝试为应用程序构建集成测试 (IT)。该应用程序的中心有一个用 Java 编写的服务器,它设置一个消息队列,从该队列中轮询在特定端口号上发送给它的消息。我想编写一个集成测试,它会在此服务器/端口号上触发一些消息并测试响应。

以下是我在 Intellij 中手动启动服务器时运行的 VM 参数的完整列表。我可以通过这种方式启动服务器,然后向其发送测试消息,但我想将其转换为 IT 测试,以便在测试开始和结束时以编程方式启动/停止服务器。

我遇到的问题是我不知道如何从我的测试类中启动服务器应用程序。所以问得更清楚一点,如何在自己的进程线程中启动一个类的Java main()。我在 Intellij (2019.1) 和 Java 8 中工作。我应该使用 ProcessBuilder 还是 ExecutorService ?

我想我可以将System.setProperty 用于某些 VM 参数,但不确定如何指定 -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();

    }

但这似乎不起作用,尽管测试确实完成了绿色。当我调试流程时,流程不会 Step-Into MyApp.main(args)。奇怪的是,当我尝试在 ExecutorService 之外自行运行 MyApp.main(args) 时,它会启动并运行良好,直到我在 IDE 中点击 Stop 为止。这是我想要启动/停止进程的额外功能的行为。

更新-1: 遵循来自@dmitrievanthony 和@RealSkeptic 的cmets,我尝试根据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 测试类中将其称为 int status = JavaProcess.exec(MyAapp.class);

我现在可以看到我的类“MyApp”正在启动 - 并且可以确认流程正在运行到我的 MyApp.main() 类中。现在的问题是我在 ProcessBuilder 中设置的 System.env 变量在被调用程序中似乎不可用,即。当我打印到日志 System.getProperty("myapp.home") 时,它返回 null 即使我可以确认它已按照代码所示进行设置 - 请问有人对此有任何想法吗?

UPDATE-2:我正在尝试实现@RealSkeptic 的建议,并以与传递命令行参数类似的方式传递参数,如下面的代码 sn-p 所示。现在我遇到了一个例外 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"
        );

在@RealSkeptic 的最后一条评论之后的Update-3 我修改了我的代码(见下文),现在可以正常工作了。

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
        );

【问题讨论】:

  • 您不打算在单独的进程中启动它吗?您传递所有参数 - 包括所有 -D-X 作为命令行参数 - 请参阅 ProcessBuilder
  • @RealSkeptic 谢谢 - 是的,我想在一个单独的过程中启动它。我将签出 ProcessBuilder
  • 您不能只在单独的进程中运行main()。要创建一个单独的进程,您需要启动 jvm,加载类,然后才启动 main()。你提到的参数最多也是jvm的参数,所以不能为已经运行的jvm设置,只能为新的。
  • 这些东西应该在命令列表中传递给进程,而不是在环境变量中。基本上你传递的东西就像你从命令行手动运行一样——但没有特殊的引用。
  • 如果这些参数是诸如-D...-XX... 等JVM 参数,则它应该在参数之后。所有JVM 参数都应该在类名之前。

标签: java integration-testing junit4 jvm-arguments


【解决方案1】:

以下内容是从我发布的 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
        );

我已经重构了上述内容,将每个参数放入一个 List 中,因此对 ProcessBuilder 的调用减少到,

ProcessBuilder processBuilder = new ProcessBuilder(arguments); Process process = processBuilder.inheritIO().start();

要停止进程,您只需要调用 process.destroy();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-24
    • 2013-05-16
    • 2011-04-05
    • 2010-11-07
    • 2020-01-09
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    相关资源
    最近更新 更多