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 /** 81 * Creates an instance, delegating loader operations to the specified 82 * delegate. 83 * 84 * @param loaderDelegate the delegate to handle loading classes 85 */ 86 public RemoteExecutionControl(LoaderDelegate loaderDelegate) { 87 super(loaderDelegate); 88 } 89 90 /** 91 * Create an instance using the default class loading. 92 */ 93 public RemoteExecutionControl() { 94 } 95 96 @Override 97 public void stop() throws EngineTerminationException, InternalException { 98 // handled by JDI 99 } 100 101 // Overridden only so this stack frame is seen 102 @Override 103 protected String invoke(Method doitMethod) throws Exception { 104 return super.invoke(doitMethod); 105 } 106 107 // Overridden only so this stack frame is seen 108 @Override 109 public String varValue(String className, String varName) throws RunException, EngineTerminationException, InternalException { 110 return super.varValue(className, varName); 111 } 112 113 @Override 114 protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException { 115 if (cause instanceof StopExecutionException) { 116 expectingStop = false; 117 throw new StoppedException(); 118 } else { 119 return super.throwConvertedInvocationException(cause); 120 } 121 } 122 123 @Override 124 protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException { 125 if (ex instanceof StopExecutionException || 126 ex.getCause() instanceof StopExecutionException) { 127 expectingStop = false; 128 throw new StoppedException(); 129 } 130 return super.throwConvertedOtherException(ex); 131 } 132 133 @Override 134 protected void clientCodeEnter() { 135 expectingStop = false; 136 inClientCode = true; 137 } 138 139 @Override 140 protected void clientCodeLeave() throws InternalException { 141 inClientCode = false; 142 while (expectingStop) { 143 try { 144 Thread.sleep(0); 145 } catch (InterruptedException ex) { 146 throw new InternalException("*** Sleep interrupted while waiting for stop exception: " + ex); 147 } 148 } 149 } 150 151 @SuppressWarnings("serial") // serialVersionUID intentionally omitted 152 private class StopExecutionException extends ThreadDeath { 153 154 @Override 155 public synchronized Throwable fillInStackTrace() { 156 return this; 157 } 158 } 159 160 }