• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18 
19 /**
20  * @author Vitaly A. Provodin
21  */
22 
23 package org.apache.harmony.jpda.tests.jdwp.share;
24 
25 import java.io.IOException;
26 import java.util.Vector;
27 
28 import org.apache.harmony.jpda.tests.framework.LogWriter;
29 import org.apache.harmony.jpda.tests.framework.StreamRedirector;
30 import org.apache.harmony.jpda.tests.framework.TestErrorException;
31 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPDebuggeeWrapper;
32 import org.apache.harmony.jpda.tests.share.JPDATestOptions;
33 
34 /**
35  * This class provides basic DebuggeeWrapper implementation based on JUnit framework,
36  * which can launch and control debuggee process.
37  */
38 public class JDWPUnitDebuggeeProcessWrapper extends JDWPDebuggeeWrapper {
39 
40     /**
41      * Target VM debuggee process.
42      */
43     public Process process;
44 
45     protected StreamRedirector errRedir;
46     protected StreamRedirector outRedir;
47 
48     /**
49      * The expected exit code for the debuggee process.
50      */
51     private int expectedExitCode = 0;
52 
53     /**
54      * Creates new instance with given data.
55      *
56      * @param settings
57      *            test run options
58      * @param logWriter
59      *            where to print log messages
60      */
JDWPUnitDebuggeeProcessWrapper(JPDATestOptions settings, LogWriter logWriter)61     public JDWPUnitDebuggeeProcessWrapper(JPDATestOptions settings, LogWriter logWriter) {
62         super(settings, logWriter);
63     }
64 
65     /**
66      * Sets the expected exit code. This is meant to be used by tests that will request target
67      * VM termination with VirtualMachine.Exit command.
68      */
setExpectedExitCode(int expectedExitCode)69     public void setExpectedExitCode(int expectedExitCode) {
70         this.expectedExitCode = expectedExitCode;
71     }
72 
73     /**
74      * Launches process and redirects output.
75      */
launchProcessAndRedirectors(String cmdLine)76     public void launchProcessAndRedirectors(String cmdLine) throws IOException {
77         logWriter.println("Launch process: " + cmdLine);
78         process = launchProcess(cmdLine);
79         logWriter.println("Launched process");
80         if (process != null) {
81             logWriter.println("Start redirectors");
82             errRedir = new StreamRedirector(process.getErrorStream(), logWriter, "STDERR");
83             errRedir.setDaemon(true);
84             errRedir.start();
85             outRedir = new StreamRedirector(process.getInputStream(), logWriter, "STDOUT");
86             outRedir.setDaemon(true);
87             outRedir.start();
88             logWriter.println("Started redirectors");
89         }
90     }
91 
92     /**
93      * Waits for process to exit and closes output redirectors
94      */
finishProcessAndRedirectors()95     public void finishProcessAndRedirectors() {
96         if (process != null) {
97             try {
98                 logWriter.println("Waiting for process exit");
99                 WaitForProcessExit(process);
100                 logWriter.println("Finished process");
101             } catch (IOException e) {
102                 throw new TestErrorException("IOException in waiting for process exit: ", e);
103             }
104 
105             logWriter.println("Waiting for redirectors finish");
106             if (outRedir != null) {
107                 outRedir.exit();
108                 try {
109                     outRedir.join(settings.getTimeout());
110                 } catch (InterruptedException e) {
111                     logWriter.println("InterruptedException in stopping outRedirector: " + e);
112                 }
113                 if (outRedir.isAlive()) {
114                     logWriter.println("WARNING: redirector not stopped: " + outRedir.getName());
115                 }
116             }
117             if (errRedir != null) {
118                 errRedir.exit();
119                 try {
120                     errRedir.join(settings.getTimeout());
121                 } catch (InterruptedException e) {
122                     logWriter.println("InterruptedException in stopping errRedirector: " + e);
123                 }
124                 if (errRedir.isAlive()) {
125                     logWriter.println("WARNING: redirector not stopped: " + errRedir.getName());
126                 }
127             }
128             logWriter.println("Finished redirectors");
129         }
130     }
131 
132     /**
133      * Launches process with given command line.
134      *
135      * @param cmdLine
136      *            command line
137      * @return associated Process object or null if not available
138      * @throws IOException
139      *             if error occurred in launching process
140      */
launchProcess(String cmdLine)141     protected Process launchProcess(String cmdLine) throws IOException {
142 
143     	// Runtime.exec(String) does not preserve quoted arguments
144     	// process = Runtime.getRuntime().exec(cmdLine);
145 
146     	String args[] = splitCommandLine(cmdLine);
147     	process = Runtime.getRuntime().exec(args);
148         return process;
149     }
150 
151     /**
152      * Splits command line into arguments preserving spaces in quoted arguments
153      * either with single and double quotes (not prefixed by '\').
154      *
155      * @param cmd
156      *            command line
157      * @return associated Process object or null if not available
158      */
159 /*
160     public String[] splitCommandLine(String cmd) {
161 
162         // allocate array for parsed arguments
163         int max_argc = 250;
164         Vector argv = new Vector();
165 
166         // parse command line
167         int len = cmd.length();
168         if (len > 0) {
169             for (int arg = 0; arg < len;) {
170                 // skip initial spaces
171                 while (Character.isWhitespace(cmd.charAt(arg))) arg++;
172                 // parse non-spaced or quoted argument
173                 for (int p = arg; ; p++) {
174                     // check for ending separator
175                     if (p >= len || Character.isWhitespace(cmd.charAt(p))) {
176                     	if (p > len) p = len;
177                     	String val = cmd.substring(arg, p);
178                         argv.add(val);
179                         arg = p + 1;
180                         break;
181                     }
182 
183                     // check for starting quote
184                     if (cmd.charAt(p) == '\"') {
185                          char quote = cmd.charAt(p++);
186                          // skip all chars until terminating quote or end of line
187                          for (; p < len; p++) {
188                              // check for terminating quote
189                              if (cmd.charAt(p) == quote)
190                             	 break;
191                              // skip escaped quote
192                              if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote)
193                             	 p++;
194                          }
195                      }
196 
197                     // skip escaped quote
198                     if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == '\"') {
199                     	p++;
200                     }
201                 }
202             }
203         }
204 
205     	logWriter.println("Splitted command line: " + argv);
206         int size = argv.size();
207         String args[] = new String[size];
208         return (String[])argv.toArray(args);
209 	}
210 */
splitCommandLine(String cmd)211     public String[] splitCommandLine(String cmd) {
212 
213         int len = cmd.length();
214         char chars[] = new char[len];
215         Vector<String> argv = new Vector<String>();
216 
217         if (len > 0) {
218             for (int arg = 0; arg < len;) {
219                 // skip initial spaces
220                 while (Character.isWhitespace(cmd.charAt(arg))) arg++;
221                 // parse non-spaced or quoted argument
222                 for (int p = arg, i = 0; ; p++) {
223                     // check for starting quote
224                     if (p < len && (cmd.charAt(p) == '\"' || cmd.charAt(p) == '\'')) {
225                          char quote = cmd.charAt(p++);
226                          // copy all chars until terminating quote or end of line
227                          for (; p < len; p++) {
228                              // check for terminating quote
229                              if (cmd.charAt(p) == quote) {
230                             	 p++;
231                             	 break;
232                              }
233                              // preserve escaped quote
234                              if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote)
235                             	 p++;
236                              chars[i++] = cmd.charAt(p);
237                          }
238                      }
239 
240                     // check for ending separator
241                     if (p >= len || Character.isWhitespace(cmd.charAt(p))) {
242                     	String val = new String(chars, 0, i);
243                         argv.add(val);
244                         arg = p + 1;
245                         break;
246                     }
247 
248                     // preserve escaped quote
249                     if (cmd.charAt(p) == '\\' && (p+1) < len
250                     		&& (cmd.charAt(p+1) == '\"' || cmd.charAt(p+1) == '\'')) {
251                     	p++;
252                     }
253 
254                     // copy current char
255                     chars[i++] = cmd.charAt(p);
256                 }
257             }
258         }
259 
260     	logWriter.println("Splitted command line: " + argv);
261         int size = argv.size();
262         String args[] = new String[size];
263         return argv.toArray(args);
264 	}
265 
266     /**
267      * Waits for launched process to exit.
268      *
269      * @param process
270      *            associated Process object or null if not available
271      * @throws IOException
272      *             if any exception occurs in waiting
273      */
WaitForProcessExit(Process process)274     protected void WaitForProcessExit(Process process) throws IOException {
275         ProcessWaiter thrd = new ProcessWaiter();
276         thrd.setDaemon(true);
277         thrd.start();
278         try {
279             thrd.join(settings.getTimeout());
280         } catch (InterruptedException e) {
281             throw new TestErrorException(e);
282         }
283 
284         if (thrd.isAlive()) {
285             // ProcessWaiter thread is still running (after we wait until a timeout) but is
286             // waiting for the debuggee process to exit. We send an interrupt request to
287             // that thread so it receives an InterrupedException and terminates.
288             thrd.interrupt();
289         }
290 
291         try {
292             int exitCode = process.exitValue();
293             logWriter.println("Finished debuggee with exit code: " + exitCode);
294             if (exitCode != expectedExitCode) {
295                 throw new TestErrorException("Debuggee exited with code " + exitCode +
296                         " but we expected code " + expectedExitCode);
297             }
298         } catch (IllegalThreadStateException e) {
299             logWriter.printError("Terminate debuggee process");
300             throw new TestErrorException("Debuggee process did not finish during timeout", e);
301         } finally {
302             // dispose any resources of the process
303             process.destroy();
304         }
305     }
306 
307     /**
308      * Separate thread for waiting for process exit for specified timeout.
309      */
310     class ProcessWaiter extends Thread {
311         @Override
run()312         public void run() {
313             try {
314                 process.waitFor();
315             } catch (InterruptedException e) {
316                 logWriter.println("Ignoring exception in ProcessWaiter thread interrupted: " + e);
317             }
318         }
319     }
320 
321     @Override
start()322     public void start() {
323     }
324 
325     @Override
stop()326     public void stop() {
327     }
328 }
329