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 */ 25package jdk.jshell.execution; 26 27import java.io.IOException; 28import java.io.ObjectInput; 29import java.io.ObjectOutput; 30import jdk.jshell.JShellException; 31import jdk.jshell.spi.ExecutionControl; 32import static jdk.jshell.execution.RemoteCodes.*; 33 34/** 35 * An implementation of the {@link jdk.jshell.spi.ExecutionControl} 36 * execution engine SPI which streams requests to a remote agent where 37 * execution takes place. 38 * 39 * @author Robert Field 40 * @since 9 41 */ 42public class StreamingExecutionControl implements ExecutionControl { 43 44 private final ObjectOutput out; 45 private final ObjectInput in; 46 47 /** 48 * Creates an instance. 49 * 50 * @param out the output for commands 51 * @param in the input for command responses 52 */ 53 public StreamingExecutionControl(ObjectOutput out, ObjectInput in) { 54 this.out = out; 55 this.in = in; 56 } 57 58 @Override 59 public void load(ClassBytecodes[] cbcs) 60 throws ClassInstallException, NotImplementedException, EngineTerminationException { 61 try { 62 // Send a load command to the remote agent. 63 writeCommand(CMD_LOAD); 64 out.writeObject(cbcs); 65 out.flush(); 66 // Retrieve and report results from the remote agent. 67 readAndReportClassInstallResult(); 68 } catch (IOException ex) { 69 throw new EngineTerminationException("Exception writing remote load: " + ex); 70 } 71 } 72 73 @Override 74 public void redefine(ClassBytecodes[] cbcs) 75 throws ClassInstallException, NotImplementedException, EngineTerminationException { 76 try { 77 // Send a load command to the remote agent. 78 writeCommand(CMD_REDEFINE); 79 out.writeObject(cbcs); 80 out.flush(); 81 // Retrieve and report results from the remote agent. 82 readAndReportClassInstallResult(); 83 } catch (IOException ex) { 84 throw new EngineTerminationException("Exception writing remote redefine: " + ex); 85 } 86 } 87 88 @Override 89 public String invoke(String classname, String methodname) 90 throws RunException, EngineTerminationException, InternalException { 91 try { 92 // Send the invoke command to the remote agent. 93 writeCommand(CMD_INVOKE); 94 out.writeUTF(classname); 95 out.writeUTF(methodname); 96 out.flush(); 97 // Retrieve and report results from the remote agent. 98 readAndReportExecutionResult(); 99 String result = in.readUTF(); 100 return result; 101 } catch (IOException ex) { 102 throw new EngineTerminationException("Exception writing remote invoke: " + ex); 103 } 104 } 105 106 @Override 107 public String varValue(String classname, String varname) 108 throws RunException, EngineTerminationException, InternalException { 109 try { 110 // Send the variable-value command to the remote agent. 111 writeCommand(CMD_VAR_VALUE); 112 out.writeUTF(classname); 113 out.writeUTF(varname); 114 out.flush(); 115 // Retrieve and report results from the remote agent. 116 readAndReportExecutionResult(); 117 String result = in.readUTF(); 118 return result; 119 } catch (IOException ex) { 120 throw new EngineTerminationException("Exception writing remote varValue: " + ex); 121 } 122 } 123 124 125 @Override 126 public void addToClasspath(String path) 127 throws EngineTerminationException, InternalException { 128 try { 129 // Send the classpath addition command to the remote agent. 130 writeCommand(CMD_ADD_CLASSPATH); 131 out.writeUTF(path); 132 out.flush(); 133 // Retrieve and report results from the remote agent. 134 readAndReportClassSimpleResult(); 135 } catch (IOException ex) { 136 throw new EngineTerminationException("Exception writing remote add to classpath: " + ex); 137 } 138 } 139 140 @Override 141 public void stop() 142 throws EngineTerminationException, InternalException { 143 try { 144 // Send the variable-value command to the remote agent. 145 writeCommand(CMD_STOP); 146 out.flush(); 147 } catch (IOException ex) { 148 throw new EngineTerminationException("Exception writing remote stop: " + ex); 149 } 150 } 151 152 @Override 153 public Object extensionCommand(String command, Object arg) 154 throws RunException, EngineTerminationException, InternalException { 155 try { 156 writeCommand(command); 157 out.writeObject(arg); 158 out.flush(); 159 // Retrieve and report results from the remote agent. 160 readAndReportExecutionResult(); 161 Object result = in.readObject(); 162 return result; 163 } catch (IOException | ClassNotFoundException ex) { 164 throw new EngineTerminationException("Exception transmitting remote extensionCommand: " 165 + command + " -- " + ex); 166 } 167 } 168 169 /** 170 * Closes the execution engine. Send an exit command to the remote agent. 171 */ 172 @Override 173 public void close() { 174 try { 175 writeCommand(CMD_CLOSE); 176 out.flush(); 177 } catch (IOException ex) { 178 // ignore; 179 } 180 } 181 182 private void writeCommand(String cmd) throws IOException { 183 out.writeInt(COMMAND_PREFIX); 184 out.writeUTF(cmd); 185 } 186 187 /** 188 * Reports results from a remote agent command that does not expect 189 * exceptions. 190 */ 191 private void readAndReportClassSimpleResult() throws EngineTerminationException, InternalException { 192 try { 193 int status = in.readInt(); 194 switch (status) { 195 case RESULT_SUCCESS: 196 return; 197 case RESULT_NOT_IMPLEMENTED: { 198 String message = in.readUTF(); 199 throw new NotImplementedException(message); 200 } 201 case RESULT_INTERNAL_PROBLEM: { 202 String message = in.readUTF(); 203 throw new InternalException(message); 204 } 205 case RESULT_TERMINATED: { 206 String message = in.readUTF(); 207 throw new EngineTerminationException(message); 208 } 209 default: { 210 throw new EngineTerminationException("Bad remote result code: " + status); 211 } 212 } 213 } catch (IOException ex) { 214 throw new EngineTerminationException(ex.toString()); 215 } 216 } 217 218 /** 219 * Reports results from a remote agent command that does not expect 220 * exceptions. 221 */ 222 private void readAndReportClassInstallResult() throws ClassInstallException, 223 NotImplementedException, EngineTerminationException { 224 try { 225 int status = in.readInt(); 226 switch (status) { 227 case RESULT_SUCCESS: 228 return; 229 case RESULT_NOT_IMPLEMENTED: { 230 String message = in.readUTF(); 231 throw new NotImplementedException(message); 232 } 233 case RESULT_CLASS_INSTALL_EXCEPTION: { 234 String message = in.readUTF(); 235 boolean[] loaded = (boolean[]) in.readObject(); 236 throw new ClassInstallException(message, loaded); 237 } 238 case RESULT_TERMINATED: { 239 String message = in.readUTF(); 240 throw new EngineTerminationException(message); 241 } 242 default: { 243 throw new EngineTerminationException("Bad remote result code: " + status); 244 } 245 } 246 } catch (IOException | ClassNotFoundException ex) { 247 throw new EngineTerminationException(ex.toString()); 248 } 249 } 250 251 /** 252 * Reports results from a remote agent command that expects runtime 253 * exceptions. 254 * 255 * @return true if successful 256 * @throws IOException if the connection has dropped 257 * @throws JShellException {@link jdk.jshell.EvalException}, if a user 258 * exception was encountered on invoke; 259 * {@link jdk.jshell.UnresolvedReferenceException}, if an unresolved 260 * reference was encountered 261 * @throws java.lang.ClassNotFoundException 262 */ 263 private void readAndReportExecutionResult() throws RunException, 264 EngineTerminationException, InternalException { 265 try { 266 int status = in.readInt(); 267 switch (status) { 268 case RESULT_SUCCESS: 269 return; 270 case RESULT_NOT_IMPLEMENTED: { 271 String message = in.readUTF(); 272 throw new NotImplementedException(message); 273 } 274 case RESULT_USER_EXCEPTION: { 275 // A user exception was encountered. 276 String message = in.readUTF(); 277 String exceptionClassName = in.readUTF(); 278 StackTraceElement[] elems = (StackTraceElement[]) in.readObject(); 279 throw new UserException(message, exceptionClassName, elems); 280 } 281 case RESULT_CORRALLED: { 282 // An unresolved reference was encountered. 283 int id = in.readInt(); 284 StackTraceElement[] elems = (StackTraceElement[]) in.readObject(); 285 ResolutionException re = new ResolutionException(id, elems); 286 throw re; 287 } 288 case RESULT_STOPPED: { 289 // Execution was aborted by the stop() 290 throw new StoppedException(); 291 } 292 case RESULT_INTERNAL_PROBLEM: { 293 // An internal error has occurred. 294 String message = in.readUTF(); 295 throw new InternalException(message); 296 } 297 case RESULT_TERMINATED: { 298 String message = in.readUTF(); 299 throw new EngineTerminationException(message); 300 } 301 default: { 302 throw new EngineTerminationException("Bad remote result code: " + status); 303 } 304 } 305 } catch (IOException | ClassNotFoundException ex) { 306 throw new EngineTerminationException(ex.toString()); 307 } 308 } 309 310} 311