1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 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.io.PrintStream; 36 import java.net.Socket; 37 import java.util.AbstractMap.SimpleEntry; 38 import java.util.Collections; 39 import java.util.HashMap; 40 import java.util.Map; 41 import java.util.Map.Entry; 42 import java.util.function.Consumer; 43 import java.util.stream.Collectors; 44 import java.util.stream.Stream; 45 import com.sun.jdi.VirtualMachine; 46 import jdk.jshell.spi.ExecutionControl; 47 48 49 /** 50 * Miscellaneous utility methods for setting-up implementations of 51 * {@link ExecutionControl}. Particularly implementations with remote 52 * execution. 53 * 54 * @author Jan Lahoda 55 * @author Robert Field 56 */ 57 public class Util { 58 59 // never instanciated 60 private Util() {} 61 62 /** 63 * Create a composite {@link ExecutionControl.Generator} instance that, when 64 * generating, will try each specified generator until successfully creating 65 * an {@link ExecutionControl} instance, or, if all fail, will re-throw the 66 * first exception. 67 * 68 * @param gec0 the first instance to try 69 * @param gecs the second through Nth instance to try 70 * @return the fail-over generator 71 */ 72 public static ExecutionControl.Generator failOverExecutionControlGenerator( 73 ExecutionControl.Generator gec0, ExecutionControl.Generator... gecs) { 74 return (ExecutionEnv env) -> { 75 Throwable thrown; 76 try { 77 return gec0.generate(env); 78 } catch (Throwable ex) { 79 thrown = ex; 80 } 81 for (ExecutionControl.Generator gec : gecs) { 82 try { 83 return gec.generate(env); 84 } catch (Throwable ignore) { 85 // only care about the first, and only if they all fail 86 } 87 } 88 throw thrown; 89 }; 90 } 91 92 /** 93 * Forward commands from the input to the specified {@link ExecutionControl} 94 * instance, then responses back on the output. 95 * @param ec the direct instance of {@link ExecutionControl} to process commands 96 * @param in the command input 97 * @param out the command response output 98 */ 99 public static void forwardExecutionControl(ExecutionControl ec, 100 ObjectInput in, ObjectOutput out) { 101 new ExecutionControlForwarder(ec, in, out).commandLoop(); 102 } 103 104 /** 105 * Forward commands from the input to the specified {@link ExecutionControl} 106 * instance, then responses back on the output. 107 * @param ec the direct instance of {@link ExecutionControl} to process commands 108 * @param inStream the stream from which to create the command input 109 * @param outStream the stream that will carry {@code System.out}, 110 * {@code System.err}, any specified auxiliary channels, and the 111 * command response output. 112 * @param streamMap a map between names of additional streams to carry and setters 113 * for the stream 114 * @throws IOException if there are errors using the passed streams 115 */ 116 public static void forwardExecutionControlAndIO(ExecutionControl ec, 117 InputStream inStream, OutputStream outStream, 118 Map<String, Consumer<OutputStream>> streamMap) throws IOException { 119 ObjectInputStream cmdIn = new ObjectInputStream(inStream); 120 for (Entry<String, Consumer<OutputStream>> e : streamMap.entrySet()) { 121 e.getValue().accept(multiplexingOutputStream(e.getKey(), outStream)); 122 } 123 ObjectOutputStream cmdOut = new ObjectOutputStream(multiplexingOutputStream("command", outStream)); 124 forwardExecutionControl(ec, cmdIn, cmdOut); 125 } 126 127 static OutputStream multiplexingOutputStream(String label, OutputStream outputStream) { 128 return new MultiplexingOutputStream(label, outputStream); 129 } 130 131 /** 132 * Reads from an InputStream which has been packetized and write its contents 133 * to the out and err OutputStreams; Copies the command stream. 134 * @param input the packetized input stream 135 * @param streamMap a map between stream names and the output streams to forward 136 * @return the command stream 137 * @throws IOException if setting up the streams raised an exception 138 */ 139 public static ObjectInput remoteInput(InputStream input, 140 Map<String, OutputStream> streamMap) throws IOException { 141 PipeInputStream commandIn = new PipeInputStream(); 142 new DemultiplexInput(input, commandIn, streamMap).start(); 143 return new ObjectInputStream(commandIn); 144 } 145 146 /** 147 * Monitor the JDI event stream for {@link com.sun.jdi.event.VMDeathEvent} 148 * and {@link com.sun.jdi.event.VMDisconnectEvent}. If encountered, invokes 149 * {@code unbiddenExitHandler}. 150 * 151 * @param vm the virtual machine to check 152 * @param unbiddenExitHandler the handler, which will accept the exit 153 * information 154 */ 155 public static void detectJDIExitEvent(VirtualMachine vm, Consumer<String> unbiddenExitHandler) { 156 if (vm.canBeModified()) { 157 new JDIEventHandler(vm, unbiddenExitHandler).start(); 158 } 159 } 160 161 /** 162 * Creates a Thread that will ship all input to the remote agent. 163 * 164 * @param inputStream the user input 165 * @param outStream the input to the remote agent 166 * @param handler a failure handler 167 */ 168 public static void forwardInputToRemote(final InputStream inputStream, 169 final OutputStream outStream, final Consumer<Exception> handler) { 170 Thread thr = new Thread("input reader") { 171 @Override 172 public void run() { 173 try { 174 byte[] buf = new byte[256]; 175 int cnt; 176 while ((cnt = inputStream.read(buf)) != -1) { 177 outStream.write(buf, 0, cnt); 178 outStream.flush(); 179 } 180 } catch (Exception ex) { 181 handler.accept(ex); 182 } 183 } 184 }; 185 thr.setPriority(Thread.MAX_PRIORITY - 1); 186 thr.start(); 187 } 188 189 }