--- /dev/null 2016-07-18 08:15:00.740233585 -0700 +++ new/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java 2016-07-19 16:57:34.286285408 -0700 @@ -0,0 +1,182 @@ +/* + * 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.util.Map; +import java.util.Map.Entry; +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 streamMap a map between names of additional streams to carry and setters + * for the stream + * @throws IOException if there are errors using the passed streams + */ + public static void forwardExecutionControlAndIO(ExecutionControl ec, + InputStream inStream, OutputStream outStream, + Map> streamMap) throws IOException { + ObjectInputStream cmdIn = new ObjectInputStream(inStream); + for (Entry> e : streamMap.entrySet()) { + e.getValue().accept(multiplexingOutputStream(e.getKey(), outStream)); + } + ObjectOutputStream cmdOut = new ObjectOutputStream(multiplexingOutputStream("command", outStream)); + forwardExecutionControl(ec, cmdIn, cmdOut); + } + + 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 streamMap a map between stream names and the output streams to forward + * @return the command stream + * @throws IOException if setting up the streams raised an exception + */ + public static ObjectInput remoteInput(InputStream input, + Map streamMap) throws IOException { + PipeInputStream commandIn = new PipeInputStream(); + new DemultiplexInput(input, commandIn, streamMap).start(); + return new ObjectInputStream(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(); + } + } + + /** + * 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(); + } + +}