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.sourcecontrols;
037:
038: import java.io.BufferedReader;
039: import java.io.File;
040: import java.io.IOException;
041: import java.io.InputStream;
042: import java.io.InputStreamReader;
043: import java.text.ParseException;
044: import java.text.SimpleDateFormat;
045: import java.util.ArrayList;
046: import java.util.Date;
047: import java.util.List;
048: import java.util.Map;
049:
050: import net.sourceforge.cruisecontrol.CruiseControlException;
051: import net.sourceforge.cruisecontrol.Modification;
052: import net.sourceforge.cruisecontrol.Modification.ModifiedFile;
053: import net.sourceforge.cruisecontrol.SourceControl;
054: import net.sourceforge.cruisecontrol.util.Commandline;
055: import net.sourceforge.cruisecontrol.util.IO;
056: import net.sourceforge.cruisecontrol.util.ValidationHelper;
057:
058: import org.apache.log4j.Logger;
059:
060: /**
061: * This class implements the SourceControl class for a Plastic SCM repository.
062: *
063: * @author <a href="mailto:rdealba@codicesoftware.com">Ruben de Alba</a>
064: */
065: public class PlasticSCM implements SourceControl {
066:
067: private static final Logger LOG = Logger
068: .getLogger(PlasticSCM.class);
069:
070: private String wkspath;
071: private String branch;
072: private String repository;
073:
074: private SourceControlProperties properties = new SourceControlProperties();
075:
076: //Format passed to Plastic SCM
077: public static final String DATEFORMAT = "dd.MM.yyyy.HH.mm.ss";
078: private final SimpleDateFormat dateFormat = new SimpleDateFormat(
079: DATEFORMAT);
080:
081: public static final String DELIMITER = "#@&@#";
082: public static final String QUERYFORMAT = DELIMITER + "{item}"
083: + DELIMITER + "{owner}" + DELIMITER + "{date}";
084:
085: /**
086: * Selects a workspace
087: *
088: * @param wkspath
089: * the path of the workspace to work in, in the local filesystem
090: */
091: public void setWkspath(String wkspath) {
092: this .wkspath = wkspath;
093: }
094:
095: /**
096: * Selects a branch
097: *
098: * @param branch
099: * the branch in which changes will be looked for.
100: */
101: public void setBranch(String branch) {
102: this .branch = branch;
103: }
104:
105: /**
106: * Selects a repository
107: *
108: * @param repository
109: * the repository in which changes will be looked for.
110: */
111: public void setRepository(String repository) {
112: this .repository = repository;
113: }
114:
115: public void setProperty(String property) {
116: properties.assignPropertyName(property);
117: }
118:
119: public Map getProperties() {
120: return properties.getPropertiesAndReset();
121: }
122:
123: /**
124: * Validate the attributes.
125: */
126: public void validate() throws CruiseControlException {
127: ValidationHelper.assertIsSet(wkspath, "wkspath", this
128: .getClass());
129: ValidationHelper.assertIsSet(branch, "branch", this .getClass());
130:
131: File workingDir = new File(wkspath);
132: ValidationHelper.assertTrue(workingDir.exists(),
133: "'wkspath' must be an existing directory. Was <"
134: + wkspath + ">");
135: ValidationHelper.assertTrue(workingDir.isDirectory(),
136: "'wkspath' must be an existing directory, not a file. Was <"
137: + wkspath + ">");
138:
139: }
140:
141: /**
142: * Returns an {@link java.util.List List} of {@link Modification}s detailing all the changes between now
143: * and the last build.
144: *
145: *@param lastBuild the last build time
146: *@param now time now, or time to check
147: *@return the list of modifications, an empty (not null) list if no
148: * modifications or if developer had checked in files since quietPeriod seconds ago.
149: *
150: */
151: public List getModifications(Date lastBuild, Date now) {
152: List modifications;
153: try {
154: Commandline commandLine = buildFindCommand(lastBuild, now);
155: Process p = commandLine.execute();
156: InputStream input = p.getInputStream();
157: modifications = parseStream(input);
158: p.waitFor();
159: IO.close(p);
160:
161: } catch (Exception e) {
162: LOG
163: .error(
164: "Error in executing the PlasticSCM command : ",
165: e);
166: return new ArrayList();
167: }
168:
169: if (!modifications.isEmpty()) {
170: properties.modificationFound();
171: }
172:
173: return modifications;
174: }
175:
176: /**
177: * Build the Plastic SCM find command.
178: */
179: protected Commandline buildFindCommand(Date lastBuild, Date now)
180: throws CruiseControlException {
181: Commandline commandLine = new Commandline();
182:
183: commandLine.setWorkingDirectory(wkspath);
184:
185: commandLine.setExecutable("cm");
186: commandLine.createArgument("find");
187: commandLine.createArgument("revision");
188: commandLine.createArgument("where");
189: commandLine.createArguments("branch", "=");
190: commandLine.createArgument("'" + branch + "'");
191: commandLine.createArguments("and", "revno");
192: commandLine.createArguments("!=", "'CO'");
193: commandLine.createArguments("and", "date");
194: commandLine.createArguments("between", "'"
195: + dateFormat.format(lastBuild) + "'");
196: commandLine.createArguments("and", "'" + dateFormat.format(now)
197: + "'");
198:
199: if (repository != null) {
200: commandLine.createArguments("on", "repository");
201: commandLine.createArgument("'" + repository + "'");
202: }
203:
204: commandLine.createArgument("--dateformat=\"" + DATEFORMAT
205: + "\"");
206: commandLine.createArgument("--format=\"" + QUERYFORMAT + "\"");
207:
208: return commandLine;
209: }
210:
211: /**
212: * Parse the find command output.
213: */
214: protected List parseStream(InputStream input) throws IOException,
215: ParseException {
216: ArrayList modifications = new ArrayList();
217: ArrayList filemodifications = new ArrayList();
218: BufferedReader reader = new BufferedReader(
219: new InputStreamReader(input));
220: String line;
221:
222: while ((line = reader.readLine()) != null) {
223:
224: if (!line.startsWith(DELIMITER)) {
225: continue;
226: }
227:
228: if (!line.equals("") && !line.startsWith("Total:")) {
229: String[] fields = line.split(DELIMITER);
230: File file = new File(fields[1]);
231: Modification mod = new Modification("plasticscm");
232: ModifiedFile modfile = mod.createModifiedFile(file
233: .getName(), file.getParent());
234: mod.userName = fields[2];
235: mod.modifiedTime = dateFormat.parse(fields[3]);
236: if (file.exists() && file.isFile()
237: && !filemodifications.contains(file)) {
238: filemodifications.add(file);
239: modifications.add(mod);
240: }
241: }
242: }
243: return modifications;
244: }
245: }
|