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