1 37 package net.sourceforge.cruisecontrol.sourcecontrols; 38 39 import java.io.File ; 40 import java.io.IOException ; 41 import java.text.ParseException ; 42 import java.text.SimpleDateFormat ; 43 import java.util.ArrayList ; 44 import java.util.Arrays ; 45 import java.util.Date ; 46 import java.util.Hashtable ; 47 import java.util.Iterator ; 48 import java.util.List ; 49 import java.util.Locale ; 50 import java.util.Map ; 51 import java.util.Properties ; 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 69 public class CMSynergy implements SourceControl { 70 71 74 public static final String CCM_ATTR_DELIMITER = "@#@#@#@"; 75 76 79 public static final String CCM_END_OBJECT = "<<<#@#@#>>>"; 80 81 84 public static final String CCM_EXE = "ccm"; 85 86 90 public static final String CCM_SESSION_VAR = "CCM_ADDR"; 91 92 95 public static final String CCM_SESSION_FILE = System 96 .getProperty("user.home") 97 + File.separator + ".ccmsessionmap"; 98 99 102 private static final Logger LOG = Logger.getLogger(CMSynergy.class); 103 104 108 private Hashtable properties = new Hashtable (); 109 110 114 private String property = "cc.ccm.haschanged"; 115 116 120 private String ccmDelimiter = "-"; 121 122 125 private String changeSynergyURL; 126 127 130 private String ccmDb; 131 132 136 private String ccmExe; 137 138 142 private String projectSpec; 143 144 148 private String projectInstance = "1"; 149 150 155 private boolean updateFolders = true; 156 157 161 private File sessionFile; 162 163 166 private String sessionName; 167 168 171 private String ccmDateFormat = "EEE MMM dd HH:mm:ss yyyy"; 173 177 private boolean reconfigure = false; 178 179 183 private boolean recurse = true; 184 185 189 private boolean ignoreWorkarea = false; 190 191 194 private Locale locale; 195 196 199 private String language = "en"; 200 201 204 private ManagedCommandline cmd; 205 206 209 private String country = "US"; 210 211 214 private int numTasks; 215 216 219 private int numObjects; 220 221 227 public void setCcmExe(String ccmExe) { 228 this.ccmExe = ccmExe; 229 } 230 231 239 public void setProject(String projectSpec) { 240 this.projectSpec = projectSpec; 241 } 242 243 251 public void setInstance(String projectInstance) { 252 this.projectInstance = projectInstance; 253 } 254 255 265 public void setChangeSynergyURL(String url) { 266 this.changeSynergyURL = url; 267 } 268 269 279 public void setCcmDb(String db) { 280 this.ccmDb = db; 281 } 282 283 290 public void setUpdateFolders(boolean updateFolders) { 291 this.updateFolders = updateFolders; 292 } 293 294 307 public void setSessionFile(String sessionFile) { 308 this.sessionFile = new File (sessionFile); 309 } 310 311 320 public void setSessionName(String sessionName) { 321 this.sessionName = sessionName; 322 } 323 324 325 334 public void setCcmDateFormat(String format) { 335 this.ccmDateFormat = format; 336 } 337 338 344 public void setReconfigure (boolean reconfigure) { 345 this.reconfigure = reconfigure; 346 } 347 348 355 public void setRecurse (boolean recurse) { 356 this.recurse = recurse; 357 } 358 359 367 public void setIgnoreWorkarea (boolean ignoreWorkarea) { 368 this.ignoreWorkarea = ignoreWorkarea; 369 } 370 371 379 public void setLanguage(String language) { 380 this.language = language; 381 } 382 383 390 public void setCountry (String country) { 391 this.country = country; 392 } 393 394 397 public Map getProperties() { 398 return properties; 399 } 400 401 404 public void setProperty(String property) { 405 this.property = property; 406 } 407 408 411 public void validate() throws CruiseControlException { 412 ValidationHelper.assertIsSet(projectSpec, "project", this.getClass()); 413 } 414 415 418 public List getModifications(Date lastBuild, Date now) { 419 420 locale = new Locale (language, country); 422 if (!locale.equals(Locale.US)) { 423 LOG.info("Locale has been set to " + locale.toString()); 424 } 425 426 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 e) { 435 StringBuffer buff = new StringBuffer ( 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 (updateFolders) { 446 refreshReconfigureProperties(); 447 } 448 449 numObjects = 0; 452 numTasks = 0; 453 List modifications = getModifiedTasks(lastBuild); 454 455 LOG.info("Found " + numObjects + " modified object(s) in " + numTasks 456 + " new task(s)."); 457 458 if (reconfigure && (numObjects > 0)) { 460 reconfigureProject(); 461 } 462 463 properties.put("cc.ccm.project", projectSpec); 465 properties.put("cc.ccm.dateformat", ccmDateFormat); 466 String 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 484 private void refreshReconfigureProperties() { 485 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 e) { 494 LOG.warn("Could not refresh reconfigure properties for project \"" 495 + projectSpec + "\".", e); 496 } 497 } 498 499 507 private List getModifiedTasks(Date lastBuild) { 508 509 SimpleDateFormat toCcmDate = new SimpleDateFormat ( 513 "yyyy/MM/dd HH:mm:ss", locale); 514 515 cmd.clearArgs(); 517 cmd.createArgument().setValue("query"); 518 cmd.createArgument().setValue("-u"); 519 520 cmd.createArgument().setValue("-f"); 522 cmd.createArgument().setValue( 523 "%displayname" + CCM_ATTR_DELIMITER + "%release" + CCM_ATTR_DELIMITER + "%owner" + CCM_ATTR_DELIMITER + "%completion_date" + CCM_ATTR_DELIMITER + "%task_synopsis" + CCM_END_OBJECT); 529 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 try { 541 cmd.execute(); 542 } catch (Exception e) { 543 LOG.error("Could not query for new tasks. The modification list " 544 + "will be empty!", e); 545 } 546 547 List modificationList = new ArrayList (); 549 Iterator tasks = format(cmd.getStdoutAsList()).iterator(); 550 while (tasks.hasNext()) { 551 numTasks++; 552 String [] attributes = tokeniseEntry((String ) 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 getModifiedObjects(mod); 567 568 getAssociatedCRs(mod); 570 571 modificationList.add(mod); 573 } 574 575 return modificationList; 576 } 577 578 589 private String [] tokeniseEntry(String line, int maxTokens) { 590 int minTokens = maxTokens - 1; String [] tokens = new String [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 620 private void getModifiedObjects(CMSynergyModification mod) { 621 cmd.clearArgs(); 623 cmd.createArgument().setValue("task"); 624 cmd.createArgument().setValue("-show"); 625 cmd.createArgument().setValue("objects"); 626 627 cmd.createArgument().setValue("-f"); 629 cmd.createArgument().setValue( 630 "%name" + CCM_ATTR_DELIMITER + "%version" + CCM_ATTR_DELIMITER + "%type" + CCM_ATTR_DELIMITER + "%instance" + CCM_ATTR_DELIMITER + "%project" + CCM_ATTR_DELIMITER + "%comment" + CCM_END_OBJECT); 637 cmd.createArgument().setValue(mod.taskNumber); 639 640 try { 642 cmd.execute(); 643 } catch (Exception e) { 644 LOG.warn("Could not query for objects in task \"" + mod.taskNumber 645 + "\". The modification list will be incomplete!", e); 646 } 647 648 Iterator objects = format(cmd.getStdoutAsList()).iterator(); 650 while (objects.hasNext()) { 651 numObjects++; 652 String object = (String ) objects.next(); 653 String [] 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 mod.createModifiedObject(attributes[0], attributes[1], 661 attributes[2], attributes[3], attributes[4], attributes[5]); 662 } 663 } 664 665 672 private void getAssociatedCRs(CMSynergyModification mod) { 673 cmd.clearArgs(); 675 cmd.createArgument().setValue("query"); 676 cmd.createArgument().setValue("-u"); 677 678 cmd.createArgument().setValue("-f"); 680 cmd.createArgument().setValue("%displayname"); 681 682 cmd.createArgument().setValue( 684 "cvtype='problem' and has_associated_task('task" 685 + mod.taskNumber 686 + ccmDelimiter 687 + "1:task:probtrac')"); 688 689 try { 691 cmd.execute(); 692 } catch (Exception e) { 693 LOG.warn("Could not query for associated CRs. The modification list " 694 + "may be incomplete!", e); 695 } 696 697 List crList = cmd.getStdoutAsList(); 699 if (crList != null) { 700 Iterator crs = crList.iterator(); 701 while (crs.hasNext()) { 702 String crNum = ((String ) crs.next()).trim(); 703 CMSynergyModification.ChangeRequest cr = mod.createChangeRequest(crNum); 704 if (changeSynergyURL != null && ccmDb != null) { 705 StringBuffer href = new StringBuffer (changeSynergyURL); 706 href.append("/servlet/com.continuus.webpt.servlet.PTweb?"); 707 href.append("ACTION_FLAG=frameset_form&TEMPLATE_FLAG=ProblemReportView&database="); 708 href.append(ccmDb); 709 href.append("&role=User&problem_number="); 710 href.append(crNum); 711 cr.href = href.toString(); 712 } 713 } 714 } 715 } 716 717 722 private String getWorkarea() { 723 String defaultWorkarea = "."; 724 725 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 e) { 737 LOG.warn("Could not determine the workarea location for project \"" 738 + projectSpec + "\".", e); 739 return defaultWorkarea; 740 } 741 742 File workareaPath = new File (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 [] 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 return workareaPath.getAbsolutePath() + File.separator + dirs[0]; 760 } 761 762 765 private void reconfigureProject() { 766 LOG.info("Reconfiguring project " + projectSpec + "."); 767 768 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 e) { 781 LOG.warn("Could not reconfigure project \"" 782 + projectSpec + "\".", e); 783 } 784 } 785 786 793 private List format(List in) { 794 List out = new ArrayList (); 796 Iterator it = in.iterator(); 797 StringBuffer buff = new StringBuffer (); 798 while (it.hasNext()) { 799 buff.append((String ) 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 (); 805 } 806 } 807 return out; 808 } 809 810 821 private Date getDateFromSynergy(String dateString) { 822 SimpleDateFormat fromCcmDate = new SimpleDateFormat (ccmDateFormat, 823 locale); 824 Date date; 825 try { 826 date = fromCcmDate.parse(dateString); 827 } catch (ParseException e) { 828 LOG.warn("Could not parse CM Synergy date \"" + dateString 829 + "\" into Java Date using format \"" + ccmDateFormat 830 + "\".", e); 831 date = new Date (); 832 } 833 return date; 834 } 835 836 847 public static String getSessionID(String sessionName, File sessionFile) 848 throws CruiseControlException { 849 850 if (sessionFile == null) { 852 sessionFile = new File (CCM_SESSION_FILE); 853 } 854 855 Properties sessionProperties = null; 857 try { 858 sessionProperties = Util.loadPropertiesFromFile(sessionFile); 859 } catch (IOException e) { 860 throw new CruiseControlException (e); 861 } 862 863 return sessionProperties.getProperty(sessionName); 865 } 866 867 882 public static ManagedCommandline createCcmCommand(String ccmExe, 883 String sessionName, File sessionFile) { 884 885 if (ccmExe == null) { 887 ccmExe = CCM_EXE; 888 } 889 890 String 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 ManagedCommandline command = new ManagedCommandline(ccmExe); 910 911 if (sessionID != null) { 913 command.setVariable(CCM_SESSION_VAR, sessionID); 914 } 915 916 return command; 917 } 918 } 919 | Popular Tags |