KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > sourcecontrols > SnapshotCM


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2004, ThoughtWorks, Inc.
4  * 651 W Washington Ave. Suite 600
5  * Chicago, IL 60661 USA
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * + Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * + Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  *
20  * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
21  * names of its contributors may be used to endorse or promote
22  * products derived from this software without specific prior
23  * written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  ********************************************************************************/

37 package net.sourceforge.cruisecontrol.sourcecontrols;
38
39 import java.io.BufferedReader JavaDoc;
40 import java.io.IOException JavaDoc;
41 import java.io.InputStream JavaDoc;
42 import java.io.InputStreamReader JavaDoc;
43 import java.text.MessageFormat JavaDoc;
44 import java.text.ParseException JavaDoc;
45 import java.text.SimpleDateFormat JavaDoc;
46 import java.util.ArrayList JavaDoc;
47 import java.util.Date JavaDoc;
48 import java.util.Hashtable JavaDoc;
49 import java.util.Iterator JavaDoc;
50 import java.util.List JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.StringTokenizer JavaDoc;
53
54 import net.sourceforge.cruisecontrol.CruiseControlException;
55 import net.sourceforge.cruisecontrol.Modification;
56 import net.sourceforge.cruisecontrol.SourceControl;
57 import net.sourceforge.cruisecontrol.util.StreamPumper;
58 import net.sourceforge.cruisecontrol.util.ValidationHelper;
59
60 import org.apache.log4j.Logger;
61
62 /**
63  * Retrieves change history from SnapshotCM source control using whist command.
64  *
65  * @author patrick.conant@hp.com
66  */

