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 jdk.jshell.spi.ExecutionEnv;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.ObjectInput;
31 import java.io.ObjectInputStream;
32 import java.io.ObjectOutput;
33 import java.io.ObjectOutputStream;
34 import java.io.OutputStream;
35 import java.util.Map;
36 import java.util.Map.Entry;
37 import java.util.function.Consumer;
38 import com.sun.jdi.VirtualMachine;
39 import jdk.jshell.spi.ExecutionControl;
40
41
42 /**
43 * Miscellaneous utility methods for setting-up implementations of
44 * {@link ExecutionControl}. Particularly implementations with remote
45 * execution.
46 *
47 * @author Jan Lahoda
48 * @author Robert Field
49 */
50 public class Util {
51
52 // never instanciated
53 private Util() {}
54
55 /**
56 * Create a composite {@link ExecutionControl.Generator} instance that, when
57 * generating, will try each specified generator until successfully creating
82 };
83 }
84
85 /**
86 * Forward commands from the input to the specified {@link ExecutionControl}
87 * instance, then responses back on the output.
88 * @param ec the direct instance of {@link ExecutionControl} to process commands
89 * @param in the command input
90 * @param out the command response output
91 */
92 public static void forwardExecutionControl(ExecutionControl ec,
93 ObjectInput in, ObjectOutput out) {
94 new ExecutionControlForwarder(ec, in, out).commandLoop();
95 }
96
97 /**
98 * Forward commands from the input to the specified {@link ExecutionControl}
99 * instance, then responses back on the output.
100 * @param ec the direct instance of {@link ExecutionControl} to process commands
101 * @param inStream the stream from which to create the command input
102 * @param outStream the stream that will carry {@code System.out},
103 * {@code System.err}, any specified auxiliary channels, and the
104 * command response output.
105 * @param streamMap a map between names of additional streams to carry and setters
106 * for the stream
107 * @throws IOException if there are errors using the passed streams
108 */
109 public static void forwardExecutionControlAndIO(ExecutionControl ec,
110 InputStream inStream, OutputStream outStream,
111 Map<String, Consumer<OutputStream>> streamMap) throws IOException {
112 ObjectInputStream cmdIn = new ObjectInputStream(inStream);
113 for (Entry<String, Consumer<OutputStream>> e : streamMap.entrySet()) {
114 e.getValue().accept(multiplexingOutputStream(e.getKey(), outStream));
115 }
116 ObjectOutputStream cmdOut = new ObjectOutputStream(multiplexingOutputStream("command", outStream));
117 forwardExecutionControl(ec, cmdIn, cmdOut);
118 }
119
120 static OutputStream multiplexingOutputStream(String label, OutputStream outputStream) {
121 return new MultiplexingOutputStream(label, outputStream);
122 }
123
124 /**
125 * Reads from an InputStream which has been packetized and write its contents
126 * to the out and err OutputStreams; Copies the command stream.
127 * @param input the packetized input stream
128 * @param streamMap a map between stream names and the output streams to forward
129 * @return the command stream
130 * @throws IOException if setting up the streams raised an exception
131 */
132 public static ObjectInput remoteInput(InputStream input,
133 Map<String, OutputStream> streamMap) throws IOException {
134 PipeInputStream commandIn = new PipeInputStream();
135 new DemultiplexInput(input, commandIn, streamMap).start();
136 return new ObjectInputStream(commandIn);
137 }
138
139 /**
140 * Monitor the JDI event stream for {@link com.sun.jdi.event.VMDeathEvent}
141 * and {@link com.sun.jdi.event.VMDisconnectEvent}. If encountered, invokes
142 * {@code unbiddenExitHandler}.
143 *
144 * @param vm the virtual machine to check
145 * @param unbiddenExitHandler the handler, which will accept the exit
146 * information
147 */
148 public static void detectJDIExitEvent(VirtualMachine vm, Consumer<String> unbiddenExitHandler) {
149 if (vm.canBeModified()) {
150 new JDIEventHandler(vm, unbiddenExitHandler).start();
151 }
152 }
153
154 /**
155 * Creates a Thread that will ship all input to the remote agent.
156 *
157 * @param inputStream the user input
158 * @param outStream the input to the remote agent
159 * @param handler a failure handler
160 */
161 public static void forwardInputToRemote(final InputStream inputStream,
162 final OutputStream outStream, final Consumer<Exception> handler) {
163 Thread thr = new Thread("input reader") {
164 @Override
165 public void run() {
166 try {
167 byte[] buf = new byte[256];
168 int cnt;
169 while ((cnt = inputStream.read(buf)) != -1) {
170 outStream.write(buf, 0, cnt);
171 outStream.flush();
172 }
173 } catch (Exception ex) {
174 handler.accept(ex);
175 }
176 }
177 };
178 thr.setPriority(Thread.MAX_PRIORITY - 1);
179 thr.start();
180 }
181
182 }
|
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 jdk.jshell.spi.ExecutionEnv;
28
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.ObjectInput;
32 import java.io.ObjectInputStream;
33 import java.io.ObjectOutput;
34 import java.io.ObjectOutputStream;
35 import java.io.OutputStream;
36 import java.util.Arrays;
37 import java.util.HashMap;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 import java.util.function.BiFunction;
41 import java.util.function.Consumer;
42
43 import com.sun.jdi.VirtualMachine;
44 import jdk.jshell.spi.ExecutionControl;
45
46
47 /**
48 * Miscellaneous utility methods for setting-up implementations of
49 * {@link ExecutionControl}. Particularly implementations with remote
50 * execution.
51 *
52 * @author Jan Lahoda
53 * @author Robert Field
54 */
55 public class Util {
56
57 // never instanciated
58 private Util() {}
59
60 /**
61 * Create a composite {@link ExecutionControl.Generator} instance that, when
62 * generating, will try each specified generator until successfully creating
87 };
88 }
89
90 /**
91 * Forward commands from the input to the specified {@link ExecutionControl}
92 * instance, then responses back on the output.
93 * @param ec the direct instance of {@link ExecutionControl} to process commands
94 * @param in the command input
95 * @param out the command response output
96 */
97 public static void forwardExecutionControl(ExecutionControl ec,
98 ObjectInput in, ObjectOutput out) {
99 new ExecutionControlForwarder(ec, in, out).commandLoop();
100 }
101
102 /**
103 * Forward commands from the input to the specified {@link ExecutionControl}
104 * instance, then responses back on the output.
105 * @param ec the direct instance of {@link ExecutionControl} to process commands
106 * @param inStream the stream from which to create the command input
107 * @param outStream the stream that will carry any specified auxiliary channels (like
108 * {@code System.out} and {@code System.err}), and the command response output.
109 * @param outputStreamMap a map between names of additional streams to carry and setters
110 * for the stream. Names starting with '$' are reserved for internal use.
111 * @param inputStreamMap a map between names of additional streams to carry and setters
112 * for the stream. Names starting with '$' are reserved for internal use.
113 * @throws IOException if there are errors using the passed streams
114 */
115 public static void forwardExecutionControlAndIO(ExecutionControl ec,
116 InputStream inStream, OutputStream outStream,
117 Map<String, Consumer<OutputStream>> outputStreamMap,
118 Map<String, Consumer<InputStream>> inputStreamMap) throws IOException {
119 for (Entry<String, Consumer<OutputStream>> e : outputStreamMap.entrySet()) {
120 e.getValue().accept(multiplexingOutputStream(e.getKey(), outStream));
121 }
122
123 ObjectOutputStream cmdOut = new ObjectOutputStream(multiplexingOutputStream("$command", outStream));
124 PipeInputStream cmdInPipe = new PipeInputStream();
125 Map<String, OutputStream> inputs = new HashMap<>();
126 inputs.put("$command", cmdInPipe.createOutput());
127 for (Entry<String, Consumer<InputStream>> e : inputStreamMap.entrySet()) {
128 OutputStream inputSignal = multiplexingOutputStream("$" + e.getKey() + "-input-requested", outStream);
129 PipeInputStream inputPipe = new PipeInputStream() {
130 @Override protected void inputNeeded() throws IOException {
131 inputSignal.write('1');
132 inputSignal.flush();
133 }
134 };
135 inputs.put(e.getKey(), inputPipe.createOutput());
136 e.getValue().accept(inputPipe);
137 }
138 new DemultiplexInput(inStream, inputs, inputs.values()).start();
139 ObjectInputStream cmdIn = new ObjectInputStream(cmdInPipe);
140
141 forwardExecutionControl(ec, cmdIn, cmdOut);
142 }
143
144 static OutputStream multiplexingOutputStream(String label, OutputStream outputStream) {
145 return new MultiplexingOutputStream(label, outputStream);
146 }
147
148 /**
149 * Creates an ExecutionControl for given packetized input and output. The given InputStream
150 * is de-packetized, and content forwarded to ObjectInput and given OutputStreams. The ObjectOutput
151 * and values read from the given InputStream are packetized and sent to the given OutputStream.
152 *
153 * @param input the packetized input stream
154 * @param output the packetized output stream
155 * @param outputStreamMap a map between stream names and the output streams to forward.
156 * Names starting with '$' are reserved for internal use.
157 * @param inputStreamMap a map between stream names and the input streams to forward.
158 * Names starting with '$' are reserved for internal use.
159 * @param factory to create the ExecutionControl from ObjectInput and ObjectOutput.
160 * @return the created ExecutionControl
161 * @throws IOException if setting up the streams raised an exception
162 */
163 public static ExecutionControl remoteInputOutput(InputStream input, OutputStream output,
164 Map<String, OutputStream> outputStreamMap, Map<String, InputStream> inputStreamMap,
165 BiFunction<ObjectInput, ObjectOutput, ExecutionControl> factory) throws IOException {
166 Map<String, OutputStream> augmentedStreamMap = new HashMap<>(outputStreamMap);
167 ObjectOutput commandOut = new ObjectOutputStream(Util.multiplexingOutputStream("$command", output));
168 for (Entry<String, InputStream> e : inputStreamMap.entrySet()) {
169 InputStream in = e.getValue();
170 OutputStream inTarget = Util.multiplexingOutputStream(e.getKey(), output);
171 augmentedStreamMap.put("$" + e.getKey() + "-input-requested", new OutputStream() {
172 @Override
173 public void write(int b) throws IOException {
174 //value ignored, just a trigger to read from the input
175 inTarget.write(in.read());
176 }
177 });
178 }
179 PipeInputStream commandIn = new PipeInputStream();
180 OutputStream commandInTarget = commandIn.createOutput();
181 augmentedStreamMap.put("$command", commandInTarget);
182 new DemultiplexInput(input, augmentedStreamMap, Arrays.asList(commandInTarget)).start();
183 return factory.apply(new ObjectInputStream(commandIn), commandOut);
184 }
185
186 /**
187 * Monitor the JDI event stream for {@link com.sun.jdi.event.VMDeathEvent}
188 * and {@link com.sun.jdi.event.VMDisconnectEvent}. If encountered, invokes
189 * {@code unbiddenExitHandler}.
190 *
191 * @param vm the virtual machine to check
192 * @param unbiddenExitHandler the handler, which will accept the exit
193 * information
194 */
195 public static void detectJDIExitEvent(VirtualMachine vm, Consumer<String> unbiddenExitHandler) {
196 if (vm.canBeModified()) {
197 new JDIEventHandler(vm, unbiddenExitHandler).start();
198 }
199 }
200
201 }
|