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;
037:
038: import java.io.BufferedReader;
039: import java.io.IOException;
040: import java.io.InputStream;
041: import java.io.InputStreamReader;
042: import java.util.ArrayList;
043: import java.util.Date;
044: import java.util.List;
045: import java.util.Map;
046: import java.util.StringTokenizer;
047:
048: import net.sourceforge.cruisecontrol.CruiseControlException;
049: import net.sourceforge.cruisecontrol.Modification;
050: import net.sourceforge.cruisecontrol.SourceControl;
051: import net.sourceforge.cruisecontrol.sourcecontrols.accurev.AccurevCommand;
052: import net.sourceforge.cruisecontrol.sourcecontrols.accurev.AccurevCommandline;
053: import net.sourceforge.cruisecontrol.sourcecontrols.accurev.AccurevInputParser;
054: import net.sourceforge.cruisecontrol.sourcecontrols.accurev.DateTimespec;
055: import net.sourceforge.cruisecontrol.sourcecontrols.accurev.Runner;
056:
057: import org.apache.log4j.Logger;
058:
059: /**
060: * This class handles all Accurev aspects of determining the modifications since the last good
061: * build.
062: *
063: * @author <a href="mailto:jason_chown@scee.net">Jason Chown </a>
064: * @author <a href="mailto:Nicola_Orru@scee.net">Nicola Orru'</a>
065: */
066: public class Accurev implements SourceControl, AccurevInputParser {
067: private static final Logger LOG = Logger.getLogger(Accurev.class);
068: private String stream;
069: private boolean verbose;
070: private ArrayList modifications;
071: private Runner runner;
072: private SourceControlProperties properties = new SourceControlProperties();
073:
074: /**
075: * Sets the Accurev stream to search for changes
076: *
077: * @param stream the name of the stream
078: */
079: public void setStream(String stream) {
080: this .stream = stream;
081: }
082:
083: /**
084: * Enables/disables verbose logging
085: *
086: * @param verbose set to true to enable verbose logging
087: */
088: public void setVerbose(boolean verbose) {
089: this .verbose = verbose;
090: }
091:
092: /**
093: * Choose a property to be set if the project has modifications
094: *
095: * @param propertyName the name of the property
096: */
097: public void setProperty(String propertyName) {
098: properties.assignPropertyName(propertyName);
099: }
100:
101: public void validate() throws CruiseControlException {
102: if (stream == null) {
103: throw new CruiseControlException(
104: "'stream' is a required attribute for Accurev");
105: }
106: }
107:
108: /**
109: * Calls "accurev hist -s [stream] -t "[now] - [lastBuild]" or something like that ; )
110: *
111: * @param lastBuild the date and time of the last successful build
112: * @param now the current date and time
113: * @return the List of all detected modifications
114: */
115: public List getModifications(Date lastBuild, Date now) {
116: LOG.info("Accurev: getting modifications for " + stream);
117: AccurevCommandline hist = AccurevCommand.HIST.create();
118: if (runner != null) {
119: hist.setRunner(runner);
120: }
121: hist.setVerbose(verbose);
122: hist.setInputParser(this );
123: hist.setStream(stream);
124: hist.setTransactionRange(new DateTimespec(lastBuild),
125: new DateTimespec(now));
126: hist.run();
127: return modifications;
128: }
129:
130: /**
131: * Parse the output from Accurev. These are lines of the form: <code>
132: * transaction <id>; <verb>; YYYY/MM/DD hh:mm:ss ; user: <user>
133: * # <comment>
134: * \.\PathTo\FileChanged.cpp <version>
135: * </code>
136: * <p/>
137: * Where <verb>can be promote, chstream or purge. There can be multiple lines of comments and
138: * files.
139: *
140: * @param input the output of the "accurev hist" command run
141: * @return true at the end
142: * @throws IOException
143: */
144: public boolean parseStream(InputStream input) throws IOException,
145: CruiseControlException {
146: modifications = new ArrayList();
147: Modification modification = null;
148: BufferedReader reader = new BufferedReader(
149: new InputStreamReader(input));
150: while (true) {
151: String line = reader.readLine();
152: if (line == null) {
153: break;
154: }
155: LOG.debug(line);
156: if (line.startsWith("transaction")) {
157: // transaction <id>; <verb>; YYYY/MM/DD hh:mm:ss ; user: <user>
158: modification = new Modification();
159: String[] parts = getParts(line);
160: modification.comment = "";
161: modification.revision = parts[0].substring(parts[0]
162: .indexOf(' ') + 1);
163: modification.type = parts[1].trim();
164: modification.modifiedTime = DateTimespec.parse(parts[2]
165: .trim());
166: modification.userName = parts[3].substring(6).trim();
167: modifications.add(modification);
168: properties.modificationFound();
169: } else if (line.startsWith(" #")) {
170: // # Comment
171: if (modification != null) {
172: modification.comment += line.substring(3) + "\n";
173: } else {
174: LOG.warn("Comment outside modification - skipping");
175: }
176: // Accurev is returning always \\ instead of File.separatorChar
177: } else if (line.startsWith(" \\.\\")
178: || line.startsWith(" /./")) {
179: // ...but just for the sake of paranoia...
180: final char separator = line.charAt(2);
181: final int lastSlash = line.lastIndexOf(separator);
182: int lastSpace = line.lastIndexOf(' ');
183: lastSpace = line.lastIndexOf(' ', lastSpace - 1);
184: if (lastSpace > lastSlash) {
185: String fileName = line.substring(lastSlash + 1,
186: lastSpace);
187: String folderName = ((lastSlash > 5) ? line
188: .substring(5, lastSlash) : line
189: .substring(5)).replace(separator, '/');
190: Modification.ModifiedFile modfile = modification
191: .createModifiedFile(fileName, folderName);
192: modfile.action = "change";
193: }
194: }
195: }
196: return true;
197: }
198:
199: private String[] getParts(String line) {
200: List partsList = new ArrayList();
201: StringTokenizer tokenizer = new StringTokenizer(line, ";");
202: while (tokenizer.hasMoreTokens()) {
203: partsList.add(tokenizer.nextToken());
204: }
205: String[] parts = new String[partsList.size()];
206: partsList.toArray(parts);
207: return parts;
208: }
209:
210: public void setRunner(Runner runner) {
211: this .runner = runner;
212: }
213:
214: public Map getProperties() {
215: return properties.getPropertiesAndReset();
216: }
217: }
|