我正在使用JDK9的JShell API。
目前,JShell API在内部自动启动远程JVM。我不想自动启动流程,而是将这两个流程分开。
注意:我知道我可以更改VM选项。但是我想知道VM是否可以在不同的机器上运行。
默认执行控件最终reaches this place in code。
如果我指定启动,它会自动使用com.sun.jdi.CommandLineLaunch
连接器,它根据定义实际启动java程序。
如果我指定no-launch,它会像我期望的那样使用com.sun.jdi.SocketListen
,一旦启动服务器套接字,它就会自动starts a remote vm and connects to this socket。我认为这是出乎意料的。
我试过的其他事情,
Shell jshell = JShell.builder()
.executionEngine("jdi:hostname(localhost),launch(false)")
.build();
jshell.eval("1+2");
我希望这会失败或被卡住,直到一个单独的过程开始。
是否有另一种方法来指定连接器,或者不启动JVM? (我对' local'也不感兴趣)
一些简单的选项,例如能够指定com.sun.jdi.RawCommandLineLaunch
作为接受自定义命令的连接器,或者能够使用套接字侦听连接器并等待其他进程连接。
感谢。
答案 0 :(得分:2)
是的,这可以通过快速黑客来完成。 my description的改编版本如下:
hack依赖于替换JShell启动的VM中的代理。它可以通过remoteAgent
execution parameter注入。
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
more robust solution可能涉及克隆JdiDefaultExecutionControl和JdiInitiator实施并使用远程连接功能扩展它们。