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 cmdLine 156 * command line 157 * @return associated Process object or null if not available 158 * @throws IOException 159 * if error occurred in launching process 160 */ 161 /* 162 public String[] splitCommandLine(String cmd) { 163 164 // allocate array for parsed arguments 165 int max_argc = 250; 166 Vector argv = new Vector(); 167 168 // parse command line 169 int len = cmd.length(); 170 if (len > 0) { 171 for (int arg = 0; arg < len;) { 172 // skip initial spaces 173 while (Character.isWhitespace(cmd.charAt(arg))) arg++; 174 // parse non-spaced or quoted argument 175 for (int p = arg; ; p++) { 176 // check for ending separator 177 if (p >= len || Character.isWhitespace(cmd.charAt(p))) { 178 if (p > len) p = len; 179 String val = cmd.substring(arg, p); 180 argv.add(val); 181 arg = p + 1; 182 break; 183 } 184 185 // check for starting quote 186 if (cmd.charAt(p) == '\"') { 187 char quote = cmd.charAt(p++); 188 // skip all chars until terminating quote or end of line 189 for (; p < len; p++) { 190 // check for terminating quote 191 if (cmd.charAt(p) == quote) 192 break; 193 // skip escaped quote 194 if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote) 195 p++; 196 } 197 } 198 199 // skip escaped quote 200 if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == '\"') { 201 p++; 202 } 203 } 204 } 205 } 206 207 logWriter.println("Splitted command line: " + argv); 208 int size = argv.size(); 209 String args[] = new String[size]; 210 return (String[])argv.toArray(args); 211 } 212 */ splitCommandLine(String cmd)213 public String[] splitCommandLine(String cmd) { 214 215 int len = cmd.length(); 216 char chars[] = new char[len]; 217 Vector<String> argv = new Vector<String>(); 218 219 if (len > 0) { 220 for (int arg = 0; arg < len;) { 221 // skip initial spaces 222 while (Character.isWhitespace(cmd.charAt(arg))) arg++; 223 // parse non-spaced or quoted argument 224 for (int p = arg, i = 0; ; p++) { 225 // check for starting quote 226 if (p < len && (cmd.charAt(p) == '\"' || cmd.charAt(p) == '\'')) { 227 char quote = cmd.charAt(p++); 228 // copy all chars until terminating quote or end of line 229 for (; p < len; p++) { 230 // check for terminating quote 231 if (cmd.charAt(p) == quote) { 232 p++; 233 break; 234 } 235 // preserve escaped quote 236 if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote) 237 p++; 238 chars[i++] = cmd.charAt(p); 239 } 240 } 241 242 // check for ending separator 243 if (p >= len || Character.isWhitespace(cmd.charAt(p))) { 244 String val = new String(chars, 0, i); 245 argv.add(val); 246 arg = p + 1; 247 break; 248 } 249 250 // preserve escaped quote 251 if (cmd.charAt(p) == '\\' && (p+1) < len 252 && (cmd.charAt(p+1) == '\"' || cmd.charAt(p+1) == '\'')) { 253 p++; 254 } 255 256 // copy current char 257 chars[i++] = cmd.charAt(p); 258 } 259 } 260 } 261 262 logWriter.println("Splitted command line: " + argv); 263 int size = argv.size(); 264 String args[] = new String[size]; 265 return (String[])argv.toArray((String[])args); 266 } 267 268 /** 269 * Waits for launched process to exit. 270 * 271 * @param process 272 * associated Process object or null if not available 273 * @throws IOException 274 * if any exception occurs in waiting 275 */ WaitForProcessExit(Process process)276 protected void WaitForProcessExit(Process process) throws IOException { 277 ProcessWaiter thrd = new ProcessWaiter(); 278 thrd.setDaemon(true); 279 thrd.start(); 280 try { 281 thrd.join(settings.getTimeout()); 282 } catch (InterruptedException e) { 283 throw new TestErrorException(e); 284 } 285 286 if (thrd.isAlive()) { 287 // ProcessWaiter thread is still running (after we wait until a timeout) but is 288 // waiting for the debuggee process to exit. We send an interrupt request to 289 // that thread so it receives an InterrupedException and terminates. 290 thrd.interrupt(); 291 } 292 293 try { 294 int exitCode = process.exitValue(); 295 logWriter.println("Finished debuggee with exit code: " + exitCode); 296 if (exitCode != expectedExitCode) { 297 throw new TestErrorException("Debuggee exited with code " + exitCode + 298 " but we expected code " + expectedExitCode); 299 } 300 } catch (IllegalThreadStateException e) { 301 logWriter.printError("Terminate debuggee process"); 302 throw new TestErrorException("Debuggee process did not finish during timeout", e); 303 } finally { 304 // dispose any resources of the process 305 process.destroy(); 306 } 307 } 308 309 /** 310 * Separate thread for waiting for process exit for specified timeout. 311 */ 312 class ProcessWaiter extends Thread { run()313 public void run() { 314 try { 315 process.waitFor(); 316 } catch (InterruptedException e) { 317 logWriter.println("Ignoring exception in ProcessWaiter thread interrupted: " + e); 318 } 319 } 320 } 321 start()322 public void start() { 323 } 324 stop()325 public void stop() { 326 } 327 } 328