View Javadoc
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 }