4

我正在使用 JDK9 的 JShell API。

目前,JShell API 会在内部自动启动远程 JVM。我不想自动启动进程,而是想将这两个进程分开。

注意:我知道我可以更改虚拟机选项。但我想看看虚拟机是否可以在另一台机器上运行。

默认的执行控制最终在代码中到达这个地方

如果我指定启动,它会自动使用com.sun.jdi.CommandLineLaunch连接器,根据定义实际启动 java 程序。如果我指定 no-launch,它com.sun.jdi.SocketListen会像我预期的那样使用,一旦它启动了服务器套接字,它就会自动启动一个远程 vm 并连接到这个套接字。这我认为是出乎意料的。

我尝试过的其他事情,

Shell jshell = JShell.builder()
    .executionEngine("jdi:hostname(localhost),launch(false)")
    .build();
jshell.eval("1+2");

我希望这会失败或被卡住,直到一个单独的进程开始。

是否有另一种方法来指定连接器,或者不启动 JVM?(我也不对“本地”感兴趣)

一些简单的选项,例如能够指定com.sun.jdi.RawCommandLineLaunch为接受自定义命令的连接器或能够使用套接字侦听连接器并等待其他进程连接。

4

1 回答 1

3

编辑:我发现了一个错误 - 这不应该工作,因为 JDWP 没有附加到新的 VM。


是的,这可以通过快速破解来完成。我的描述的改编版本如下:

hack 依赖于替换由 JShell 启动的 VM 中的代理。可以通过remoteAgent 执行参数注入。

CLASSPATH="<injectpath>" ./jshell --execution "jdi:hostname(localhost),launch(false),remoteAgent(jshellhack.DumpPort),timeout(10000)"

新的虚拟代理必须以某种方式给出它应该连接的端口号。如果您不介意丑陋的黑客攻击,它可以像将其写入文件一样简单。您还可以利用命名管道。不过,我不会推荐这个用于任何严重的事情。

一个简单的代理:

package jshellhack;

import java.nio.file.*;
import java.lang.*;

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;

public class DumpPort {

    public static void main(String[] args) throws Exception {
        String str = args[0] + "\n";
        OpenOption[] opts = new OpenOption[] { CREATE, WRITE, TRUNCATE_EXISTING };
        Files.write(Paths.get("/tmp/jshellargs"), str.getBytes(), opts);
    }
}

您计算机上的 JShell 是 JDWP 通道的监听端。要重用现有的远程代理,您必须将所选端口反向转发到远程端。然后,您必须使用远程端口作为参数在远程端运行原始代理。

使用 SSH,它可能看起来像这样:

ssh -R "8000:localhost:$(cat /tmp/jshellargs)" ssh.example.org java jdk.jshell.execution.RemoteExecutionControl 8000

健壮的解决方案可能涉及克隆JdiDefaultExecutionControlJdiInitiator实现并使用远程连接功能扩展它们。

于 2018-01-06T23:00:46.723 回答