KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > labelincrementers > P4ChangelistLabelIncrementer


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2006, 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
38 package net.sourceforge.cruisecontrol.labelincrementers;
39
40 import java.io.BufferedReader JavaDoc;
41 import java.io.File JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.InputStream JavaDoc;
44 import java.io.InputStreamReader JavaDoc;
45 import java.util.ArrayList JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.StringTokenizer JavaDoc;
48
49 import net.sourceforge.cruisecontrol.CruiseControlException;
50 import net.sourceforge.cruisecontrol.LabelIncrementer;
51 import net.sourceforge.cruisecontrol.util.Commandline;
52 import net.sourceforge.cruisecontrol.util.StreamPumper;
53 import net.sourceforge.cruisecontrol.util.ValidationHelper;
54
55 import org.apache.log4j.Logger;
56 import org.apache.tools.ant.BuildException;
57 import org.apache.tools.ant.Project;
58 import org.apache.tools.ant.taskdefs.Delete;
59 import org.apache.tools.ant.types.FileSet;
60 import org.apache.tools.ant.types.PatternSet.NameEntry;
61 import org.jdom.Element;
62
63 /**
64  * This class uses the most current changelist of the user in Perforce as the
65  * label for the builds. It can also sync the Perforce managed files to that
66  * changelist number, as well as clean out the existing managed files.
67  *
68  * @author <a HREF="mailto:groboclown@users.sourceforge.net">Matt Albrecht</a>
69  */

