KickJava   Java API By Example, From Geeks To Geeks.

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


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2001, 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.File JavaDoc;
40 import java.io.IOException JavaDoc;
41 import java.text.ParseException JavaDoc;
42 import java.text.SimpleDateFormat JavaDoc;
43 import java.util.ArrayList JavaDoc;
44 import java.util.Arrays JavaDoc;
45 import java.util.Date JavaDoc;
46 import java.util.Hashtable JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.Locale JavaDoc;
50 import java.util.Map JavaDoc;
51 import java.util.Properties JavaDoc;
52
53 import net.sourceforge.cruisecontrol.CruiseControlException;
54 import net.sourceforge.cruisecontrol.SourceControl;
55 import net.sourceforge.cruisecontrol.util.ValidationHelper;
56 import net.sourceforge.cruisecontrol.util.ManagedCommandline;
57 import net.sourceforge.cruisecontrol.util.Util;
58
59 import org.apache.log4j.Logger;
60
61 /**
62  * Checks for modifications made to a Telelogic CM Synergy repository.
63  * It does this by examining a provided reference project, getting
64  * the tasks from all folders in that project, and checking the
65  * completion time of those tasks against the last build.
66  *
67  * @author <a HREF="mailto:rjmpsmith@gmail.com">Robert J. Smith</a>
68  */

