1 /* 2 * Copyright (c) 2014, 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 java.io.InputStream; 28 import java.io.OutputStream; 29 import java.io.PrintStream; 30 import java.lang.reflect.Method; 31 import java.net.Socket; 32 33 import java.util.HashMap; 34 import java.util.Map; 35 import java.util.function.Consumer; 36 import jdk.jshell.spi.ExecutionControl; 37 import static jdk.jshell.execution.Util.forwardExecutionControlAndIO; 38 39 /** 40 * The remote agent runs in the execution process (separate from the main JShell 41 * process). This agent loads code over a socket from the main JShell process, 42 * executes the code, and other misc, Specialization of 43 * {@link DirectExecutionControl} which adds stop support controlled by 44 * an external process. Designed to work with {@link JDIDefaultExecutionControl}. 45 * 46 * @author Jan Lahoda 47 * @author Robert Field 48 */ 49 public class RemoteExecutionControl extends DirectExecutionControl implements ExecutionControl { 50 51 /** 52 * Launch the agent, connecting to the JShell-core over the socket specified 53 * in the command-line argument. 54 * 55 * @param args standard command-line arguments, expectation is the socket 56 * number is the only argument 57 * @throws Exception any unexpected exception 58 */ 59 public static void main(String[] args) throws Exception { 60 String loopBack = null; 61 Socket socket = new Socket(loopBack, Integer.parseInt(args[0])); 62 InputStream inStream = socket.getInputStream(); 63 OutputStream outStream = socket.getOutputStream(); 64 Map<String, Consumer<OutputStream>> outputs = new HashMap<>(); 65 outputs.put("out", st -> System.setOut(new PrintStream(st, true))); 66 outputs.put("err", st -> System.setErr(new PrintStream(st, true))); 67 Map<String, Consumer<InputStream>> input = new HashMap<>(); 68 input.put("in", st -> System.setIn(st)); 69 forwardExecutionControlAndIO(new RemoteExecutionControl(), inStream, outStream, outputs, input); 70 } 71 72 // These three variables are used by the main JShell process in interrupting 73 // the running process. Access is via JDI, so the reference is not visible 74 // to code inspection. 75 private boolean inClientCode; // Queried by the main process (in superclass) 76 private boolean expectingStop; // Set by the main process 77 // Set by the main process 78 79 // thrown by the main process via JDI: 80 private final StopExecutionException stopException = new StopExecutionException(); 81 82 /** 83 * Creates an instance, delegating loader operations to the specified 84 * delegate. 85 * 86 * @param loaderDelegate the delegate to handle loading classes 87 */ 88 public RemoteExecutionControl(LoaderDelegate loaderDelegate) { 89 super(loaderDelegate); 90 } 91 92 /** 93 * Create an instance using the default class loading. 94 */ 95 public RemoteExecutionControl() { 96 } 97 98 @Override 99 public void stop() throws EngineTerminationException, InternalException { 100 // handled by JDI 101 } 102 103 // Overridden only so this stack frame is seen 104 @Override 105 protected String invoke(Method doitMethod) throws Exception { 106 return super.invoke(doitMethod); 107 } 108 109 // Overridden only so this stack frame is seen 110 @Override 111 public String varValue(String className, String varName) throws RunException, EngineTerminationException, InternalException { 112 return super.varValue(className, varName); 113 } 114 115 @Override 116 protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException { 117 if (cause instanceof StopExecutionException) { 118 expectingStop = false; 119 throw new StoppedException(); 120 } else { 121 return super.throwConvertedInvocationException(cause); 122 } 123 } 124 125 @Override 126 protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException { 127 if (ex instanceof StopExecutionException || 128 ex.getCause() instanceof StopExecutionException) { 129 expectingStop = false; 130 throw new StoppedException(); 131 } 132 return super.throwConvertedOtherException(ex); 133 } 134 135 @Override 136 protected void clientCodeEnter() { 137 expectingStop = false; 138 inClientCode = true; 139 } 140 141 @Override 142 protected void clientCodeLeave() throws InternalException { 143 inClientCode = false; 144 while (expectingStop) { 145 try { 146 Thread.sleep(0); 147 } catch (InterruptedException ex) { 148 throw new InternalException("*** Sleep interrupted while waiting for stop exception: " + ex); 149 } 150 } 151 } 152 153 @SuppressWarnings("serial") // serialVersionUID intentionally omitted 154 private class StopExecutionException extends ThreadDeath { 155 156 @Override 157 public synchronized Throwable fillInStackTrace() { 158 return this; 159 } 160 } 161 162 }