70 public class P4ChangelistLabelIncrementer implements LabelIncrementer {
71
72     private static final Logger LOG =
73         Logger.getLogger(P4ChangelistLabelIncrementer.class);
74     private static final String JavaDoc CHANGELIST_PREFIX = "@";
75     private static final String JavaDoc REVISION_PREFIX = "#";
76     private static final String JavaDoc RECURSE_U = "/...";
77     private static final String JavaDoc RECURSE_W = "\\...";
78     
79     private String JavaDoc p4Port;
80     private String JavaDoc p4Client;
81     private String JavaDoc p4User;
82     private String JavaDoc p4View;
83     private String JavaDoc p4Passwd;
84     
85     private boolean clean = false;
86     private boolean delete = false;
87     private boolean sync = true;
88     
89     private int baseChangelist = -1;
90
91     /**
92      * Retrieves the current changelist, or, if given, the specified changelist,
93      * and also performs any necessary actions the user requested.
94      *
95      * @param oldLabel Label from previous successful build.
96      * @return Label to use for most recent successful build.
97      */

98     public String JavaDoc incrementLabel(String JavaDoc oldLabel, Element buildLog) {
99         String JavaDoc label = null;
100         try {
101             validate();
102             
103             // Perform conditional actions.
104
// Since the settings might change or be executed in any order,
105
// we perform the checks on which actions to run here.
106
boolean delTree = delete;
107             boolean cleanP4 = delTree || clean;
108             boolean syncP4 = cleanP4 || sync;
109             
110             if (cleanP4) {
111                 LOG.info("Cleaning Perforce clientspec " + p4Client);
112                 syncTo(REVISION_PREFIX + 0);
113             }
114             if (delTree) {
115                 deleteView();
116             }
117             
118             label = getDefaultLabel();
119             
120             if (syncP4) {
121                 syncTo(CHANGELIST_PREFIX + label);
122             }
123         } catch (CruiseControlException cce) {
124             LOG.warn("Couldn't run expected tasks", cce);
125         }
126         
127         return label;
128     }
129
130     public boolean isPreBuildIncrementer() {
131         // This only has use when used as a pre-build incrementer
132
return true;
133     }
134
135     /**
136      * Verify that the label specified is a valid label. In this case a valid
137      * label contains at least one separator character, and an integer after the last
138      * occurrence of the separator character.
139      */

140     public boolean isValidLabel(String JavaDoc label) {
141         try {
142             Integer.parseInt(label);
143             return true;
144         } catch (NumberFormatException JavaDoc e) {
145             return false;
146         }
147     }
148
149     /**
150      * The instance must be fully initialized before calling this method.
151      * @throws IllegalStateException if the instance is not properly initialized
152      */

153     public String JavaDoc getDefaultLabel() {
154         if (baseChangelist > 0) {
155             return Integer.toString(baseChangelist);
156         }
157         // else
158

159         try {
160             validate();
161             
162             return getCurrentChangelist();
163         } catch (CruiseControlException cce) {
164             cce.printStackTrace();
165             LOG.fatal("Problem accessing Perforce changelist", cce);
166             throw new IllegalStateException JavaDoc(
167                 "Problem accessing Perforce changelist");
168         }
169     }
170
171     // User settings
172

173     /**
174      * Set the changelist number that you want to build at. If this isn't
175      * set, then the class will get the most current submitted changelist
176      * number. Note that setting this will cause the build to ALWAYS build
177      * at this changelist number.
178      *
179      * @param syncChange the changelist number to perform the sync to.
180      */

181     public void setChangelist(int syncChange) {
182         baseChangelist = syncChange;
183     }
184     
185     
186     
187     public void setPort(String JavaDoc p4Port) {
188         this.p4Port = p4Port;
189     }
190
191     public void setClient(String JavaDoc p4Client) {
192         this.p4Client = p4Client;
193     }
194
195     public void setUser(String JavaDoc p4User) {
196         this.p4User = p4User;
197     }
198
199     public void setView(String JavaDoc p4View) {
200         this.p4View = p4View;
201     }
202
203     public void setPasswd(String JavaDoc p4Passwd) {
204         this.p4Passwd = p4Passwd;
205     }
206     
207     /**
208      * Disables the label incrementer from synchronizing Perforce to the
209      * view.
210      *
211      * @param b
212      */

213     public void setNoSync(boolean b) {
214         this.sync = !b;
215     }
216     
217     /**
218      * Perform a "p4 sync -f [view]#0" before syncing anew. This will force
219      * the sync to happen.
220      *
221      * @param b
222      */

223     public void setClean(boolean b) {
224         this.clean = b;
225     }
226     
227     
228     /**
229      * Perform a recursive delete of the clientspec view. This
230      * will force a clean & sync. Note that this can potentially
231      * be very destructive, so use with the utmost caution.
232      *
233      * @param b
234      */

235     public void setDelete(boolean b) {
236         this.delete = b;
237     }
238
239
240     public void validate() throws CruiseControlException {
241         ValidationHelper.assertIsSet(p4View, "view", this.getClass());
242         ValidationHelper.assertNotEmpty(p4View, "view", this.getClass());
243         ValidationHelper.assertNotEmpty(p4Client, "client", this.getClass());
244         ValidationHelper.assertNotEmpty(p4Port, "port", this.getClass());
245         ValidationHelper.assertNotEmpty(p4User, "user", this.getClass());
246         ValidationHelper.assertNotEmpty(p4Passwd, "passwd", this.getClass());
247     }
248
249
250
251     protected String JavaDoc getCurrentChangelist()
252             throws CruiseControlException {
253         Commandline cmd = buildBaseP4Command();
254         cmd.createArgument().setValue("changes");
255         cmd.createArgument().setValue("-m1");
256         cmd.createArgument().setValue("-ssubmitted");
257         
258         ParseChangelistNumbers pcn = new ParseChangelistNumbers();
259         runP4Cmd(cmd, pcn);
260         
261         String JavaDoc[] changes = pcn.getChangelistNumbers();
262         if (changes != null && changes.length == 1) {
263             return changes[0];
264         } else {
265             throw new CruiseControlException(
266                 "Could not discover the changelist");
267         }
268     }
269     
270     
271     protected void syncTo(String JavaDoc viewArg) throws CruiseControlException {
272         Commandline cmd = buildBaseP4Command();
273         cmd.createArgument().setValue("sync");
274         cmd.createArgument().setValue(p4View + viewArg);
275         
276         runP4Cmd(cmd, new P4CmdParserAdapter());
277     }
278     
279     
280     protected void deleteView() throws CruiseControlException {
281         // despite what people tell you, deleting correctly in Java is
282
// hard. So, let Ant do our dirty work for us.
283
try {
284             Project p = createProject();
285             FileSet fs = getWhereView(p);
286             Delete d = createDelete(p);
287             d.setProject(p);
288             d.setVerbose(true);
289             d.addFileset(fs);
290             d.execute();
291         } catch (BuildException be) {
292             throw new CruiseControlException(be.getMessage(), be);
293         }
294     }
295     
296     
297     /**
298      * If the view mapping contains a reference to a single file,
299      *
300      * @return the collection of recursive directories inside the Perforce
301      * view.
302      * @throws CruiseControlException
303      */

304     protected FileSet getWhereView(Project p) throws CruiseControlException {
305         String JavaDoc view = p4View;
306         if (view == null) {
307             view = "//...";
308         }
309         if (!view.endsWith(RECURSE_U) && !view.endsWith(RECURSE_W)) {
310             // we'll only care about the recursive view. Anything else
311
// should be handled by the sync view#0
312
LOG.debug("view [" + view + "] isn't recursive.");
313             return null;
314         }
315         Commandline cmd = buildBaseP4Command();
316         cmd.createArgument().setValue("where");
317         cmd.createArgument().setValue(view);
318         
319         ParseOutputParam pop = new ParseOutputParam("");
320         runP4Cmd(cmd, pop);
321         String JavaDoc[] values = pop.getValues();
322         if (values == null || values.length <= 0) {
323             LOG.debug("Didn't find any files for view");
324             return null;
325         }
326         FileSet fs = createFileSet(p);
327         
328         // on windows, this is considered higher than the drive letter.
329
fs.setDir(new File JavaDoc("/"));
330         int count = 0;
331         
332         for (int i = 0; i < values.length; ++i) {
333             // first token: the depot name
334
// second token: the client name
335
// third token+: the local file system name
336

337             // like above, we only care about the recursive view. If the
338
// line doesn't end in /... or \... (even if it's a %%1), we ignore
339
// it. This makes our life so much simpler when dealing with
340
// spaces.
341
String JavaDoc s = values[i];
342             //LOG.debug("Parsing view line " + i + " [" + s + "]");
343
if (!s.endsWith(RECURSE_U) && !s.endsWith(RECURSE_W)) {
344                 continue;
345             }
346             
347             String JavaDoc[] tokens = new String JavaDoc[3];
348             int pos = 0;
349             for (int j = 0; j < 3; ++j) {
350                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
351                 boolean neot = true;
352                 while (neot) {
353                     if (pos >= s.length()) {
354                         break;
355                     }
356                     int q1 = s.indexOf('\'', pos);
357                     int q2 = s.indexOf('"', pos);
358                     int sp = s.indexOf(' ', pos);
359                     if (q1 >= 0 && (q1 < q2 || q2 < 0) && (q1 < sp || sp < 0)) {
360                         sb.append(s.substring(pos, q1));
361                         pos = q1 + 1;
362                     } else
363                     if (q2 >= 0 && (q2 < q1 || q1 < 0) && (q2 < sp || sp < 0)) {
364                         sb.append(s.substring(pos, q2));
365                         pos = q2 + 1;
366                     } else
367                     if (sp >= 0) {
368                         // check if we're at the end of the token
369
String JavaDoc sub = s.substring(pos, sp);
370                         pos = sp + 1;
371                         sb.append(sub);
372                         if (sub.endsWith(RECURSE_U) || sub.endsWith(RECURSE_W)) {
373                             neot = false;
374                         } else {
375                             // keep the space - it's inside the token
376
sb.append(' ');
377                         }
378                     } else {
379                         sb.append(s.substring(pos));
380                         neot = false;
381                     }
382                 }
383                 tokens[j] = new String JavaDoc(sb).trim();
384             }
385             if (tokens[0] != null && tokens[1] != null && tokens[2] != null
386                     && (tokens[2].endsWith(RECURSE_U)
387                     || tokens[2].endsWith(RECURSE_W))) {
388                 // convert the P4 recurse expression with the Ant
389
// recurse expression
390
String JavaDoc f = tokens[2].substring(0,
391                         tokens[2].length() - RECURSE_W.length())
392                         + File.separator + "**";
393                 // a - in front of the depot name means to exclude this path
394
if (tokens[0].startsWith("-//")) {
395                     NameEntry ne = fs.createExclude();
396                     ne.setName(f);
397                 } else {
398                     NameEntry ne = fs.createInclude();
399                     ne.setName(f);
400                 }
401                 ++count;
402             }
403         }
404         if (count > 0) {
405             return fs;
406         } else {
407             LOG.debug("no files in view to delete");
408             return null;
409         }
410     }
411     
412     
413     protected Project createProject() {
414         Project p = new Project();
415         p.init();
416         return p;
417     }
418     
419     
420     protected Delete createDelete(Project p) throws CruiseControlException {
421         Object JavaDoc o = p.createTask("delete");
422         if (o == null || !(o instanceof Delete)) {
423             // Backup code just in case we didn't work right.
424
// If we can guarantee the above operation works all the time,
425
// then this log note should be replaced with an exception.
426
LOG.info("Could not find <delete> task in Ant. Defaulting to basic constructor.");
427             Delete d = new Delete();
428             d.setProject(p);
429             o = d;
430         }
431         return (Delete) o;
432     }
433     
434     
435     protected FileSet createFileSet(Project p) throws CruiseControlException {
436         Object JavaDoc o = p.createDataType("fileset");
437         if (o == null || !(o instanceof FileSet)) {
438             // Backup code just in case we didn't work right.
439
// If we can guarantee the above operation works all the time,
440
// then this log note should be replaced with an exception.
441
LOG.info("Could not find <fileset> type in Ant. Defaulting to basic constructor.");
442             FileSet fs = new FileSet();
443             fs.setProject(p);
444             o = fs;
445         }
446         return (FileSet) o;
447     }
448     
449     
450     protected Commandline buildBaseP4Command() {
451         Commandline commandLine = new Commandline();
452         commandLine.setExecutable("p4");
453         commandLine.createArgument().setValue("-s");
454
455         if (p4Client != null) {
456             commandLine.createArgument().setValue("-c");
457             commandLine.createArgument().setValue(p4Client);
458         }
459
460         if (p4Port != null) {
461             commandLine.createArgument().setValue("-p");
462             commandLine.createArgument().setValue(p4Port);
463         }
464
465         if (p4User != null) {
466             commandLine.createArgument().setValue("-u");
467             commandLine.createArgument().setValue(p4User);
468         }
469
470         if (p4Passwd != null) {
471             commandLine.createArgument().setValue("-P");
472             commandLine.createArgument().setValue(p4Passwd);
473         }
474         return commandLine;
475     }
476     
477     
478     protected void runP4Cmd(Commandline cmd, P4CmdParser parser)
479             throws CruiseControlException {
480         try {
481             LOG.info("Executing commandline [" + cmd + "]");
482             Process JavaDoc p = Runtime.getRuntime().exec(cmd.getCommandline());
483             
484             try {
485                 new Thread JavaDoc(new StreamPumper(p.getErrorStream())).start();
486                 
487                 InputStream JavaDoc p4Stream = p.getInputStream();
488                 parseStream(p4Stream, parser);
489             } finally {
490                 p.waitFor();
491                 p.getInputStream().close();
492                 p.getOutputStream().close();
493                 p.getErrorStream().close();
494             }
495         } catch (IOException JavaDoc e) {
496             throw new CruiseControlException("Problem trying to execute command line process", e);
497         } catch (InterruptedException JavaDoc e) {
498             throw new CruiseControlException("Problem trying to execute command line process", e);
499         }
500     }
501     
502     protected void parseStream(InputStream JavaDoc stream, P4CmdParser parser)
503             throws IOException JavaDoc {
504         String JavaDoc line;
505         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(
506                 new InputStreamReader JavaDoc(stream));
507         while ((line = reader.readLine()) != null) {
508             if (line.startsWith("error:")) {
509                 throw new IOException JavaDoc(
510                         "Error reading P4 stream: P4 says: " + line);
511             } else if (line.startsWith("exit: 0")) {
512                 System.err.println("p4cmd: Found exit 0");
513                 break;
514             } else if (line.startsWith("exit:")) {
515                 // not an exit code of 0
516
System.err.println("p4cmd: Found exit " + line);
517                 throw new IOException JavaDoc(
518                         "Error reading P4 stream: P4 says: " + line);
519             } else if (line.startsWith("warning:")) {
520                 parser.warning(line.substring(8));
521             } else if (line.startsWith("info:")
522                     || line.startsWith("info1:")) {
523                 parser.info(line.substring(5));
524             } else if (line.startsWith("text:")) {
525                 parser.text(line.substring(5));
526             }
527         }
528         if (line == null) {
529             throw new IOException JavaDoc(
530                     "Error reading P4 stream: Unexpected EOF reached");
531         }
532     }
533
534     protected static interface P4CmdParser {
535         public void warning(String JavaDoc msg);
536         public void info(String JavaDoc msg);
537         public void text(String JavaDoc msg);
538     }
539     
540     protected static class P4CmdParserAdapter implements P4CmdParser {
541         public void warning(String JavaDoc msg) {
542             // empty
543
}
544         public void info(String JavaDoc msg) {
545             // empty
546
}
547         public void text(String JavaDoc msg) {
548             // empty
549
}
550     }
551     
552     protected static class ParseChangelistNumbers extends P4CmdParserAdapter {
553         private ArrayList JavaDoc changelists = new ArrayList JavaDoc();
554         public void info(String JavaDoc msg) {
555             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(msg);
556             st.nextToken(); // skip 'Change' text
557
changelists.add(st.nextToken());
558         }
559         
560         public String JavaDoc[] getChangelistNumbers() {
561             String JavaDoc[] changelistNumbers = new String JavaDoc[ 0 ];
562             return (String JavaDoc[]) changelists.toArray(changelistNumbers);
563         }
564     }
565     
566     protected static class ParseOutputParam extends P4CmdParserAdapter {
567         public ParseOutputParam(String JavaDoc paramName) {
568             this.paramName = paramName;
569         }
570         private final String JavaDoc paramName;
571         private List JavaDoc values = new ArrayList JavaDoc();
572         public void info(final String JavaDoc msg) {
573             String JavaDoc m = msg.trim();
574             if (m.startsWith(paramName)) {
575                 String JavaDoc m2 = m.substring(paramName.length()).trim();
576                 values.add(m2);
577             }
578         }
579         
580         public String JavaDoc[] getValues() {
581             String JavaDoc[] v = new String JavaDoc[ 0 ];
582             return (String JavaDoc[]) values.toArray(v);
583         }
584     }
585 }
586
Popular Tags