69 public class CMSynergy implements SourceControl {
70     
71     /**
72      * A delimiter used for data values returned from a CM Synergy query
73      */

74     public static final String JavaDoc CCM_ATTR_DELIMITER = "@#@#@#@";
75
76     /**
77      * A delimiter used to mark the end of a multi-lined result from a query
78      */

79     public static final String JavaDoc CCM_END_OBJECT = "<<<#@#@#>>>";
80     
81     /**
82      * The default CM Synergy command line client executable
83      */

84     public static final String JavaDoc CCM_EXE = "ccm";
85     
86     /**
87      * The environment variable used by CM Synergy to determine
88      * which backend ccmSession to use when issuing commands.
89      */

90     public static final String JavaDoc CCM_SESSION_VAR = "CCM_ADDR";
91         
92     /**
93      * The default CM Synergy session map file
94      */

95     public static final String JavaDoc CCM_SESSION_FILE = System
96             .getProperty("user.home")
97             + File.separator + ".ccmsessionmap";
98
99     /**
100      * An instance of the logging class
101      */

102     private static final Logger LOG = Logger.getLogger(CMSynergy.class);
103
104     /**
105      * A collection of properties which will be passed to and set
106      * within the builder.
107      */

108     private Hashtable JavaDoc properties = new Hashtable JavaDoc();
109
110     /**
111      * The name of the property which will be set and passed to the
112      * builder if any object has changed since the last build.
113      */

114     private String JavaDoc property = "cc.ccm.haschanged";
115
116     /**
117      * The version number delimeter used by the database with which
118      * this CM Synergy session is connected.
119      */

120     private String JavaDoc ccmDelimiter = "-";
121     
122     /**
123      * The URL for your installation of Change Synergy
124      */

125     private String JavaDoc changeSynergyURL;
126     
127     /**
128      * The CCM database with which we wish to connect
129      */

130     private String JavaDoc ccmDb;
131     
132     /**
133      * The CM Synergy executable used for executing commands. If not set,
134      * we will use the default value "ccm".
135      */

136     private String JavaDoc ccmExe;
137     
138     /**
139      * The CM Synergy project spec (2 part name) of the project we will
140      * use as a template to determine if any new tasks have been completed.
141      */

142     private String JavaDoc projectSpec;
143     
144     /**
145      * The instance number of the project. This is almost always "1", but might
146      * need to be overridden if you are using DCM?
147      */

148     private String JavaDoc projectInstance = "1";
149     
150     /**
151      * If set to true, the contents of the folders contained within the
152      * project's reconfigure properties will be updated before we query
153      * to find new tasks.
154      */

155     private boolean updateFolders = true;
156     
157     /**
158      * The file which contains the mapping between CM Synergy session names
159      * and IDs.
160      */

161     private File JavaDoc sessionFile;
162     
163     /**
164      * The name of the CM Synergy session to use.
165      */

166     private String JavaDoc sessionName;
167     
168     /**
169      * The date format as returned by your installation of CM Synergy.
170      */

171     private String JavaDoc ccmDateFormat = "EEE MMM dd HH:mm:ss yyyy"; // Fri Dec 3 17:51:56 2004
172

173     /**
174      * If set to true, the project will be reconfigured when changes are
175      * detected.
176      */

177     private boolean reconfigure = false;
178     
179     /**
180      * Used in conjunction with reconfigure. If set to true, all subprojects
181      * will be reconfigured when changes are detected.
182      */

183     private boolean recurse = true;
184     
185     /**
186      * If set to true, the work area location will not be queried and passed
187      * to the builder.
188      */

189     private boolean ignoreWorkarea = false;
190     
191     /**
192      * The locale used for parsing dates.
193      */

194     private Locale JavaDoc locale;
195     
196     /**
197      * The language used to set the locale for parsing CM Synergy dates.
198      */

199     private String JavaDoc language = "en";
200     
201     /**
202      * A reusable commandline for issuing CM Synergy commands
203      */

204     private ManagedCommandline cmd;
205     
206     /**
207      * The country used to set the locale for parsing CM Synergy dates.
208      */

209     private String JavaDoc country = "US";
210     
211     /**
212      * The number of modified tasks found
213      */

214     private int numTasks;
215     
216     /**
217      * The number of modified objects found
218      */

219     private int numObjects;
220     
221     /**
222      * Sets the name of the CM Synergy executable to use when issuing
223      * commands.
224      *
225      * @param ccmExe the name of the CM Synergy executable
226      */

227     public void setCcmExe(String JavaDoc ccmExe) {
228         this.ccmExe = ccmExe;
229     }
230     
231     /**
232      * Sets the CM Synergy project spec to be used as a template for calculating
233      * changes. The value set here can be accessed from within the build as the
234      * property "cc.ccm.project".
235      *
236      * @param projectSpec
237      * The project spec (in 2 part name format).
238      */

239     public void setProject(String JavaDoc projectSpec) {
240         this.projectSpec = projectSpec;
241     }
242     
243     /**
244      * Sets the project's instance value. This value will be used in any query
245      * which involves the project. Defaults to "1". This default should work for
246      * most people. You might, however, need to override this value when using
247      * DCM?
248      *
249      * @param projectInstance The instance number of the project.
250      */

251     public void setInstance(String JavaDoc projectInstance) {
252         this.projectInstance = projectInstance;
253     }
254     
255     /**
256      * Sets the URL for your installation of Change Synergy. This is used to
257      * create active links from the modification report to the Change Requests
258      * associated with the modified tasks. If not set, the links will not be
259      * created. If you wish to use this feature, you must also set the ccmdb
260      * attribute to the remote location of the Synergy database.
261      *
262      * @param url
263      * The URL of your ChangeSynergy installation
264      */

265     public void setChangeSynergyURL(String JavaDoc url) {
266         this.changeSynergyURL = url;
267     }
268
269     /**
270      * Sets the remote Synergy database with which to connect. This is only
271      * needed if you wish to create active links from the build results page to
272      * your installation of Change Synergy. If you set this attribute, you must
273      * also set the changesynergyurl attribute.
274      *
275      * @param db
276      * The remote Synergy database with which to connect (e.g.
277      * /ccmdb/mydb).
278      */

279     public void setCcmDb(String JavaDoc db) {
280         this.ccmDb = db;
281     }
282     
283     /**
284      * Sets the value of the updateFolders attribute. If set to true, the
285      * contents of the folders contained within the project's reconfigure
286      * properties will be updated before we query to find new tasks.
287      *
288      * @param updateFolders
289      */

290     public void setUpdateFolders(boolean updateFolders) {
291         this.updateFolders = updateFolders;
292     }
293     
294     /**
295      * Sets the file which contains the mapping between CM Synergy session names
296      * and IDs. This file should be in the standard properties file format. Each
297      * line should map one name to a CM Synergy session ID (as returned by the
298      * "ccm status" command).
299      * <p>
300      * example:
301      * <br><br>
302      * session1=localhost:65024:192.168.1.17
303      *
304      * @param sessionFile
305      * The session file
306      */

307     public void setSessionFile(String JavaDoc sessionFile) {
308         this.sessionFile = new File JavaDoc(sessionFile);
309     }
310     
311     /**
312      * Sets the name of the CM Synergy session to use with this plugin. This
313      * name should appear in the specified session file.
314      *
315      * @param sessionName
316      * The session name
317      *
318      * @see #setSessionFile(String)
319      */

320     public void setSessionName(String JavaDoc sessionName) {
321         this.sessionName = sessionName;
322     }
323  
324     
325     /**
326      * Sets the date format used by your installation of CM Synergy. The format
327      * string should use the syntax described in <code>SimpleDateFormat</code>.
328      * The default is "EEE MMM dd HH:mm:ss yyyy" The value set here can be
329      * accessed from within the build as the property "cc.ccm.dateformat".
330      *
331      * @param format
332      * the date format
333      */

334     public void setCcmDateFormat(String JavaDoc format) {
335         this.ccmDateFormat = format;
336     }
337     
338     /**
339      * Sets the value of the reconfigure attribute. If set to true, the project
340      * will be reconfigured when changes are detected. Default value is false.
341      *
342      * @param reconfigure
343      */

344     public void setReconfigure (boolean reconfigure) {
345         this.reconfigure = reconfigure;
346     }
347     
348     /**
349      * Sets the value of the recurse attribute. Used in conjuction with the
350      * reconfigure attribute. If set to true, all subprojects will also be
351      * reconfigured when changes are detected. Default is true.
352      *
353      * @param recurse
354      */

355     public void setRecurse (boolean recurse) {
356         this.recurse = recurse;
357     }
358     
359     /**
360      * Sets the value of the ignoreWorkarea attribute. If set to true, we will
361      * not attempt to determine the location of the project's workarea, nor
362      * will we pass the cc.ccm.workarea attribute to the builders. Default
363      * is false.
364      *
365      * @param ignoreWorkarea
366      */

367     public void setIgnoreWorkarea (boolean ignoreWorkarea) {
368         this.ignoreWorkarea = ignoreWorkarea;
369     }
370
371     /**
372      * Sets the language used to create the locale for parsing CM Synergy dates.
373      * The format should follow the ISO standard as specified by
374      * <code>java.util.Locale</code>. The default is "en" (English).
375      *
376      * @param language
377      * The language to use when creating the <code>Locale</code>
378      */

379     public void setLanguage(String JavaDoc language) {
380         this.language = language;
381     }
382     
383     /**
384      * Sets the country used to create the locale for parsing CM Synergy dates.
385      * The format should follow the ISO standard as specified by
386      * <code>java.util.Locale</code>. The default is "US" (United States).
387      *
388      * @param country The ISO country code to use
389      */

390     public void setCountry (String JavaDoc country) {
391         this.country = country;
392     }
393     
394     /* (non-Javadoc)
395      * @see net.sourceforge.cruisecontrol.SourceControl#getProperties()
396      */

397     public Map JavaDoc getProperties() {
398         return properties;
399     }
400
401     /* (non-Javadoc)
402      * @see net.sourceforge.cruisecontrol.SourceControl#setProperty(java.lang.String)
403      */

404     public void setProperty(String JavaDoc property) {
405         this.property = property;
406     }
407
408     /* (non-Javadoc)
409      * @see net.sourceforge.cruisecontrol.SourceControl#validate()
410      */

411     public void validate() throws CruiseControlException {
412         ValidationHelper.assertIsSet(projectSpec, "project", this.getClass());
413     }
414     
415     /* (non-Javadoc)
416      * @see net.sourceforge.cruisecontrol.SourceControl#getModifications(java.util.Date, java.util.Date)
417      */

418     public List JavaDoc getModifications(Date JavaDoc lastBuild, Date JavaDoc now) {
419                 
420         // Create a Locale appropriate for this installation
421
locale = new Locale JavaDoc(language, country);
422         if (!locale.equals(Locale.US)) {
423             LOG.info("Locale has been set to " + locale.toString());
424         }
425
426         // Attempt to get the database delimiter
427
cmd = createCcmCommand(ccmExe, sessionName,
428                 sessionFile);
429         cmd.createArgument().setValue("delimiter");
430         try {
431             cmd.execute();
432             cmd.assertExitCode(0);
433             this.ccmDelimiter = cmd.getStdoutAsString().trim();
434         } catch (Exception JavaDoc e) {
435             StringBuffer JavaDoc buff = new StringBuffer JavaDoc(
436                     "Could not connect to provided CM Synergy session");
437             LOG.error(buff.toString(), e);
438             return null;
439         }
440         
441         LOG.info("Checking for modifications between " + lastBuild.toString()
442                 + " and " + now.toString());
443                 
444         // If we were asked to update the folders, do so
445
if (updateFolders) {
446             refreshReconfigureProperties();
447         }
448
449         // Create a list of modifications based upon tasks completed
450
// since the last build.
451
numObjects = 0;
452         numTasks = 0;
453         List JavaDoc modifications = getModifiedTasks(lastBuild);
454         
455         LOG.info("Found " + numObjects + " modified object(s) in " + numTasks
456                 + " new task(s).");
457                
458         // If we were asked to reconfigure the project, do so
459
if (reconfigure && (numObjects > 0)) {
460             reconfigureProject();
461         }
462
463         // Pass to the build any relevent properties
464
properties.put("cc.ccm.project", projectSpec);
465         properties.put("cc.ccm.dateformat", ccmDateFormat);
466         String JavaDoc sessionID = cmd.getVariable(CCM_SESSION_VAR);
467         if (sessionID != null) {
468             properties.put("cc.ccm.session", sessionID);
469         }
470         if (numObjects > 0) {
471             properties.put(property, "true");
472         }
473         if (!ignoreWorkarea) {
474             properties.put("cc.ccm.workarea", getWorkarea());
475         }
476         
477         return modifications;
478     }
479
480     /**
481      * Update the folders within the given project's reconfigure
482      * properties.
483      */

484     private void refreshReconfigureProperties() {
485         // Construct the CM Synergy command
486
cmd.clearArgs();
487         cmd.createArgument().setValue("reconfigure_properties");
488         cmd.createArgument().setValue("-refresh");
489         cmd.createArgument().setValue(projectSpec);
490         try {
491             cmd.execute();
492             cmd.assertExitCode(0);
493         } catch (Exception JavaDoc e) {
494             LOG.warn("Could not refresh reconfigure properties for project \""
495                     + projectSpec + "\".", e);
496         }
497     }
498     
499     /**
500      * Get a list of all tasks which are contained in all folders in the
501      * reconfigure properties of the specified project and were completed after
502      * the last build.
503      *
504      * @return A list of <code>CMSynergyModifications</code> which represent
505      * the new tasks
506      */

507     private List JavaDoc getModifiedTasks(Date JavaDoc lastBuild) {
508                 
509         // The format used for converting Java dates into CM Synergy dates
510
// Note that the format used to submit commands differs from the
511
// format used in the results of that command!?!
512
SimpleDateFormat JavaDoc toCcmDate = new SimpleDateFormat JavaDoc(
513                 "yyyy/MM/dd HH:mm:ss", locale);
514
515         // Construct the CM Synergy command
516
cmd.clearArgs();
517         cmd.createArgument().setValue("query");
518         cmd.createArgument().setValue("-u");
519         
520         // Set up the output format
521
cmd.createArgument().setValue("-f");
522         cmd.createArgument().setValue(
523                 "%displayname" + CCM_ATTR_DELIMITER + // 0
524
"%release" + CCM_ATTR_DELIMITER + // 1
525
"%owner" + CCM_ATTR_DELIMITER + // 2
526
"%completion_date" + CCM_ATTR_DELIMITER + // 3
527
"%task_synopsis" + CCM_END_OBJECT); // 4
528

529         // Construct the query string
530
cmd.createArgument().setValue(
531                 "is_task_in_folder_of(is_folder_in_rp_of('"
532                 + projectSpec
533                 + ":project:"
534                 + projectInstance
535                 + "')) and completion_date>time('"
536                 + toCcmDate.format(lastBuild)
537                 + "')");
538         
539         // Execute the command
540
try {
541             cmd.execute();
542         } catch (Exception JavaDoc e) {
543             LOG.error("Could not query for new tasks. The modification list "
544                     + "will be empty!", e);
545         }
546
547         //create a modification list with discovered tasks
548
List JavaDoc modificationList = new ArrayList JavaDoc();
549         Iterator JavaDoc tasks = format(cmd.getStdoutAsList()).iterator();
550         while (tasks.hasNext()) {
551             numTasks++;
552             String JavaDoc[] attributes = tokeniseEntry((String JavaDoc) tasks.next(), 5);
553             if (attributes == null) {
554                 LOG.warn("Could not determine attributes for at least one "
555                         + "discovered task! The modification set is suspect.");
556                 continue;
557             }
558             CMSynergyModification mod = new CMSynergyModification();
559             mod.taskNumber = attributes[0];
560             mod.revision = attributes[1];
561             mod.userName = attributes[2];
562             mod.modifiedTime = getDateFromSynergy(attributes[3]);
563             mod.comment = attributes[4];
564             
565             // Populate the included files by quering for objects in the task
566
getModifiedObjects(mod);
567             
568             // Find any Change Requests with which the task is associated
569
getAssociatedCRs(mod);
570
571             // Add the modification to the list
572
modificationList.add(mod);
573         }
574         
575         return modificationList;
576     }
577
578     /**
579      * Split the results of a CM Synergy query into individual tokens. This
580      * method was added for compatibility with the 1.3 JRE.
581      *
582      * @param line
583      * The line to be tokenised.
584      * @param maxTokens
585      * The maximum number of tokens in the line
586      *
587      * @return The tokens found
588      */

589     private String JavaDoc[] tokeniseEntry(String JavaDoc line, int maxTokens) {
590         int minTokens = maxTokens - 1; // comment may be absent.
591
String JavaDoc[] tokens = new String JavaDoc[maxTokens];
592         Arrays.fill(tokens, "");
593         int tokenIndex = 0;
594         for (int oldIndex = 0, index = line.indexOf(CCM_ATTR_DELIMITER, 0); true; oldIndex = index
595                 + CCM_ATTR_DELIMITER.length(), index = line.indexOf(
596                 CCM_ATTR_DELIMITER, oldIndex), tokenIndex++) {
597             if (tokenIndex > maxTokens) {
598                 LOG.debug("Too many tokens; skipping entry");
599                 return null;
600             }
601             if (index == -1) {
602                 tokens[tokenIndex] = line.substring(oldIndex);
603                 break;
604             } else {
605                 tokens[tokenIndex] = line.substring(oldIndex, index);
606             }
607         }
608         if (tokenIndex < minTokens) {
609             LOG.debug("Not enough tokens; skipping entry");
610             return null;
611         }
612         return tokens;
613     }
614     
615     
616     /**
617      * Populate the object list of a Modification by quering for objects
618      * associated with the task.
619      */

620     private void getModifiedObjects(CMSynergyModification mod) {
621         // Construct the CM Synergy command
622
cmd.clearArgs();
623         cmd.createArgument().setValue("task");
624         cmd.createArgument().setValue("-show");
625         cmd.createArgument().setValue("objects");
626             
627         // Set up the output format
628
cmd.createArgument().setValue("-f");
629         cmd.createArgument().setValue(
630                 "%name" + CCM_ATTR_DELIMITER + // 0
631
"%version" + CCM_ATTR_DELIMITER + // 1
632
"%type" + CCM_ATTR_DELIMITER + // 2
633
"%instance" + CCM_ATTR_DELIMITER + // 3
634
"%project" + CCM_ATTR_DELIMITER + // 4
635
"%comment" + CCM_END_OBJECT); // 5
636

637         // Construct the query string
638
cmd.createArgument().setValue(mod.taskNumber);
639         
640         // Execute the command
641
try {
642             cmd.execute();
643         } catch (Exception JavaDoc e) {
644             LOG.warn("Could not query for objects in task \"" + mod.taskNumber
645                     + "\". The modification list will be incomplete!", e);
646         }
647         
648         // Populate the modification with the object data from the task
649
Iterator JavaDoc objects = format(cmd.getStdoutAsList()).iterator();
650         while (objects.hasNext()) {
651             numObjects++;
652             String JavaDoc object = (String JavaDoc) objects.next();
653             String JavaDoc[] attributes = tokeniseEntry(object, 6);
654             if (attributes == null) {
655                 LOG.warn("Could not determine attributes for object associated "
656                         + "with task \"" + mod.revision + "\".");
657                 continue;
658             }
659             // Add each object to the CMSynergyModification
660
mod.createModifiedObject(attributes[0], attributes[1],
661                     attributes[2], attributes[3], attributes[4], attributes[5]);
662         }
663     }
664
665     /**
666      * Queries the CM Synergy repository to find any Change Requests with which
667      * a task is associated. If the Change Synergy URL and database were provided,
668      * we will add HTML based links to those CRs.
669      *
670      * @param mod The modification object
671      */

672     private void getAssociatedCRs(CMSynergyModification mod) {
673         // Construct the CM Synergy command
674
cmd.clearArgs();
675         cmd.createArgument().setValue("query");
676         cmd.createArgument().setValue("-u");
677         
678         // Set up the output format
679
cmd.createArgument().setValue("-f");
680         cmd.createArgument().setValue("%displayname");
681         
682         // Construct the query string
683
cmd.createArgument().setValue(
684                 "cvtype='problem' and has_associated_task('task"
685                 + mod.taskNumber
686                 + ccmDelimiter
687                 + "1:task:probtrac')");
688         
689         // Execute the command
690
try {
691             cmd.execute();
692         } catch (Exception JavaDoc e) {
693             LOG.warn("Could not query for associated CRs. The modification list "
694                     + "may be incomplete!", e);
695         }
696
697         // Add the Change Request(s) to the modification
698
List JavaDoc crList = cmd.getStdoutAsList();
699         if (crList != null) {
700             Iterator JavaDoc crs = crList.iterator();
701             while (crs.hasNext()) {
702                 String JavaDoc crNum = ((String JavaDoc) crs.next()).trim();
703                 CMSynergyModification.ChangeRequest cr = mod.createChangeRequest(crNum);
704                 if (changeSynergyURL != null && ccmDb != null) {
705                     StringBuffer JavaDoc href = new StringBuffer JavaDoc(changeSynergyURL);
706                     href.append("/servlet/com.continuus.webpt.servlet.PTweb?");
707                     href.append("ACTION_FLAG=frameset_form&#38;TEMPLATE_FLAG=ProblemReportView&#38;database=");
708                     href.append(ccmDb);
709                     href.append("&#38;role=User&#38;problem_number=");
710                     href.append(crNum);
711                     cr.href = href.toString();
712                 }
713             }
714         }
715     }
716     
717     /**
718      * Determine the work area location for the specified project.
719      *
720      * @return The work area location
721      */

722     private String JavaDoc getWorkarea() {
723         String JavaDoc defaultWorkarea = ".";
724         
725         // Get the literal workarea from Synergy
726
cmd.clearArgs();
727         cmd.createArgument().setValue("attribute");
728         cmd.createArgument().setValue("-show");
729         cmd.createArgument().setValue("wa_path");
730         cmd.createArgument().setValue("-project");
731         cmd.createArgument().setValue(projectSpec);
732         
733         try {
734             cmd.execute();
735             cmd.assertExitCode(0);
736         } catch (Exception JavaDoc e) {
737             LOG.warn("Could not determine the workarea location for project \""
738                     + projectSpec + "\".", e);
739             return defaultWorkarea;
740         }
741         
742         // The command will return the literal work area, but what we are
743
// really interested in is the top level directory within that work area.
744
File JavaDoc workareaPath = new File JavaDoc(cmd.getStdoutAsString()
745                 .trim());
746         if (!workareaPath.isDirectory()) {
747             LOG.warn("The workarea reported by Synergy does not exist or is not accessible by this session - \""
748                             + workareaPath.toString() + "\".");
749             return defaultWorkarea;
750         }
751         String JavaDoc[] dirs = workareaPath.list();
752         if (dirs.length != 1) {
753             LOG.warn("The workarea reported by Synergy is invalid - \""
754                             + workareaPath.toString() + "\".");
755             return defaultWorkarea;
756         }
757         
758         // Found it!
759
return workareaPath.getAbsolutePath() + File.separator + dirs[0];
760     }
761     
762     /**
763      * Reconfigure the project
764      */

765     private void reconfigureProject() {
766         LOG.info("Reconfiguring project " + projectSpec + ".");
767         
768         // Construct the CM Synergy command
769
cmd.clearArgs();
770         cmd.createArgument().setValue("reconfigure");
771         if (recurse) {
772             cmd.createArgument().setValue("-recurse");
773         }
774         cmd.createArgument().setValue("-project");
775         cmd.createArgument().setValue(projectSpec);
776         
777         try {
778             cmd.execute();
779             cmd.assertExitCode(0);
780         } catch (Exception JavaDoc e) {
781             LOG.warn("Could not reconfigure project \""
782                     + projectSpec + "\".", e);
783         }
784     }
785     
786     /**
787      * Format the output of a CM Synergy query by removing
788      * newlines introduced by comments.
789      *
790      * @param in The <code>List</code> to be formated
791      * @return The formated <code>List</code>
792      */

793     private List JavaDoc format(List JavaDoc in) {
794         // Concatenate output lines until we hit the end of object delimiter.
795
List JavaDoc out = new ArrayList JavaDoc();
796         Iterator JavaDoc it = in.iterator();
797         StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
798         while (it.hasNext()) {
799             buff.append((String JavaDoc) it.next());
800             int index = buff.toString().lastIndexOf(CCM_END_OBJECT);
801             if (index > -1) {
802                 buff.delete(index, buff.length());
803                 out.add(buff.toString());
804                 buff = new StringBuffer JavaDoc();
805             }
806         }
807         return out;
808     }
809
810     /**
811      * Parse a CM Synergy date string into a Java <code>Date</code>. If the
812      * string cannot be parsed, a warning is written to the log, and the current
813      * date is returned.
814      *
815      * @param dateString
816      * the date string to parse
817      * @return The date
818      *
819      * @see #setCcmDateFormat(String)
820      */

821     private Date JavaDoc getDateFromSynergy(String JavaDoc dateString) {
822         SimpleDateFormat JavaDoc fromCcmDate = new SimpleDateFormat JavaDoc(ccmDateFormat,
823                 locale);
824         Date JavaDoc date;
825         try {
826             date = fromCcmDate.parse(dateString);
827         } catch (ParseException JavaDoc e) {
828             LOG.warn("Could not parse CM Synergy date \"" + dateString
829                     + "\" into Java Date using format \"" + ccmDateFormat
830                     + "\".", e);
831             date = new Date JavaDoc();
832         }
833         return date;
834     }
835     
836     /**
837      * Given a CM Synergy session name, looks up the corresponding session ID.
838      *
839      * @param sessionName
840      * The CM Synergy session name
841      * @param sessionFile
842      * The session map file
843      * @return The session ID.
844      *
845      * @throws CruiseControlException
846      */

847     public static String JavaDoc getSessionID(String JavaDoc sessionName, File JavaDoc sessionFile)
848             throws CruiseControlException {
849
850         // If no session file was provided, try to use the default
851
if (sessionFile == null) {
852             sessionFile = new File JavaDoc(CCM_SESSION_FILE);
853         }
854         
855         // Load the persisted session information from file
856
Properties JavaDoc sessionProperties = null;
857         try {
858             sessionProperties = Util.loadPropertiesFromFile(sessionFile);
859         } catch (IOException JavaDoc e) {
860             throw new CruiseControlException (e);
861         }
862
863         // Look up and return the full session ID
864
return sessionProperties.getProperty(sessionName);
865     }
866     
867     /**
868      * Creates a <code>ManagedCommandline</code> configured to run CM Synergy
869      * commands.
870      *
871      * @param ccmExe
872      * Full path of the CM Synergy command line client (or
873      * <code>null</code> to use the default).
874      * @param sessionName
875      * The name of the session as stored in the map file (or
876      * <code>null</code> to use the default session).
877      * @param sessionFile
878      * The CM Synergy session map file (or <code>null</code> to use
879      * the default).
880      * @return A configured <code>ManagedCommandline</code>
881      */

882     public static ManagedCommandline createCcmCommand(String JavaDoc ccmExe,
883             String JavaDoc sessionName, File JavaDoc sessionFile) {
884         
885         // If no executable name was provided, use the default
886
if (ccmExe == null) {
887             ccmExe = CCM_EXE;
888         }
889         
890         // Attempt to get the appropriate CM Synergy session
891
String JavaDoc sessionID = null;
892         if (sessionName != null) {
893             try {
894                 sessionID = getSessionID(sessionName, sessionFile);
895                 if (sessionID == null) {
896                     LOG.error("Could not find a session ID for CM Synergy session named \""
897                             + sessionName
898                             + "\". Attempting to use the default (current) session.");
899                 }
900             } catch (CruiseControlException e) {
901                 LOG.error("Failed to look up CM Synergy session named \""
902                         + sessionName
903                         + "\". Attempting to use the default (current) session.",
904                         e);
905             }
906         }
907
908         // Create a managed command line
909
ManagedCommandline command = new ManagedCommandline(ccmExe);
910
911         // If we were able to find a CM Synergy session ID, use it
912
if (sessionID != null) {
913             command.setVariable(CCM_SESSION_VAR, sessionID);
914         }
915
916         return command;
917     }
918 }
919
Popular Tags