001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Vitaly A. Provodin
021: * @version $Revision: 1.4 $
022: */package org.apache.harmony.jpda.tests.jdwp.share;
023:
024: import java.io.IOException;
025: import java.util.Vector;
026:
027: import org.apache.harmony.jpda.tests.framework.LogWriter;
028: import org.apache.harmony.jpda.tests.framework.StreamRedirector;
029: import org.apache.harmony.jpda.tests.framework.TestErrorException;
030: import org.apache.harmony.jpda.tests.framework.jdwp.JDWPDebuggeeWrapper;
031: import org.apache.harmony.jpda.tests.share.JPDATestOptions;
032:
033: /**
034: * This class provides basic DebuggeeWrapper implementation based on JUnit framework,
035: * which can launch and control debuggee process.
036: */
037: public class JDWPUnitDebuggeeProcessWrapper extends JDWPDebuggeeWrapper {
038:
039: /**
040: * Target VM debuggee process.
041: */
042: public Process process;
043:
044: protected StreamRedirector errRedir;
045: protected StreamRedirector outRedir;
046:
047: /**
048: * Creates new instance with given data.
049: *
050: * @param settings
051: * test run options
052: * @param logWriter
053: * where to print log messages
054: */
055: public JDWPUnitDebuggeeProcessWrapper(JPDATestOptions settings,
056: LogWriter logWriter) {
057: super (settings, logWriter);
058: }
059:
060: /**
061: * Launches process and redirects output.
062: */
063: public void launchProcessAndRedirectors(String cmdLine)
064: throws IOException {
065: logWriter.println("Launch process: " + cmdLine);
066: process = launchProcess(cmdLine);
067: logWriter.println("Launched process");
068: if (process != null) {
069: logWriter.println("Start redirectors");
070: errRedir = new StreamRedirector(process.getErrorStream(),
071: logWriter, "STDERR");
072: errRedir.setDaemon(true);
073: errRedir.start();
074: outRedir = new StreamRedirector(process.getInputStream(),
075: logWriter, "STDOUT");
076: outRedir.setDaemon(true);
077: outRedir.start();
078: logWriter.println("Started redirectors");
079: }
080: }
081:
082: /**
083: * Waits for process to exit and closes uotput redirectors
084: */
085: public void finishProcessAndRedirectors() {
086: if (process != null) {
087: try {
088: logWriter.println("Waiting for process exit");
089: WaitForProcessExit(process);
090: logWriter.println("Finished process");
091: } catch (IOException e) {
092: throw new TestErrorException(
093: "IOException in waiting for process exit: ", e);
094: }
095:
096: logWriter.println("Waiting for redirectors finish");
097: if (outRedir != null) {
098: outRedir.exit();
099: try {
100: outRedir.join(settings.getTimeout());
101: } catch (InterruptedException e) {
102: logWriter
103: .println("InterruptedException in stopping outRedirector: "
104: + e);
105: }
106: if (outRedir.isAlive()) {
107: logWriter
108: .println("WARNING: redirector not stopped: "
109: + outRedir.getName());
110: }
111: }
112: if (errRedir != null) {
113: errRedir.exit();
114: try {
115: errRedir.join(settings.getTimeout());
116: } catch (InterruptedException e) {
117: logWriter
118: .println("InterruptedException in stopping errRedirector: "
119: + e);
120: }
121: if (errRedir.isAlive()) {
122: logWriter
123: .println("WARNING: redirector not stopped: "
124: + errRedir.getName());
125: }
126: }
127: logWriter.println("Finished redirectors");
128: }
129: }
130:
131: /**
132: * Launches process with given command line.
133: *
134: * @param cmdLine
135: * command line
136: * @return associated Process object or null if not available
137: * @throws IOException
138: * if error occurred in launching process
139: */
140: protected Process launchProcess(String cmdLine) throws IOException {
141:
142: // Runtime.exec(String) does not preserve quoted arguments
143: // process = Runtime.getRuntime().exec(cmdLine);
144:
145: String args[] = splitCommandLine(cmdLine);
146: process = Runtime.getRuntime().exec(args);
147: return process;
148: }
149:
150: /**
151: * Splits command line into arguments preserving spaces in quoted arguments
152: * either with single and double quotes (not prefixed by '\').
153: *
154: * @param cmdLine
155: * command line
156: * @return associated Process object or null if not available
157: * @throws IOException
158: * if error occurred in launching process
159: */
160: /*
161: public String[] splitCommandLine(String cmd) {
162:
163: // allocate array for parsed arguments
164: int max_argc = 250;
165: Vector argv = new Vector();
166:
167: // parse command line
168: int len = cmd.length();
169: if (len > 0) {
170: for (int arg = 0; arg < len;) {
171: // skip initial spaces
172: while (Character.isWhitespace(cmd.charAt(arg))) arg++;
173: // parse non-spaced or quoted argument
174: for (int p = arg; ; p++) {
175: // check for ending separator
176: if (p >= len || Character.isWhitespace(cmd.charAt(p))) {
177: if (p > len) p = len;
178: String val = cmd.substring(arg, p);
179: argv.add(val);
180: arg = p + 1;
181: break;
182: }
183:
184: // check for starting quote
185: if (cmd.charAt(p) == '\"') {
186: char quote = cmd.charAt(p++);
187: // skip all chars until terminating quote or end of line
188: for (; p < len; p++) {
189: // check for terminating quote
190: if (cmd.charAt(p) == quote)
191: break;
192: // skip escaped quote
193: if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == quote)
194: p++;
195: }
196: }
197:
198: // skip escaped quote
199: if (cmd.charAt(p) == '\\' && (p+1) < len && cmd.charAt(p+1) == '\"') {
200: p++;
201: }
202: }
203: }
204: }
205:
206: logWriter.println("Splitted command line: " + argv);
207: int size = argv.size();
208: String args[] = new String[size];
209: return (String[])argv.toArray(args);
210: }
211: */
212: public String[] splitCommandLine(String cmd) {
213:
214: int len = cmd.length();
215: char chars[] = new char[len];
216: Vector<String> argv = new Vector<String>();
217:
218: if (len > 0) {
219: for (int arg = 0; arg < len;) {
220: // skip initial spaces
221: while (Character.isWhitespace(cmd.charAt(arg)))
222: 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
227: && (cmd.charAt(p) == '\"' || cmd.charAt(p) == '\'')) {
228: char quote = cmd.charAt(p++);
229: // copy all chars until terminating quote or end of line
230: for (; p < len; p++) {
231: // check for terminating quote
232: if (cmd.charAt(p) == quote) {
233: p++;
234: break;
235: }
236: // preserve escaped quote
237: if (cmd.charAt(p) == '\\' && (p + 1) < len
238: && cmd.charAt(p + 1) == quote)
239: p++;
240: chars[i++] = cmd.charAt(p);
241: }
242: }
243:
244: // check for ending separator
245: if (p >= len
246: || Character.isWhitespace(cmd.charAt(p))) {
247: String val = new String(chars, 0, i);
248: argv.add(val);
249: arg = p + 1;
250: break;
251: }
252:
253: // preserve escaped quote
254: if (cmd.charAt(p) == '\\'
255: && (p + 1) < len
256: && (cmd.charAt(p + 1) == '\"' || cmd
257: .charAt(p + 1) == '\'')) {
258: p++;
259: }
260:
261: // copy current char
262: chars[i++] = cmd.charAt(p);
263: }
264: }
265: }
266:
267: logWriter.println("Splitted command line: " + argv);
268: int size = argv.size();
269: String args[] = new String[size];
270: return (String[]) argv.toArray((String[]) args);
271: }
272:
273: /**
274: * Waits for launched process to exit.
275: *
276: * @param process
277: * associated Process object or null if not available
278: * @throws IOException
279: * if any exception occurs in waiting
280: */
281: protected void WaitForProcessExit(Process process)
282: throws IOException {
283: ProcessWaiter thrd = new ProcessWaiter();
284: thrd.setDaemon(true);
285: thrd.start();
286: try {
287: thrd.join(settings.getTimeout());
288: } catch (InterruptedException e) {
289: throw new TestErrorException(e);
290: }
291:
292: if (thrd.isAlive()) {
293: thrd.interrupt();
294: }
295:
296: try {
297: int exitCode = process.exitValue();
298: logWriter.println("Finished debuggee with exit code: "
299: + exitCode);
300: } catch (IllegalThreadStateException e) {
301: logWriter.printError("Terminate debugge process");
302: process.destroy();
303: throw new TestErrorException(
304: "Debuggee process did not finish during timeout", e);
305: }
306:
307: // dispose any resources of the process
308: process.destroy();
309: }
310:
311: /**
312: * Separate thread for waiting for process exit for specified timeout.
313: */
314: class ProcessWaiter extends Thread {
315: public void run() {
316: try {
317: process.waitFor();
318: } catch (InterruptedException e) {
319: logWriter
320: .println("Ignoring exception in ProcessWaiter thread interrupted: "
321: + e);
322: }
323: }
324: }
325:
326: public void start() {
327: }
328:
329: public void stop() {
330: }
331: }
|