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.spi.ExecutionControl; 31import jdk.jshell.spi.ExecutionControl.ClassBytecodes; 32import jdk.jshell.spi.ExecutionControl.ClassInstallException; 33import jdk.jshell.spi.ExecutionControl.EngineTerminationException; 34import jdk.jshell.spi.ExecutionControl.InternalException; 35import jdk.jshell.spi.ExecutionControl.NotImplementedException; 36import jdk.jshell.spi.ExecutionControl.ResolutionException; 37import jdk.jshell.spi.ExecutionControl.StoppedException; 38import jdk.jshell.spi.ExecutionControl.UserException; 39import static jdk.jshell.execution.RemoteCodes.*; 40 41/** 42 * Forwards commands from the input to the specified {@link ExecutionControl} 43 * instance, then responses back on the output. 44 */ 45class ExecutionControlForwarder { 46 47 /** 48 * Maximum number of characters for writeUTF(). Byte maximum is 65535, at 49 * maximum three bytes per character that is 65535 / 3 == 21845. Minus one 50 * for safety. 51 */ 52 private static final int MAX_UTF_CHARS = 21844; 53 54 private final ExecutionControl ec; 55 private final ObjectInput in; 56 private final ObjectOutput out; 57 58 ExecutionControlForwarder(ExecutionControl ec, ObjectInput in, ObjectOutput out) { 59 this.ec = ec; 60 this.in = in; 61 this.out = out; 62 } 63 64 private boolean writeSuccess() throws IOException { 65 writeStatus(RESULT_SUCCESS); 66 flush(); 67 return true; 68 } 69 70 private boolean writeSuccessAndResult(String result) throws IOException { 71 writeStatus(RESULT_SUCCESS); 72 writeUTF(result); 73 flush(); 74 return true; 75 } 76 77 private boolean writeSuccessAndResult(Object result) throws IOException { 78 writeStatus(RESULT_SUCCESS); 79 writeObject(result); 80 flush(); 81 return true; 82 } 83 84 private void writeStatus(int status) throws IOException { 85 out.writeInt(status); 86 } 87 88 private void writeObject(Object o) throws IOException { 89 out.writeObject(o); 90 } 91 92 private void writeInt(int i) throws IOException { 93 out.writeInt(i); 94 } 95 96 private void writeUTF(String s) throws IOException { 97 if (s == null) { 98 s = ""; 99 } else if (s.length() > MAX_UTF_CHARS) { 100 // Truncate extremely long strings to prevent writeUTF from crashing the VM 101 s = s.substring(0, MAX_UTF_CHARS); 102 } 103 out.writeUTF(s); 104 } 105 106 private void flush() throws IOException { 107 out.flush(); 108 } 109 110 private boolean processCommand() throws IOException { 111 try { 112 int prefix = in.readInt(); 113 if (prefix != COMMAND_PREFIX) { 114 throw new EngineTerminationException("Invalid command prefix: " + prefix); 115 } 116 String cmd = in.readUTF(); 117 switch (cmd) { 118 case CMD_LOAD: { 119 // Load a generated class file over the wire 120 ClassBytecodes[] cbcs = (ClassBytecodes[]) in.readObject(); 121 ec.load(cbcs); 122 return writeSuccess(); 123 } 124 case CMD_REDEFINE: { 125 // Load a generated class file over the wire 126 ClassBytecodes[] cbcs = (ClassBytecodes[]) in.readObject(); 127 ec.redefine(cbcs); 128 return writeSuccess(); 129 } 130 case CMD_INVOKE: { 131 // Invoke executable entry point in loaded code 132 String className = in.readUTF(); 133 String methodName = in.readUTF(); 134 String res = ec.invoke(className, methodName); 135 return writeSuccessAndResult(res); 136 } 137 case CMD_VAR_VALUE: { 138 // Retrieve a variable value 139 String className = in.readUTF(); 140 String varName = in.readUTF(); 141 String res = ec.varValue(className, varName); 142 return writeSuccessAndResult(res); 143 } 144 case CMD_ADD_CLASSPATH: { 145 // Append to the claspath 146 String cp = in.readUTF(); 147 ec.addToClasspath(cp); 148 return writeSuccess(); 149 } 150 case CMD_STOP: { 151 // Stop the current execution 152 try { 153 ec.stop(); 154 } catch (Throwable ex) { 155 // JShell-core not waiting for a result, ignore 156 } 157 return true; 158 } 159 case CMD_CLOSE: { 160 // Terminate this process 161 try { 162 ec.close(); 163 } catch (Throwable ex) { 164 // JShell-core not waiting for a result, ignore 165 } 166 return true; 167 } 168 default: { 169 Object arg = in.readObject(); 170 Object res = ec.extensionCommand(cmd, arg); 171 return writeSuccessAndResult(res); 172 } 173 } 174 } catch (IOException ex) { 175 // handled by the outer level 176 throw ex; 177 } catch (EngineTerminationException ex) { 178 writeStatus(RESULT_TERMINATED); 179 writeUTF(ex.getMessage()); 180 flush(); 181 return false; 182 } catch (NotImplementedException ex) { 183 writeStatus(RESULT_NOT_IMPLEMENTED); 184 writeUTF(ex.getMessage()); 185 flush(); 186 return true; 187 } catch (InternalException ex) { 188 writeStatus(RESULT_INTERNAL_PROBLEM); 189 writeUTF(ex.getMessage()); 190 flush(); 191 return true; 192 } catch (ClassInstallException ex) { 193 writeStatus(RESULT_CLASS_INSTALL_EXCEPTION); 194 writeUTF(ex.getMessage()); 195 writeObject(ex.installed()); 196 flush(); 197 return true; 198 } catch (UserException ex) { 199 writeStatus(RESULT_USER_EXCEPTION); 200 writeUTF(ex.getMessage()); 201 writeUTF(ex.causeExceptionClass()); 202 writeObject(ex.getStackTrace()); 203 flush(); 204 return true; 205 } catch (ResolutionException ex) { 206 writeStatus(RESULT_CORRALLED); 207 writeInt(ex.id()); 208 writeObject(ex.getStackTrace()); 209 flush(); 210 return true; 211 } catch (StoppedException ex) { 212 writeStatus(RESULT_STOPPED); 213 flush(); 214 return true; 215 } catch (Throwable ex) { 216 writeStatus(RESULT_TERMINATED); 217 writeUTF(ex.getMessage()); 218 flush(); 219 return false; 220 } 221 } 222 223 void commandLoop() { 224 try { 225 while (processCommand()) { 226 // condition is loop action 227 } 228 } catch (IOException ex) { 229 // drop out of loop 230 } 231 } 232 233} 234