--- /dev/null 2016-07-12 14:35:29.888302783 -0700 +++ new/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java 2016-07-12 22:46:59.367053822 -0700 @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jshell.execution; + +import jdk.jshell.spi.ExecutionEnv; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.Socket; +import java.util.function.Consumer; +import com.sun.jdi.VirtualMachine; +import jdk.jshell.spi.ExecutionControl; + + +/** + * Miscellaneous utility methods for setting-up implementations of + * {@link ExecutionControl}. Particularly implementations with remote + * execution. + * + * @author Jan Lahoda + * @author Robert Field + */ +public class Util { + + // never instanciated + private Util() {} + + /** + * Create a composite {@link ExecutionControl.Generator} instance that, when + * generating, will try each specified generator until successfully creating + * an {@link ExecutionControl} instance, or, if all fail, will re-throw the + * first exception. + * + * @param gec0 the first instance to try + * @param gecs the second through Nth instance to try + * @return the fail-over generator + */ + public static ExecutionControl.Generator failOverExecutionControlGenerator( + ExecutionControl.Generator gec0, ExecutionControl.Generator... gecs) { + return (ExecutionEnv env) -> { + Throwable thrown; + try { + return gec0.generate(env); + } catch (Throwable ex) { + thrown = ex; + } + for (ExecutionControl.Generator gec : gecs) { + try { + return gec.generate(env); + } catch (Throwable ignore) { + // only care about the first, and only if they all fail + } + } + throw thrown; + }; + } + + /** + * Forward commands from the input to the specified {@link ExecutionControl} + * instance, then responses back on the output. + * @param ec the direct instance of {@link ExecutionControl} to process commands + * @param in the command input + * @param out the command response output + */ + public static void forwardExecutionControl(ExecutionControl ec, + ObjectInput in, ObjectOutput out) { + new ExecutionControlForwarder(ec, in, out).commandLoop(); + } + + /** + * Forward commands from the input to the specified {@link ExecutionControl} + * instance, then responses back on the output. + * @param ec the direct instance of {@link ExecutionControl} to process commands + * @param inStream the stream from which to create the command input + * @param outStream the stream that will carry {@code System.out}, + * {@code System.err}, any specified auxiliary channels, and the + * command response output. + * @param out setter for {@code System.out} + * @param err setter for {@code System.err} + * @param aux setters for any auxiliary channels + * @throws IOException if there are errors using the passed streams + */ + @SafeVarargs + private static void forwardExecutionControlAndIO(ExecutionControl ec, + InputStream inStream, OutputStream outStream, + Consumer out, Consumer err, Consumer... aux) throws IOException { + ObjectInputStream cmdIn = new ObjectInputStream(inStream); + out.accept(multiplexingOutputStream("out", outStream)); + err.accept(multiplexingOutputStream("err", outStream)); + for (int i = 0; i < aux.length; ++i) { + aux[i].accept(multiplexingOutputStream("aux" + (i+1), outStream)); + } + ObjectOutputStream cmdOut = new ObjectOutputStream(multiplexingOutputStream("command", outStream)); + forwardExecutionControl(ec, cmdIn, cmdOut); + } + + /** + * Forward commands from the socket input to the specified + * {@link ExecutionControl} instance, then responses back on the + * socket output; Also, forward the {@code System.out} and + * {@code System.err} streams. + * + * @param ec the direct instance of {@link ExecutionControl} to process commands + * @param socket the command/response/terminal-I/O socket + * @throws IOException if there are errors using the socket + */ + public static void forwardExecutionControlAndIO(ExecutionControl ec, + Socket socket) throws IOException { + // in before out -- so we don't hang the controlling process + InputStream inStream = socket.getInputStream(); + OutputStream outStream = socket.getOutputStream(); + forwardExecutionControlAndIO(ec, inStream, outStream, + st -> System.setOut(new PrintStream(st, true)), + st -> System.setErr(new PrintStream(st, true))); + } + + static OutputStream multiplexingOutputStream(String label, OutputStream outputStream) { + return new MultiplexingOutputStream(label, outputStream); + } + + /** + * Reads from an InputStream which has been packetized and write its contents + * to the out and err OutputStreams; Copies the command stream. + * @param input the packetized input stream + * @param log the log for debugging information + * @param out the output stream to forward {@code System.out} to + * @param err the output stream to forward {@code System.err} to + * @return the command stream + */ + public static InputStream demultiplexInput(InputStream input, ECLogger log, + OutputStream out, OutputStream err) { + PipeInputStream commandIn = new PipeInputStream(); + new DemultiplexInput(input, commandIn, log, out, err).start(); + return commandIn; + } + + /** + * Monitor the JDI event stream for {@link com.sun.jdi.event.VMDeathEvent} + * and {@link com.sun.jdi.event.VMDisconnectEvent}. If encountered, invokes + * {@code unbiddenExitHandler}. + * + * @param vm the virtual machine to check + * @param unbiddenExitHandler the handler, which will accept the exit + * information + */ + public static void detectJDIExitEvent(VirtualMachine vm, Consumer unbiddenExitHandler) { + if (vm.canBeModified()) { + new JDIEventHandler(vm, unbiddenExitHandler).start(); + } + } + + /** + * Retrieve JShell's default logger. + * + * @param execEnv the execution environment + * @return the logger + */ + public static ECLogger defaultLogger(ExecutionEnv execEnv) { + return new JShellECLogger(execEnv); + } + + /** + * Creates a Thread that will ship all input to the remote agent. + * + * @param inputStream the user input + * @param outStream the input to the remote agent + * @param handler a failure handler + */ + public static void forwardInputToRemote(final InputStream inputStream, + final OutputStream outStream, final Consumer handler) { + Thread thr = new Thread("input reader") { + @Override + public void run() { + try { + byte[] buf = new byte[256]; + int cnt; + while ((cnt = inputStream.read(buf)) != -1) { + outStream.write(buf, 0, cnt); + outStream.flush(); + } + } catch (Exception ex) { + handler.accept(ex); + } + } + }; + thr.setPriority(Thread.MAX_PRIORITY - 1); + thr.start(); + } + +}