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.spi.ExecutionControl;
  31 import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
  32 import jdk.jshell.spi.ExecutionControl.ClassInstallException;
  33 import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
  34 import jdk.jshell.spi.ExecutionControl.InternalException;
  35 import jdk.jshell.spi.ExecutionControl.ResolutionException;
  36 import jdk.jshell.spi.ExecutionControl.StoppedException;
  37 import jdk.jshell.spi.ExecutionControl.UserException;
  38 
  39 /**
  40  * Forwards commands from the input to the specified {@link ExecutionControl}
  41  * instance, then responses back on the output.
  42  */
  43 class ExecutionControlForwarder {
  44 
  45     private final ExecutionControl ec;
  46     private final ObjectInput in;
  47     private final ObjectOutput out;
  48 
  49     ExecutionControlForwarder(ExecutionControl ec, ObjectInput in, ObjectOutput out) {
  50         this.ec = ec;
  51         this.in = in;
  52         this.out = out;
  53     }
  54 
  55     private boolean writeSuccess() throws IOException {
  56         writeStatus(RemoteStatus.SUCCESS);
  57         flush();
  58         return true;
  59     }
  60 
  61     private boolean writeSuccessAndResult(String result) throws IOException {
  62         writeStatus(RemoteStatus.SUCCESS);
  63         writeUTF(result);
  64         flush();
  65         return true;
  66     }
  67 
  68     private void writeStatus(RemoteStatus status) throws IOException {
  69         out.writeObject(status);
  70     }
  71 
  72     private void writeObject(Object o) throws IOException {
  73         out.writeObject(o);
  74     }
  75 
  76     private void writeInt(int i) throws IOException {
  77         out.writeInt(i);
  78     }
  79 
  80     private void writeUTF(String s) throws IOException {
  81         if (s == null) {
  82             s = "";
  83         }
  84         out.writeUTF(s);
  85     }
  86 
  87     private void flush() throws IOException {
  88         out.flush();
  89     }
  90 
  91     private boolean processCommand() throws IOException {
  92         RemoteCommand cmd;
  93         try {
  94             cmd = (RemoteCommand) in.readObject();
  95             if (cmd == null) {
  96                 throw new IOException("Missing command: " + cmd);
  97             }
  98             switch (cmd) {
  99                 case LOAD: {
 100                     // Load a generated class file over the wire
 101                     ClassBytecodes[] cbcs = (ClassBytecodes[]) in.readObject();
 102                     ec.load(cbcs);
 103                     return writeSuccess();
 104                 }
 105                 case REDEFINE: {
 106                     // Load a generated class file over the wire
 107                     ClassBytecodes[] cbcs = (ClassBytecodes[]) in.readObject();
 108                     ec.redefine(cbcs);
 109                     return writeSuccess();
 110                 }
 111                 case INVOKE: {
 112                     // Invoke executable entry point in loaded code
 113                     String className = in.readUTF();
 114                     String methodName = in.readUTF();
 115                     String res = ec.invoke(className, methodName);
 116                     return writeSuccessAndResult(res);
 117                 }
 118                 case VAR_VALUE: {
 119                     // Retrieve a variable value
 120                     String className = in.readUTF();
 121                     String varName = in.readUTF();
 122                     String res = ec.varValue(className, varName);
 123                     return writeSuccessAndResult(res);
 124                 }
 125                 case ADD_CLASSPATH: {
 126                     // Append to the claspath
 127                     String cp = in.readUTF();
 128                     ec.addToClasspath(cp);
 129                     return writeSuccess();
 130                 }
 131                 case SET_CLASSPATH: {
 132                     // Set the claspath
 133                     String cp = in.readUTF();
 134                     ec.setClasspath(cp);
 135                     return writeSuccess();
 136                 }
 137                 case STOP: {
 138                     // Stop the current execution
 139                     try {
 140                         ec.stop();
 141                     } catch (Throwable ex) {
 142                         // JShell-core not waiting for a result, ignore
 143                     }
 144                     return true;
 145                 }
 146                 case CLOSE: {
 147                     // Terminate this process
 148                     try {
 149                         ec.close();
 150                     } catch (Throwable ex) {
 151                         // JShell-core not waiting for a result, ignore
 152                     }
 153                     return true;
 154                 }
 155             }
 156             return false; // cannot get here, make the compiler happy
 157         } catch (IOException ex) {
 158             // handled by the outer level
 159             throw ex;
 160         } catch (EngineTerminationException ex) {
 161             writeStatus(RemoteStatus.TERMINATED);
 162             writeUTF(ex.getMessage());
 163             flush();
 164             return false;
 165         } catch (InternalException ex) {
 166             writeStatus(RemoteStatus.INTERNAL_PROBLEM);
 167             writeUTF(ex.getMessage());
 168             flush();
 169             return true;
 170         } catch (ClassInstallException ex) {
 171             writeStatus(RemoteStatus.CLASS_INSTALL_EXCEPTION);
 172             writeUTF(ex.getMessage());
 173             writeObject(ex.installed());
 174             flush();
 175             return true;
 176         } catch (UserException ex) {
 177             writeStatus(RemoteStatus.USER_EXCEPTION);
 178             writeUTF(ex.getMessage());
 179             writeUTF(ex.causeExceptionClass());
 180             writeObject(ex.getStackTrace());
 181             flush();
 182             return true;
 183         } catch (ResolutionException ex) {
 184             writeStatus(RemoteStatus.CORRALLED);
 185             writeInt(ex.id());
 186             writeObject(ex.getStackTrace());
 187             flush();
 188             return true;
 189         } catch (StoppedException ex) {
 190             writeStatus(RemoteStatus.STOPPED);
 191             flush();
 192             return true;
 193         } catch (Throwable ex) {
 194             writeStatus(RemoteStatus.TERMINATED);
 195             writeUTF(ex.getMessage());
 196             flush();
 197             return false;
 198         }
 199     }
 200 
 201     void commandLoop() {
 202         try {
 203             while (processCommand()) {
 204                 // condition is loop action
 205             }
 206         } catch (IOException ex) {
 207             // ignore
 208         }
 209     }
 210 
 211 }