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>> chans = new HashMap<>(); 65 chans.put("out", st -> System.setOut(new PrintStream(st, true))); 66 chans.put("err", st -> System.setErr(new PrintStream(st, true))); 67 forwardExecutionControlAndIO(new RemoteExecutionControl(), inStream, outStream, chans); 68 } 69 70 // These three variables are used by the main JShell process in interrupting 71 // the running process. Access is via JDI, so the reference is not visible 72 // to code inspection. 73 private boolean inClientCode; // Queried by the main process (in superclass) 74 private boolean expectingStop; // Set by the main process 75 // Set by the main process 76 77 // thrown by the main process via JDI: 78 private final StopExecutionException stopException = new StopExecutionException(); 79 80 @Override 81 public void stop() throws EngineTerminationException, InternalException { 82 // handled by JDI 83 } 84 85 // Overridden only so this stack frame is seen 86 @Override 87 protected String invoke(Method doitMethod) throws Exception { 88 return super.invoke(doitMethod); 89 } 90 91 // Overridden only so this stack frame is seen 92 @Override 93 public String varValue(String className, String varName) throws RunException, EngineTerminationException, InternalException { 94 return super.varValue(className, varName); 95 } 96 97 @Override 98 protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException { 99 if (cause instanceof StopExecutionException) { 100 expectingStop = false; 101 throw new StoppedException(); 102 } else { 103 return super.throwConvertedInvocationException(cause); 104 } 105 } 106 107 @Override 108 protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException { 109 if (ex instanceof StopExecutionException || 110 ex.getCause() instanceof StopExecutionException) { 111 expectingStop = false; 112 throw new StoppedException(); 113 } 114 return super.throwConvertedOtherException(ex); 115 } 116 117 @Override 118 protected void clientCodeEnter() { 119 expectingStop = false; 120 inClientCode = true; 121 } 122 123 @Override 124 protected void clientCodeLeave() throws InternalException { 125 inClientCode = false; 126 while (expectingStop) { 127 try { 128 Thread.sleep(0); 129 } catch (InterruptedException ex) { 130 throw new InternalException("*** Sleep interrupted while waiting for stop exception: " + ex); 131 } 132 } 133 } 134 135 @SuppressWarnings("serial") // serialVersionUID intentionally omitted 136 private class StopExecutionException extends ThreadDeath { 137 138 @Override 139 public synchronized Throwable fillInStackTrace() { 140 return this; 141 } 142 } 143 144 }