67
68 public class SnapshotCM implements SourceControl {
69     /** Date format required by commands passed to SnapshotCM */
70     private final SimpleDateFormat JavaDoc inDateFormatter = new SimpleDateFormat JavaDoc("yyyy.MM.dd.HH.mm.ss");
71
72     public static final String JavaDoc OUT_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss";
73     /** Date format returned in the output of SnapshotCM commands. */
74     private final SimpleDateFormat JavaDoc outDateFormatter = new SimpleDateFormat JavaDoc(OUT_DATE_FORMAT);
75
76     private static final MessageFormat JavaDoc EXECUTABLE = new MessageFormat JavaDoc("whist -RA -c{0} \"{1}\"");
77
78     private static final String JavaDoc FILE_HEADER = "=============================================================";
79
80     private static final String JavaDoc REVISION_HEADER = "----------------------------";
81
82     private static final String JavaDoc CHANGE_DELETE = "Delete";
83
84     /** enable logging for this class */
85     private static final Logger LOG = Logger.getLogger(SnapshotCM.class);
86
87     private Hashtable JavaDoc props = new Hashtable JavaDoc();
88
89     private String JavaDoc property;
90
91     private String JavaDoc propertyOnDelete;
92
93     /**
94      * List of source path values provided either with sourcePath="...",
95      * sourcePaths="...;...", or nested <sourcePath path="..."> elements.
96      */

97     private List JavaDoc sourcePaths = new ArrayList JavaDoc();
98
99     /**
100      * From SourceControl interface.
101      */

102     public void setProperty(String JavaDoc property) {
103         this.property = property;
104     }
105
106     /**
107      * From SourceControl interface. n
108      */

109     public void setPropertyOnDelete(String JavaDoc propertyOnDelete) {
110         this.propertyOnDelete = propertyOnDelete;
111     }
112
113     public Map JavaDoc getProperties() {
114         return this.props;
115     }
116
117     public void setSourcePaths(String JavaDoc sourcePaths) {
118         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(sourcePaths, ";");
119         while (st.hasMoreTokens()) {
120             setSourcePath(st.nextToken());
121         }
122     }
123
124     public void setSourcePath(String JavaDoc sourcePath) {
125         this.sourcePaths.add(new SourcePath(sourcePath));
126     }
127
128     public SourcePath createSourcePath() {
129         SourcePath sourcePath = new SourcePath();
130         this.sourcePaths.add(sourcePath);
131         return sourcePath;
132     }
133
134     /**
135      * From SourceControl interface.
136      */

137     public void validate() throws CruiseControlException {
138         ValidationHelper.assertFalse(this.sourcePaths.isEmpty(),
139             "'sourcePaths' or 'sourcePath' attribute, or nested sourcepath element(s)"
140             + " is a required attribute for SnapshotCM.");
141     }
142
143     /**
144      * Returns an {@link java.util.List List} of {@link Modification}
145      * detailing all the changes between now and the last build.
146      *
147      * @param lastBuild the last build time
148      * @param now time now, or time to check, NOT USED
149      * @return the list of modifications, an empty (not null) list if no
150      * modifications.
151      */

152     public List JavaDoc getModifications(Date JavaDoc lastBuild, Date JavaDoc now) {
153         // Return value
154
List JavaDoc modificationList = new ArrayList JavaDoc();
155
156         //Command parameters
157
String JavaDoc[] parameters = new String JavaDoc[2];
158         parameters[0] = inDateFormatter.format(lastBuild);
159
160         for (Iterator JavaDoc i = this.sourcePaths.iterator(); i.hasNext(); ) {
161             parameters[1] = ((SourcePath) i.next()).getPath();
162
163             String JavaDoc command = EXECUTABLE.format(parameters);
164             LOG.info("Running command: " + command);
165             try {
166                 Process JavaDoc p = Runtime.getRuntime().exec(command);
167
168                 StreamPumper errorPumper = new StreamPumper(p.getErrorStream());
169                 new Thread JavaDoc(errorPumper).start();
170
171                 InputStream JavaDoc input = p.getInputStream();
172                 modificationList.addAll(parseStream(input));
173
174                 p.getInputStream().close();
175                 p.getOutputStream().close();
176                 p.getErrorStream().close();
177             } catch (Exception JavaDoc e) {
178                 LOG.error("Error in executing the SnapshotCM command : ", e);
179             }
180         }
181
182         if (!modificationList.isEmpty() && property != null) {
183             props.put(property, "true");
184         }
185
186         return modificationList;
187     }
188
189     /**
190      * Parses the input stream to construct the modifications list.
191      * Parser splits the returned string into seperate sections
192      * based on the token string FILE_HEADER. Each file in the list
193      * is passed to the parseEntry method in order to pull out the
194      * user & file names and modification dates.
195      *
196      * Package-private to make it available to the unit test.
197      *
198      * @param input the stream to parse
199      * @return a list of modification elements
200      * @exception IOException
201      */

202     List JavaDoc parseStream(InputStream JavaDoc input) throws IOException JavaDoc {
203         List JavaDoc modifications = new ArrayList JavaDoc();
204         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(input));
205
206         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
207
208         String JavaDoc line;
209         while ((line = reader.readLine()) != null) {
210             if (line.equals(FILE_HEADER)) {
211                 List JavaDoc fileMods = parseEntry(sb.toString());
212                 modifications.addAll(fileMods);
213                 sb = new StringBuffer JavaDoc();
214             } else {
215                 sb.append(line);
216                 sb.append('\n');
217             }
218         }
219         List JavaDoc fileMods = parseEntry(sb.toString());
220         modifications.addAll(fileMods);
221         return modifications;
222     }
223
224
225     /**
226      * Parses a single line from the reader. Each entry looks like this:
227      *
228      *
229      * File: /src/Backend/CEUI/tomcat/build.xml
230      * Snapshot: /RSTDevelopment/A.03.50/Develop
231      * Current revision: 46
232      * I/O mode: text
233      * Keyword expansion: keyword and value
234      * Permissions: r--r--r--
235      * ----------------------------
236      * Revision: 46 (current) Derivation: 45 --> (46)
237      * Date: 2004/01/06 17:00:38 -0700; Size: 39459 bytes
238      * Author: pacon (Patrick Conant)
239      * Snapshot: /RSTDevelopment/A.03.50/Develop
240      * Used in: /RSTDevelopment/A.03.50/Develop
241      * Change: Content
242      * Removed -D param from SnapshotCM wco and wci commands.
243      * ----------------------------
244      * Revision: 45 Derivation: 44 --> (45) --> 46
245      * Date: 2004/01/06 13:10:31 -0700; Size: 39468 bytes
246      * Author: pacon (Patrick Conant)
247      * Snapshot: /RSTDevelopment/A.03.50/Develop
248      * Change: Content
249      * Checked in via UltraEdit
250      *
251      * There may be one or more revision entries.
252      *
253      * @param entry the entry to parse.
254      * @return an array of modification elements corresponding to the given
255      * entry.
256      */

