001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001-2003, ThoughtWorks, Inc.
004: * 200 E. Randolph, 25th Floor
005: * Chicago, IL 60601 USA
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * + Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * + Redistributions in binary form must reproduce the above
016: * copyright notice, this list of conditions and the following
017: * disclaimer in the documentation and/or other materials provided
018: * with the distribution.
019: *
020: * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021: * names of its contributors may be used to endorse or promote
022: * products derived from this software without specific prior
023: * written permission.
024: *
025: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036: ********************************************************************************/package net.sourceforge.cruisecontrol.util;
037:
038: import java.io.BufferedReader;
039: import java.io.IOException;
040: import java.io.InputStreamReader;
041: import java.util.ArrayList;
042: import java.util.List;
043:
044: import org.apache.log4j.Logger;
045:
046: import net.sourceforge.cruisecontrol.CruiseControlException;
047:
048: /**
049: * Extends <cod>EnvCommandline</code> by adding stdout and stderr
050: * stream handling as well as some assertions to check for proper
051: * execution of the command.
052: *
053: * @author <a href="mailto:rjmpsmith@hotmail.com">Robert J. Smith</a>
054: */
055: public class ManagedCommandline extends EnvCommandline {
056:
057: private static final Logger LOG = Logger
058: .getLogger(ManagedCommandline.class);
059:
060: /**
061: * Holds the exit code from the command
062: */
063: private int exitCode;
064:
065: /**
066: * The stdout from the command as a string
067: */
068: private String stdout;
069:
070: /**
071: * The stdout from the command as a List of output lines
072: */
073: private List stdoutLines = new ArrayList();
074:
075: /**
076: * The stderr from the command as a string
077: */
078: private String stderr;
079:
080: /**
081: * The stderr from the command as a List of output lines
082: */
083: private List stderrLines = new ArrayList();
084:
085: /**
086: * Constructor which takes a command line string and attempts
087: * to parse it into it's various components.
088: *
089: * @param command The command
090: */
091: public ManagedCommandline(String command) {
092: super (command);
093: }
094:
095: /**
096: * Default constructor
097: */
098: public ManagedCommandline() {
099: super ();
100: }
101:
102: /**
103: * Returns the exit code of the command as reported by the OS.
104: *
105: * @return The exit code of the command
106: */
107: public int getExitCode() {
108: return exitCode;
109: }
110:
111: /**
112: * Returns the stdout from the command as a String
113: *
114: * @return The standard output of the command as a <code>String</code>
115: */
116: public String getStdoutAsString() {
117: return stdout;
118: }
119:
120: /**
121: * Returns the stdout from the command as a List of Strings where each
122: * String is one line of the output.
123: *
124: * @return The standard output of the command as a <code>List</code> of
125: * output lines.
126: */
127: public List getStdoutAsList() {
128: return stdoutLines;
129: }
130:
131: /**
132: * Returns the stderr from the command as a String
133: *
134: * @return The standard error of the command as a <code>String</code>
135: */
136: public String getStderrAsString() {
137: return stderr;
138: }
139:
140: /**
141: * Returns the stderr from the command as a List of Strings where each
142: * String is one line of the error.
143: *
144: * @return The standard error of the command as a <code>List</code> of
145: * output lines.
146: */
147: public List getStderrAsList() {
148: return stderrLines;
149: }
150:
151: /**
152: * Clear out the whole command line.
153: */
154: public void clear() {
155: super .clear();
156: clearArgs();
157: }
158:
159: /**
160: * Clear out the arguments and stored command output, but leave the
161: * executable in place for another operation.
162: */
163: public void clearArgs() {
164: exitCode = -1;
165: stdout = "";
166: stderr = "";
167: stdoutLines.clear();
168: stderrLines.clear();
169: super .clearArgs();
170: }
171:
172: /**
173: * Asserts that the stdout of the command does not contain a
174: * given <code>String</code>. Throws a
175: * <code>CruiseControlException</code> if it does.
176: *
177: * @param string
178: * The forbidden <code>String</code>
179: *
180: * @throws CruiseControlException
181: */
182: public void assertStdoutDoesNotContain(String string)
183: throws CruiseControlException {
184: if (stdout.indexOf(string) > -1) {
185: throw new CruiseControlException("The command \""
186: + this .toString()
187: + "\" returned the forbidden string \"" + string
188: + "\". \n" + "Stdout: " + stdout + "Stderr: "
189: + stderr);
190: }
191: }
192:
193: /**
194: * Asserts that the stdout of the command contains a
195: * given <code>String</code>. Throws a
196: * <code>CruiseControlException</code> if it does not.
197: *
198: * @param string
199: * The required <code>String</code>
200: *
201: * @throws CruiseControlException
202: */
203: public void assertStdoutContains(String string)
204: throws CruiseControlException {
205: if (stdout.indexOf(string) < 0) {
206: throw new CruiseControlException(
207: "The stdout of the command \""
208: + this .toString()
209: + "\" did not contain the required string \""
210: + string + "\". \n" + "Stdout: " + stdout
211: + "Stderr: " + stderr);
212: }
213: }
214:
215: /**
216: * Asserts that the stderr of the command does not contain a
217: * given <code>String</code>. Throws a
218: * <code>CruiseControlException</code> if it does.
219: *
220: * @param string
221: * The forbidden <code>String</code>
222: *
223: * @throws CruiseControlException
224: */
225: public void assertStderrDoesNotContain(String string)
226: throws CruiseControlException {
227: if (stderr.indexOf(string) > -1) {
228: throw new CruiseControlException("The command \""
229: + this .toString()
230: + "\" returned the forbidden string \"" + string
231: + "\". \n" + "Stdout: " + stdout + "Stderr: "
232: + stderr);
233: }
234: }
235:
236: /**
237: * Asserts that the exit code of the command matches an expected value.
238: * Throws a <code>CruiseControlException</code> if it does not.
239: *
240: * @param code
241: * The expected exit code of the command
242: *
243: * @throws CruiseControlException
244: */
245: public void assertExitCode(int code) throws CruiseControlException {
246: if (exitCode != code) {
247: throw new CruiseControlException("The command \""
248: + this .toString() + "\" returned exit code \""
249: + exitCode + "\" when \"" + code
250: + "\" was expected.\n" + "Stdout: " + stdout
251: + "Stderr: " + stderr);
252: }
253: }
254:
255: /**
256: * Asserts that the exit code of the command is not a given value. Throws a
257: * <code>CruiseControlException</code> if it is.
258: *
259: * @param code
260: * The expected exit code of the command
261: *
262: * @throws CruiseControlException
263: */
264: public void assertExitCodeNot(int code)
265: throws CruiseControlException {
266: if (exitCode == code) {
267: throw new CruiseControlException("The command \""
268: + this .toString() + "\" returned exit code \""
269: + exitCode + "\".\n" + "Stdout: " + stdout
270: + "Stderr: " + stderr);
271: }
272: }
273:
274: /**
275: * Asserts that the exit code of the command is greater than a given value.
276: * Throws a <code>CruiseControlException</code> if it is not.
277: *
278: * @param code
279: * The expected exit code of the command
280: *
281: * @throws CruiseControlException
282: */
283: public void assertExitCodeGreaterThan(int code)
284: throws CruiseControlException {
285: if (exitCode <= code) {
286: throw new CruiseControlException("The command \""
287: + this .toString() + "\" returned exit code \""
288: + exitCode + "\" when a value greater than \""
289: + code + "\" was expected.\n" + "Stdout: " + stdout
290: + "Stderr: " + stderr);
291: }
292: }
293:
294: /**
295: * Asserts that the exit code of the command is less than a given value.
296: * Throws a <code>CruiseControlException</code> if it is not.
297: *
298: * @param code
299: * The expected exit code of the command
300: *
301: * @throws CruiseControlException
302: */
303: public void assertExitCodeLessThan(int code)
304: throws CruiseControlException {
305: if (exitCode >= code) {
306: throw new CruiseControlException("The command \""
307: + this .toString() + "\" returned exit code \""
308: + exitCode + "\" when a value less than \"" + code
309: + "\" was expected.\n" + "Stdout: " + stdout
310: + "Stderr: " + stderr);
311: }
312: }
313:
314: /**
315: * Executes the command.
316: */
317: public Process execute() throws IOException {
318:
319: // Execute the command using the specified environment
320: Process proc = super .execute();
321:
322: // Capture the output of the command
323: BufferedReader stdoutStream = new BufferedReader(
324: new InputStreamReader(proc.getInputStream()));
325: BufferedReader stderrStream = new BufferedReader(
326: new InputStreamReader(proc.getErrorStream()));
327:
328: // Parse the stdout of the command
329: String line;
330: StringBuffer buff = new StringBuffer();
331: while ((line = stdoutStream.readLine()) != null) {
332: stdoutLines.add(line);
333: buff.append(line).append('\n');
334: }
335: stdout = buff.toString();
336:
337: // Parse the stderr of the command
338: buff.setLength(0);
339: while ((line = stderrStream.readLine()) != null) {
340: stderrLines.add(line);
341: buff.append(line).append('\n');
342: }
343: stderr = buff.toString();
344:
345: // Wait for the command to complete
346: try {
347: proc.waitFor();
348: } catch (InterruptedException e) {
349: LOG.error(
350: "Thread was interrupted while executing command \""
351: + this .toString() + "\".", e);
352: }
353:
354: // Close down our streams
355: stdoutStream.close();
356: stderrStream.close();
357:
358: // Set the exit code
359: exitCode = proc.exitValue();
360:
361: // Just to be compatible
362: return proc;
363: }
364: }
|