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 23package org.apache.harmony.jpda.tests.jdwp.share; 24 25import java.io.IOException; 26import java.util.Vector; 27 28import org.apache.harmony.jpda.tests.framework.LogWriter; 29import org.apache.harmony.jpda.tests.framework.StreamRedirector; 30import org.apache.harmony.jpda.tests.framework.TestErrorException; 31import org.apache.harmony.jpda.tests.framework.jdwp.JDWPDebuggeeWrapper; 32import 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 */ 38public 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 * Creates new instance with given data. 50 * 51 * @param settings 52 * test run options 53 * @param logWriter 54 * where to print log messages 55 */ 56 public JDWPUnitDebuggeeProcessWrapper(JPDATestOptions settings, LogWriter logWriter) { 57 super(settings, logWriter); 58 } 59 60 /** 61 * Launches process and redirects output. 62 */ 63 public void launchProcessAndRedirectors(String cmdLine) throws IOException { 64 logWriter.println("Launch process: " + cmdLine); 65 process = launchProcess(cmdLine); 66 logWriter.println("Launched process"); 67 if (process != null) { 68 logWriter.println("Start redirectors"); 69 errRedir = new StreamRedirector(process.getErrorStream(), logWriter, "STDERR"); 70 errRedir.setDaemon(true); 71 errRedir.start(); 72 outRedir = new StreamRedirector(process.getInputStream(), logWriter, "STDOUT"); 73 outRedir.setDaemon(true); 74 outRedir.start(); 75 logWriter.println("Started redirectors"); 76 } 77 } 78 79 /** 80 * Waits for process to exit and closes uotput redirectors 81 */ 82 public void finishProcessAndRedirectors() { 83 if (process != null) { 84 try { 85 logWriter.println("Waiting for process exit"); 86 WaitForProcessExit(process); 87 logWriter.println("Finished process"); 88 } catch (IOException e) { 89 throw new TestErrorException("IOException in waiting for process exit: ", e); 90 } 91 92 logWriter.println("Waiting for redirectors finish"); 93 if (outRedir != null) { 94 outRedir.exit(); 95 try { 96 outRedir.join(settings.getTimeout()); 97 } catch (InterruptedException e) { 98 logWriter.println("InterruptedException in stopping outRedirector: " + e); 99 } 100 if (outRedir.isAlive()) { 101 logWriter.println("WARNING: redirector not stopped: " + outRedir.getName()); 102 } 103 } 104 if (errRedir != null) { 105 errRedir.exit(); 106 try { 107 errRedir.join(settings.getTimeout()); 108 } catch (InterruptedException e) { 109 logWriter.println("InterruptedException in stopping errRedirector: " + e); 110 } 111 if (errRedir.isAlive()) { 112 logWriter.println("WARNING: redirector not stopped: " + errRedir.getName()); 113 } 114 } 115 logWriter.println("Finished redirectors"); 116 } 117 } 118 119 /** 120 * Launches process with given command line. 121 * 122 * @param cmdLine 123 * command line 124 * @return associated Process object or null if not available 125 * @throws IOException 126 * if error occurred in launching process 127 */ 128 protected Process launchProcess(String cmdLine) throws IOException { 129 130 // Runtime.exec(String) does not preserve quoted arguments 131 // process = Runtime.getRuntime().exec(cmdLine); 132 133 String args[] = splitCommandLine(cmdLine); 134 process = Runtime.getRuntime().exec(args); 135 return process; 136 } 137 138 /** 139 * Splits command line into arguments preserving spaces in quoted arguments 140 * either with single and double quotes (not prefixed by '\'). 141 * 142 * @param cmdLine 143 * command line 144 * @return associated Process object or null if not available 145 * @throws IOException 146 * if error occurred in launching process 147 */ 148/* 149 public String[] splitCommandLine(String cmd) { 150 151 // allocate array for parsed arguments 152 int max_argc = 250; 153 Vector argv = new Vector(); 154 155 // parse command line 156 int len = cmd.length(); 157 if (len > 0) { 158 for (int arg = 0; arg < len;) { 159 // skip initial spaces 160 while (Character.isWhitespace(cmd.charAt(arg))) arg++; 161 // parse non-spaced or quoted argument 162 for (int p = arg; ; p++) { 163 // check for ending separator 164 if (p >= len || Character.isWhitespace(cmd.charAt(p))) { 165 if (p > len) p = len; 166 String val = cmd.substring(arg, p); 167 argv.add(val); 168 arg = p + 1; 169 break; 170 } 171 172 // check for starting quote 173 if (cmd.charAt(p) == '\"') { 174 char quote = cmd.charAt(p++); 175 // skip all chars until terminating quote or end of line 176 for (; p < len; p++) { 177 // check for terminating quote 178 if (cmd.charAt(p) == quote) 179 break; 180 // skip escaped quote 181 if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote) 182 p++; 183 } 184 } 185 186 // skip escaped quote 187 if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == '\"') { 188 p++; 189 } 190 } 191 } 192 } 193 194 logWriter.println("Splitted command line: " + argv); 195 int size = argv.size(); 196 String args[] = new String[size]; 197 return (String[])argv.toArray(args); 198 } 199*/ 200 public String[] splitCommandLine(String cmd) { 201 202 int len = cmd.length(); 203 char chars[] = new char[len]; 204 Vector<String> argv = new Vector<String>(); 205 206 if (len > 0) { 207 for (int arg = 0; arg < len;) { 208 // skip initial spaces 209 while (Character.isWhitespace(cmd.charAt(arg))) arg++; 210 // parse non-spaced or quoted argument 211 for (int p = arg, i = 0; ; p++) { 212 // check for starting quote 213 if (p < len && (cmd.charAt(p) == '\"' || cmd.charAt(p) == '\'')) { 214 char quote = cmd.charAt(p++); 215 // copy all chars until terminating quote or end of line 216 for (; p < len; p++) { 217 // check for terminating quote 218 if (cmd.charAt(p) == quote) { 219 p++; 220 break; 221 } 222 // preserve escaped quote 223 if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote) 224 p++; 225 chars[i++] = cmd.charAt(p); 226 } 227 } 228 229 // check for ending separator 230 if (p >= len || Character.isWhitespace(cmd.charAt(p))) { 231 String val = new String(chars, 0, i); 232 argv.add(val); 233 arg = p + 1; 234 break; 235 } 236 237 // preserve escaped quote 238 if (cmd.charAt(p) == '\\' && (p+1) < len 239 && (cmd.charAt(p+1) == '\"' || cmd.charAt(p+1) == '\'')) { 240 p++; 241 } 242 243 // copy current char 244 chars[i++] = cmd.charAt(p); 245 } 246 } 247 } 248 249 logWriter.println("Splitted command line: " + argv); 250 int size = argv.size(); 251 String args[] = new String[size]; 252 return (String[])argv.toArray((String[])args); 253 } 254 255 /** 256 * Waits for launched process to exit. 257 * 258 * @param process 259 * associated Process object or null if not available 260 * @throws IOException 261 * if any exception occurs in waiting 262 */ 263 protected void WaitForProcessExit(Process process) throws IOException { 264 ProcessWaiter thrd = new ProcessWaiter(); 265 thrd.setDaemon(true); 266 thrd.start(); 267 try { 268 thrd.join(settings.getTimeout()); 269 } catch (InterruptedException e) { 270 throw new TestErrorException(e); 271 } 272 273 if (thrd.isAlive()) { 274 thrd.interrupt(); 275 } 276 277 try { 278 int exitCode = process.exitValue(); 279 logWriter.println("Finished debuggee with exit code: " + exitCode); 280 } catch (IllegalThreadStateException e) { 281 logWriter.printError("Terminate debuggee process"); 282 process.destroy(); 283 throw new TestErrorException("Debuggee process did not finish during timeout", e); 284 } 285 286 // dispose any resources of the process 287 process.destroy(); 288 } 289 290 /** 291 * Separate thread for waiting for process exit for specified timeout. 292 */ 293 class ProcessWaiter extends Thread { 294 public void run() { 295 try { 296 process.waitFor(); 297 } catch (InterruptedException e) { 298 logWriter.println("Ignoring exception in ProcessWaiter thread interrupted: " + e); 299 } 300 } 301 } 302 303 public void start() { 304 } 305 306 public void stop() { 307 } 308} 309