1 /* 2 * Copyright (c) 1998, 2011, 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 26 /* 27 * This source code is provided to illustrate the usage of a given feature 28 * or technique and has been deliberately simplified. Additional steps 29 * required for a production-quality application, such as security checks, 30 * input validation and proper error handling, might not be present in 31 * this sample code. 32 */ 33 34 35 package com.sun.tools.example.debug.bdi; 36 37 import com.sun.jdi.*; 38 import com.sun.jdi.connect.LaunchingConnector; 39 import com.sun.jdi.connect.Connector; 40 import com.sun.jdi.connect.VMStartException; 41 import com.sun.jdi.connect.IllegalConnectorArgumentsException; 42 import java.io.*; 43 import java.util.Map; 44 import javax.swing.SwingUtilities; 45 46 47 class ChildSession extends Session { 48 49 private Process process; 50 51 private PrintWriter in; 52 private BufferedReader out; 53 private BufferedReader err; 54 55 private InputListener input; 56 private OutputListener output; 57 private OutputListener error; 58 59 public ChildSession(ExecutionManager runtime, 60 String userVMArgs, String cmdLine, 61 InputListener input, 62 OutputListener output, 63 OutputListener error, 64 OutputListener diagnostics) { 65 this(runtime, getVM(diagnostics, userVMArgs, cmdLine), 66 input, output, error, diagnostics); 67 } 68 69 public ChildSession(ExecutionManager runtime, 70 LaunchingConnector connector, 71 Map<String, Connector.Argument> arguments, 72 InputListener input, 73 OutputListener output, 74 OutputListener error, 75 OutputListener diagnostics) { 76 this(runtime, generalGetVM(diagnostics, connector, arguments), 77 input, output, error, diagnostics); 78 } 79 80 private ChildSession(ExecutionManager runtime, 81 VirtualMachine vm, 82 InputListener input, 83 OutputListener output, 84 OutputListener error, 85 OutputListener diagnostics) { 86 super(vm, runtime, diagnostics); 87 this.input = input; 88 this.output = output; 89 this.error = error; 90 } 91 92 @Override 93 public boolean attach() { 94 95 if (!connectToVMProcess()) { 96 diagnostics.putString("Could not launch VM"); 97 return false; 98 } 99 100 /* 101 * Create a Thread that will retrieve and display any output. 102 * Needs to be high priority, else debugger may exit before 103 * it can be displayed. 104 */ 105 106 //### Rename InputWriter and OutputReader classes 107 //### Thread priorities cribbed from ttydebug. Think about them. 108 109 OutputReader outputReader = 110 new OutputReader("output reader", "output", 111 out, output, diagnostics); 112 outputReader.setPriority(Thread.MAX_PRIORITY-1); 113 outputReader.start(); 114 115 OutputReader errorReader = 116 new OutputReader("error reader", "error", 117 err, error, diagnostics); 118 errorReader.setPriority(Thread.MAX_PRIORITY-1); 119 errorReader.start(); 120 121 InputWriter inputWriter = 122 new InputWriter("input writer", in, input); 123 inputWriter.setPriority(Thread.MAX_PRIORITY-1); 124 inputWriter.start(); 125 126 if (!super.attach()) { 127 if (process != null) { 128 process.destroy(); 129 process = null; 130 } 131 return false; 132 } 133 134 //### debug 135 //System.out.println("IO after attach: "+ inputWriter + " " + outputReader + " "+ errorReader); 136 137 return true; 138 } 139 140 @Override 141 public void detach() { 142 143 //### debug 144 //System.out.println("IO before detach: "+ inputWriter + " " + outputReader + " "+ errorReader); 145 146 super.detach(); 147 148 /* 149 inputWriter.quit(); 150 outputReader.quit(); 151 errorReader.quit(); 152 */ 153 154 if (process != null) { 155 process.destroy(); 156 process = null; 157 } 158 159 } 160 161 /** 162 * Launch child java interpreter, return host:port 163 */ 164 165 static private void dumpStream(OutputListener diagnostics, 166 InputStream stream) throws IOException { 167 BufferedReader in = 168 new BufferedReader(new InputStreamReader(stream)); 169 String line; 170 while ((line = in.readLine()) != null) { 171 diagnostics.putString(line); 172 } 173 } 174 175 static private void dumpFailedLaunchInfo(OutputListener diagnostics, 176 Process process) { 177 try { 178 dumpStream(diagnostics, process.getErrorStream()); 179 dumpStream(diagnostics, process.getInputStream()); 180 } catch (IOException e) { 181 diagnostics.putString("Unable to display process output: " + 182 e.getMessage()); 183 } 184 } 185 186 static private VirtualMachine getVM(OutputListener diagnostics, 187 String userVMArgs, 188 String cmdLine) { 189 VirtualMachineManager manager = Bootstrap.virtualMachineManager(); 190 LaunchingConnector connector = manager.defaultConnector(); 191 Map<String, Connector.Argument> arguments = connector.defaultArguments(); 192 arguments.get("options").setValue(userVMArgs); 193 arguments.get("main").setValue(cmdLine); 194 return generalGetVM(diagnostics, connector, arguments); 195 } 196 197 static private VirtualMachine generalGetVM(OutputListener diagnostics, 198 LaunchingConnector connector, 199 Map<String, Connector.Argument> arguments) { 200 VirtualMachine vm = null; 201 try { 202 diagnostics.putString("Starting child."); 203 vm = connector.launch(arguments); 204 } catch (IOException ioe) { 205 diagnostics.putString("Unable to start child: " + ioe.getMessage()); 206 } catch (IllegalConnectorArgumentsException icae) { 207 diagnostics.putString("Unable to start child: " + icae.getMessage()); 208 } catch (VMStartException vmse) { 209 diagnostics.putString("Unable to start child: " + vmse.getMessage() + '\n'); 210 dumpFailedLaunchInfo(diagnostics, vmse.process()); 211 } 212 return vm; 213 } 214 215 private boolean connectToVMProcess() { 216 if (vm == null) { 217 return false; 218 } 219 process = vm.process(); 220 in = new PrintWriter(new OutputStreamWriter(process.getOutputStream())); 221 //### Note small buffer sizes! 222 out = new BufferedReader(new InputStreamReader(process.getInputStream()), 1); 223 err = new BufferedReader(new InputStreamReader(process.getErrorStream()), 1); 224 return true; 225 } 226 227 /** 228 * Threads to handle application input/output. 229 */ 230 231 private static class OutputReader extends Thread { 232 233 private String streamName; 234 private BufferedReader stream; 235 private OutputListener output; 236 private OutputListener diagnostics; 237 private boolean running = true; 238 private char[] buffer = new char[512]; 239 240 OutputReader(String threadName, 241 String streamName, 242 BufferedReader stream, 243 OutputListener output, 244 OutputListener diagnostics) { 245 super(threadName); 246 this.streamName = streamName; 247 this.stream = stream; 248 this.output = output; 249 this.diagnostics = diagnostics; 250 } 251 252 @Override 253 public void run() { 254 try { 255 int count; 256 while (running && (count = stream.read(buffer, 0, 512)) != -1) { 257 if (count > 0) { 258 // Run in Swing event dispatcher thread. 259 final String chars = new String(buffer, 0, count); 260 SwingUtilities.invokeLater(new Runnable() { 261 @Override 262 public void run() { 263 output.putString(chars); 264 } 265 }); 266 } 267 //### Should we sleep briefly here? 268 } 269 } catch (IOException e) { 270 // Run in Swing event dispatcher thread. 271 SwingUtilities.invokeLater(new Runnable() { 272 @Override 273 public void run() { 274 diagnostics.putString("IO error reading " + 275 streamName + 276 " stream of child java interpreter"); 277 } 278 }); 279 } 280 } 281 } 282 283 private static class InputWriter extends Thread { 284 285 private PrintWriter stream; 286 private InputListener input; 287 private boolean running = true; 288 289 InputWriter(String threadName, 290 PrintWriter stream, 291 InputListener input) { 292 super(threadName); 293 this.stream = stream; 294 this.input = input; 295 } 296 297 @Override 298 public void run() { 299 String line; 300 while (running) { 301 line = input.getLine(); 302 stream.println(line); 303 // Should not be needed for println above! 304 stream.flush(); 305 } 306 } 307 } 308 309 }