257     private List JavaDoc parseEntry(String JavaDoc entry) {
258         List JavaDoc modifications = new ArrayList JavaDoc();
259
260         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(entry, "\n");
261         /*
262          * We should get at least 13 lines if there has been a modification.
263          */

264         if (st.countTokens() < 13) {
265             return modifications;
266         }
267
268         /*
269          * Read the header, which is the first 6 lines.
270          */

271         String JavaDoc line = st.nextToken();
272         String JavaDoc entryname = line.substring(6);
273         String JavaDoc fileName;
274         String JavaDoc folderName;
275         int sep = entryname.lastIndexOf("/");
276         if (sep == -1) {
277             sep = entryname.lastIndexOf("\\");
278         }
279         if (sep > -1) {
280             folderName = entryname.substring(0, sep);
281             fileName = entryname.substring(sep + 1);
282         } else {
283             folderName = "";
284             fileName = entryname;
285         }
286
287         // ignore next 5 lines.
288
String JavaDoc nextToken;
289         do {
290             nextToken = st.nextToken();
291         } while (!(nextToken).equals(REVISION_HEADER));
292
293         /*
294          * Now read in each modification.
295          */

296         Modification mod = new Modification("snapshotcm");
297         mod.createModifiedFile(fileName, folderName);
298
299         while (st.hasMoreTokens()) {
300             line = st.nextToken();
301             if (line.equals(REVISION_HEADER) || !st.hasMoreTokens()) {
302                 if (!line.trim().equals("") && !line.equals(REVISION_HEADER)) {
303                     //consider this part of the comment.
304
mod.comment += line;
305                 }
306
307                 //Save the modification
308
modifications.add(mod);
309                 mod = new Modification("snapshotcm");
310                 mod.createModifiedFile(fileName, folderName);
311             } else if (line.startsWith("Revision: ")) { //e.g. Revision: 46 (current) Derivation: 45 --> (46)
312
int nextSpaceDelimiterIndex = line.indexOf(" ", 10);
313                 int endIndex = nextSpaceDelimiterIndex > -1 ? nextSpaceDelimiterIndex : line.length();
314                 mod.revision = line.substring(10, endIndex);
315             } else if (line.startsWith("Date: ")) { //e.g. Date: 2004/01/06 17:00:38 -0700; Size: 39459 bytes
316
try {
317                     mod.modifiedTime = outDateFormatter.parse(line.substring(6, line.indexOf("-") - 1));
318                 } catch (ParseException JavaDoc pe) {
319                     LOG.warn("Unable to parse date " + line.substring(6, line.indexOf("-") - 1));
320                     mod.modifiedTime = new Date JavaDoc(0);
321                 }
322             } else if (line.startsWith("Author: ")) { //e.g. Author: pacon (Patrick Conant)
323
mod.userName = line.substring(8).trim();
324                 if (mod.userName.indexOf(" ") > -1) {
325                     mod.userName = mod.userName.substring(0, mod.userName.indexOf(" "));
326                 }
327             } else if (line.startsWith("Change: ")) { //e.g. Change: Content
328
mod.type = line.substring(8).trim();
329                 if (mod.type.equals(CHANGE_DELETE) && propertyOnDelete != null) {
330                     props.put(propertyOnDelete, "true");
331                 }
332             } else if (line.startsWith("Snapshot: ")) {
333                 //ignore
334
} else if (line.startsWith("Used in: ") || line.startsWith(" /")) {
335                 //e.g. Used in: /aaaa/cccc/bbbb/ccccdddd
336
// /aaaa/cccc/bbbb/
337

338                 //ignore
339
} else if (!line.trim().equals("")) {
340                 //consider this part of the comment.
341
mod.comment += line;
342             }
343         }
344
345         return modifications;
346     }
347
348     public static class SourcePath {
349         private String JavaDoc path;
350
351         public SourcePath() {
352         }
353
354         public SourcePath(String JavaDoc path) {
355             this.path = path;
356         }
357
358         public void setPath(String JavaDoc path) {
359             this.path = path;
360         }
361
362         public String JavaDoc getPath() {
363             return this.path;
364         }
365     }
366 }
367
Popular Tags