8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package jdk.jshell.execution;
26
27 import java.io.IOException;
28 import java.io.ObjectInput;
29 import java.io.ObjectOutput;
30 import java.io.ObjectOutputStream;
31 import java.io.OutputStream;
32 import java.net.ServerSocket;
33 import java.net.Socket;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.function.Consumer;
39 import com.sun.jdi.BooleanValue;
40 import com.sun.jdi.ClassNotLoadedException;
41 import com.sun.jdi.Field;
42 import com.sun.jdi.IncompatibleThreadStateException;
43 import com.sun.jdi.InvalidTypeException;
44 import com.sun.jdi.ObjectReference;
45 import com.sun.jdi.StackFrame;
46 import com.sun.jdi.ThreadReference;
47 import com.sun.jdi.VMDisconnectedException;
48 import com.sun.jdi.VirtualMachine;
49 import jdk.jshell.spi.ExecutionControl;
50 import jdk.jshell.spi.ExecutionEnv;
51 import static jdk.jshell.execution.Util.remoteInput;
52
53 /**
54 * The implementation of {@link jdk.jshell.spi.ExecutionControl} that the
55 * JShell-core uses by default.
56 * Launches a remote process -- the "remote agent".
57 * Interfaces to the remote agent over a socket and via JDI.
58 * Designed to work with {@link RemoteExecutionControl}.
59 *
60 * @author Robert Field
61 * @author Jan Lahoda
62 */
63 public class JDIDefaultExecutionControl extends JDIExecutionControl {
64
65 private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName();
66
67 private VirtualMachine vm;
68 private Process process;
69
70 private final Object STOP_LOCK = new Object();
71 private boolean userCodeRunning = false;
86 *
87 * @return the generator
88 */
89 public static ExecutionControl.Generator listen() {
90 return env -> create(env, false);
91 }
92
93 /**
94 * Creates an ExecutionControl instance based on a JDI
95 * {@code ListeningConnector} or {@code LaunchingConnector}.
96 *
97 * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
98 * commands and results. This socket also transports the user
99 * input/output/error.
100 *
101 * @param env the context passed by
102 * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
103 * @return the channel
104 * @throws IOException if there are errors in set-up
105 */
106 private static JDIDefaultExecutionControl create(ExecutionEnv env, boolean isLaunch) throws IOException {
107 try (final ServerSocket listener = new ServerSocket(0)) {
108 // timeout after 60 seconds
109 listener.setSoTimeout(60000);
110 int port = listener.getLocalPort();
111
112 // Set-up the JDI connection
113 JDIInitiator jdii = new JDIInitiator(port,
114 env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch);
115 VirtualMachine vm = jdii.vm();
116 Process process = jdii.process();
117
118 // Forward input to the remote agent
119 Util.forwardInputToRemote(env.userIn(), process.getOutputStream(),
120 ex -> debug(ex, "input forwarding failure"));
121
122 List<Consumer<String>> deathListeners = new ArrayList<>();
123 deathListeners.add(s -> env.closeDown());
124 Util.detectJDIExitEvent(vm, s -> {
125 for (Consumer<String> h : deathListeners) {
126 h.accept(s);
127 }
128 });
129
130 // Set-up the commands/reslts on the socket. Piggy-back snippet
131 // output.
132 Socket socket = listener.accept();
133 // out before in -- match remote creation so we don't hang
134 ObjectOutput cmdout = new ObjectOutputStream(socket.getOutputStream());
135 Map<String, OutputStream> io = new HashMap<>();
136 io.put("out", env.userOut());
137 io.put("err", env.userErr());
138 ObjectInput cmdin = remoteInput(socket.getInputStream(), io);
139 return new JDIDefaultExecutionControl(cmdout, cmdin, vm, process, deathListeners);
140 }
141 }
142
143 /**
144 * Create an instance.
145 *
146 * @param cmdout the output for commands
147 * @param cmdin the input for responses
148 */
149 private JDIDefaultExecutionControl(ObjectOutput cmdout, ObjectInput cmdin,
150 VirtualMachine vm, Process process, List<Consumer<String>> deathListeners) {
151 super(cmdout, cmdin);
152 this.vm = vm;
153 this.process = process;
154 deathListeners.add(s -> disposeVM());
155 }
156
157 @Override
158 public String invoke(String classname, String methodname)
159 throws RunException,
|
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package jdk.jshell.execution;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.ObjectInput;
30 import java.io.ObjectOutput;
31 import java.io.OutputStream;
32 import java.net.ServerSocket;
33 import java.net.Socket;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.function.Consumer;
39 import com.sun.jdi.BooleanValue;
40 import com.sun.jdi.ClassNotLoadedException;
41 import com.sun.jdi.Field;
42 import com.sun.jdi.IncompatibleThreadStateException;
43 import com.sun.jdi.InvalidTypeException;
44 import com.sun.jdi.ObjectReference;
45 import com.sun.jdi.StackFrame;
46 import com.sun.jdi.ThreadReference;
47 import com.sun.jdi.VMDisconnectedException;
48 import com.sun.jdi.VirtualMachine;
49 import jdk.jshell.spi.ExecutionControl;
50 import jdk.jshell.spi.ExecutionEnv;
51 import static jdk.jshell.execution.Util.remoteInputOutput;
52
53 /**
54 * The implementation of {@link jdk.jshell.spi.ExecutionControl} that the
55 * JShell-core uses by default.
56 * Launches a remote process -- the "remote agent".
57 * Interfaces to the remote agent over a socket and via JDI.
58 * Designed to work with {@link RemoteExecutionControl}.
59 *
60 * @author Robert Field
61 * @author Jan Lahoda
62 */
63 public class JDIDefaultExecutionControl extends JDIExecutionControl {
64
65 private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName();
66
67 private VirtualMachine vm;
68 private Process process;
69
70 private final Object STOP_LOCK = new Object();
71 private boolean userCodeRunning = false;
86 *
87 * @return the generator
88 */
89 public static ExecutionControl.Generator listen() {
90 return env -> create(env, false);
91 }
92
93 /**
94 * Creates an ExecutionControl instance based on a JDI
95 * {@code ListeningConnector} or {@code LaunchingConnector}.
96 *
97 * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
98 * commands and results. This socket also transports the user
99 * input/output/error.
100 *
101 * @param env the context passed by
102 * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
103 * @return the channel
104 * @throws IOException if there are errors in set-up
105 */
106 private static ExecutionControl create(ExecutionEnv env, boolean isLaunch) throws IOException {
107 try (final ServerSocket listener = new ServerSocket(0)) {
108 // timeout after 60 seconds
109 listener.setSoTimeout(60000);
110 int port = listener.getLocalPort();
111
112 // Set-up the JDI connection
113 JDIInitiator jdii = new JDIInitiator(port,
114 env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch);
115 VirtualMachine vm = jdii.vm();
116 Process process = jdii.process();
117
118 List<Consumer<String>> deathListeners = new ArrayList<>();
119 deathListeners.add(s -> env.closeDown());
120 Util.detectJDIExitEvent(vm, s -> {
121 for (Consumer<String> h : deathListeners) {
122 h.accept(s);
123 }
124 });
125
126 // Set-up the commands/reslts on the socket. Piggy-back snippet
127 // output.
128 Socket socket = listener.accept();
129 // out before in -- match remote creation so we don't hang
130 OutputStream out = socket.getOutputStream();
131 Map<String, OutputStream> outputs = new HashMap<>();
132 outputs.put("out", env.userOut());
133 outputs.put("err", env.userErr());
134 Map<String, InputStream> input = new HashMap<>();
135 input.put("in", env.userIn());
136 return remoteInputOutput(socket.getInputStream(), out, outputs, input, (objIn, objOut) -> new JDIDefaultExecutionControl(objOut, objIn, vm, process, deathListeners));
137 }
138 }
139
140 /**
141 * Create an instance.
142 *
143 * @param cmdout the output for commands
144 * @param cmdin the input for responses
145 */
146 private JDIDefaultExecutionControl(ObjectOutput cmdout, ObjectInput cmdin,
147 VirtualMachine vm, Process process, List<Consumer<String>> deathListeners) {
148 super(cmdout, cmdin);
149 this.vm = vm;
150 this.process = process;
151 deathListeners.add(s -> disposeVM());
152 }
153
154 @Override
155 public String invoke(String classname, String methodname)
156 throws RunException,
|