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