I am playing with the JDK9's JShell API.
At present, the JShell API automatically launches the remote JVM internally. Instead of starting the process automatically, I want to separate the two process.
Note: I understand I can change the VM options. But I want to see if the VM can even be run on a different machine.
The default execution control eventually reaches this place in code.
If I specify launch, it automatically uses com.sun.jdi.CommandLineLaunch
connector, that actually launches the java program by definition.
If I specify no-launch, it uses com.sun.jdi.SocketListen
as I would expect, once it started the server socket, it automatically starts a remote vm and connects to this socket. This I think is unexpected.
Other things I tried,
Shell jshell = JShell.builder()
.executionEngine("jdi:hostname(localhost),launch(false)")
.build();
jshell.eval("1+2");
I would expect this to fail or be stuck until a separate process starts.
Is there an alternate way to specify the connectors, or not start a JVM? (I am not interested in 'local' either)
Some easy options like being able to specify com.sun.jdi.RawCommandLineLaunch
as the connector that accepts the custom command or being able to use the socket listen connector and wait for the other process to connect.
Thanks.
EDIT: I've found a bug in this - this shouldn't work, because the JDWP not attached to the new VM.
Yes, this can be accomplished with a quick hack. Adapted version of my description follows:
The hack relies on replacing the agent in the VM started by JShell. It can be injected through the remoteAgent
execution parameter.
CLASSPATH="<injectpath>" ./jshell --execution "jdi:hostname(localhost),launch(false),remoteAgent(jshellhack.DumpPort),timeout(10000)"
The new dummy agent has to somehow give out the port number it was supposed to connect to. If you don't mind ugly hacks, it can be as simple as writing it to a file. You can also take advantage of a named pipe. I wouldn't recommend this for anything serious, though.
A simple agent:
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 on your computer is the listening side of the JDWP channel. To reuse the existing remote agent, you have to reverse-forward a chosen port to the remote side. Then, you have to run the original agent on the remote side with the remote port as an argument.
Using SSH, it may look like this:
ssh -R "8000:localhost:$(cat /tmp/jshellargs)" ssh.example.org java jdk.jshell.execution.RemoteExecutionControl 8000
A more robust solution will probably involve cloning the JdiDefaultExecutionControl and JdiInitiator implementations and extending them with remote connection capability.
来源:https://stackoverflow.com/questions/46641749/is-there-an-alternate-way-to-launch-remote-jvm