001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001, 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.sourcecontrols.accurev;
037:
038: import net.sourceforge.cruisecontrol.CruiseControlException;
039: import net.sourceforge.cruisecontrol.sourcecontrols.Accurev;
040: import net.sourceforge.cruisecontrol.util.EnvCommandline;
041: import net.sourceforge.cruisecontrol.util.IO;
042: import net.sourceforge.cruisecontrol.util.StreamLogger;
043: import org.apache.log4j.Logger;
044:
045: import java.io.BufferedReader;
046: import java.io.File;
047: import java.io.IOException;
048: import java.io.InputStream;
049: import java.io.InputStreamReader;
050:
051: /**
052: * Allows to build and execute a valid accurev command line.
053: *
054: * @author <a href="mailto:Nicola_Orru@scee.net">Nicola Orru'</a>
055: * @author <a href="mailto:jason_chown@scee.net">Jason Chown </a>
056: */
057: public class AccurevCommandline extends EnvCommandline implements
058: AccurevInputParser, Runner {
059: private static final Logger LOG = Logger.getLogger(Accurev.class);
060: private boolean verbose;
061: private int runCount;
062: private int returnCode;
063: private AccurevCommand command;
064: private AccurevInputParser inputParser;
065: private static final int SUCCESS = 0;
066: private boolean syntaxError;
067: private Runner runner;
068:
069: /**
070: * Creates a new AccurevCommandline.
071: *
072: * @param command
073: */
074: public AccurevCommandline(AccurevCommand command) {
075: super ("accurev");
076: this .inputParser = this ;
077: this .runner = this ;
078: this .command = command;
079: createArgument().setValue(command.toString());
080: }
081:
082: /**
083: * Sets the Accurev stream to work in (-s stream)
084: *
085: * @param stream the stream name
086: */
087: public void setStream(String stream) {
088: addOption("-s", stream);
089: }
090:
091: /**
092: * Sets the Accurev depot to work in (-d depot)
093: *
094: * @param depot the depot name
095: */
096: public void setDepot(String depot) {
097: addOption("-d", depot);
098: }
099:
100: /**
101: * Sets the transaction comment (-c comment)
102: *
103: * @param comment the comment text. Quotes and escapes are not required.
104: */
105: public void setComment(String comment) {
106: addOption("-c", comment);
107: }
108:
109: /**
110: * Switches the -i option on
111: */
112: public void setInfoOnly() {
113: addArgument("-i");
114: }
115:
116: /**
117: * Selects a transaction range for hist as in (-t), single timespec
118: *
119: * @param time a timespec (can be a DateTimespec, a KeywordTimespec
120: */
121: public void setTransactionRange(Timespec time) {
122: addOption("-t", time.toString());
123: }
124:
125: /**
126: * Selects a transaction range for hist as in (-t), timespec span (a-b, a-, -b)
127: */
128: public void setTransactionRange(Timespec begin, Timespec end) {
129: StringBuffer buf = new StringBuffer();
130: if (begin != null) {
131: buf.append(begin);
132: }
133: buf.append("-");
134: if (end != null) {
135: buf.append(end);
136: }
137: addOption("-t", buf.toString());
138: }
139:
140: /**
141: * Selects a format for hist as in (-f)
142: */
143: public void setFormatExpanded(char format)
144: throws CruiseControlException {
145: if ("evstx".indexOf(format) < 0) {
146: throw new CruiseControlException(
147: "Invalid format specifier (use one of 'e' 'v' 's' 't' 'x') "
148: + format);
149: }
150: addOption("-f", new String(new char[] { format }));
151: }
152:
153: /**
154: * Adds an argument to the command line
155: *
156: * @param argument the argument to add (eg "-i"). Quotes and escape codes are not required.
157: */
158: public void addArgument(String argument) {
159: createArgument().setValue(argument);
160: }
161:
162: /**
163: * Sets the input parser, which is the object that handles Accurev's output as its input.
164: */
165: public void setInputParser(AccurevInputParser inputParser) {
166: this .inputParser = inputParser;
167: }
168:
169: /**
170: * Adds an option with an argument (eg. -s my_stream)
171: *
172: * @param option the option flag (eg. "-s")
173: * @param optionArgument the option argument ("eg. my_stream"). No need for quotes or escape characters.
174: */
175: public void addOption(String option, String optionArgument) {
176: createArgument().setValue(option);
177: createArgument().setValue(optionArgument);
178: }
179:
180: /**
181: * Selects all modified files in keep (as in -m)
182: */
183: public void selectModified() {
184: addArgument("-m");
185: }
186:
187: /**
188: * Selects the files to use reading them from the filelist (as in -l filelistName)
189: *
190: * @param filelistName the path of the file containing the list of files to process
191: */
192: public void setFileList(String filelistName) {
193: addOption("-l", filelistName);
194: }
195:
196: /**
197: * Selects the workspace to use, specifying its path in the local filesystem.
198: *
199: * @param workspace the workspace path
200: * @throws CruiseControlException if the path does not exist
201: */
202: public void setWorkspaceLocalPath(File workspace)
203: throws CruiseControlException {
204: this .setWorkingDirectory(workspace.getAbsolutePath());
205: }
206:
207: /**
208: * Selects the workspace to use, specifying its path in the local filesystem.
209: *
210: * @param workspace the workspace path
211: * @throws CruiseControlException if the path does not exist
212: */
213: public void setWorkspaceLocalPath(String workspace)
214: throws CruiseControlException {
215: this .setWorkingDirectory(workspace);
216: }
217:
218: /**
219: * Runs accurev and returns a reference to this.
220: */
221: public void run() {
222: if (verbose) {
223: LOG.info("Accurev: Executing '" + toString() + "'");
224: }
225: this .syntaxError = runner.execute(inputParser);
226: this .returnCode = runner.getReturnCode();
227: runCount++;
228: }
229:
230: /**
231: * Runs accurev and parses the output
232: *
233: * @return true if there are no parsing errors.
234: */
235: public boolean execute(AccurevInputParser inputParser) {
236: Process proc;
237: boolean error = false;
238: try {
239: proc = super .execute();
240: Thread stderr = new Thread(StreamLogger.getWarnPumper(LOG,
241: proc));
242: stderr.start();
243: InputStream input = proc.getInputStream();
244: try {
245: if (inputParser != null) {
246: error = !inputParser.parseStream(input);
247: }
248: returnCode = proc.waitFor();
249: stderr.join();
250: } finally {
251: IO.close(proc);
252: }
253: } catch (IOException e) {
254: LOG.error(e);
255: throw new RuntimeException(e.getMessage());
256: } catch (CruiseControlException e) {
257: LOG.error(e);
258: throw new RuntimeException(e.getMessage());
259: } catch (InterruptedException e) {
260: LOG.error(e);
261: throw new RuntimeException(e.getMessage());
262: }
263: return error;
264: }
265:
266: protected String[] buildCommandLine() {
267: return null;
268: }
269:
270: /**
271: * Default stream parser. It scans Accurev output and detects basic errors.
272: *
273: * @return true if no errors were found in Accurev's output.
274: */
275: public boolean parseStream(InputStream iStream)
276: throws CruiseControlException {
277: BufferedReader reader = new BufferedReader(
278: new InputStreamReader(iStream));
279: boolean badSyntax = false;
280: try {
281: while (true) {
282: String line = reader.readLine();
283: if (line == null) {
284: break;
285: }
286: if (line
287: .startsWith("AccuRev was unable to understand your command.")) {
288: badSyntax = true;
289: }
290: if (verbose) {
291: LOG.info(line);
292: }
293: }
294: } catch (IOException ex) {
295: throw new CruiseControlException("Error reading input");
296: }
297: return !badSyntax;
298: }
299:
300: /**
301: * Gets the last "accurev" exec's return code.
302: *
303: * @return the return code from the command line. Usually 0 is SUCCESS.
304: */
305: public int getReturnCode() {
306: return returnCode;
307: }
308:
309: /**
310: * Returns the accurev subcommand to be run by this command line object
311: * (eg. keep, synctime, update). The subcommand can be selected by the
312: * {@link #AccurevCommandline(AccurevCommand command)} constructor.
313: *
314: * @return the command
315: */
316: public AccurevCommand getCommand() {
317: return command;
318: }
319:
320: /**
321: * Enables/disables verbose logging
322: *
323: * @param verbose if true, verbose logging is enabled.
324: */
325: public void setVerbose(boolean verbose) {
326: this .verbose = verbose;
327: }
328:
329: /**
330: * Returns the verbose flag
331: *
332: * @return true if verbose logging is enabled
333: */
334: public boolean isVerbose() {
335: return verbose;
336: }
337:
338: /**
339: * Returns the run status
340: *
341: * @return true if the last command executed successfully (that is, returnCode == success and the
342: * parser didn't detect errors). It returns false if the command has not been run yet.
343: */
344: public boolean isSuccess() {
345: return (runCount > 0) && (!syntaxError)
346: && (returnCode == SUCCESS);
347: }
348:
349: /**
350: * Throws a CruiseControlException if the last command was not executed successfully.
351: *
352: * @throws CruiseControlException if the command was not executed successfully
353: */
354: public void assertSuccess() throws CruiseControlException {
355: if (!isSuccess()) {
356: throw new CruiseControlException("Error running "
357: + toString());
358: }
359: }
360:
361: /**
362: * Sets the runner
363: *
364: * @param runner
365: * the object that is in charge for provide some input to the parser
366: */
367: public void setRunner(Runner runner) {
368: this.runner = runner;
369: }
370: }
|