package com.coverity.ps.integrations.reporting; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.coverity.ps.common.CimProxy; import com.coverity.ps.common.config.ConfigurationManager; import com.coverity.ps.integrations.Integration; import com.coverity.ws.v4.MergedDefectDataObj; /** * Creates a summary e-mail report of defects that have been detected in the * last N number of days based upon component. */ public class NotifyComponentOwnersSummary extends ComponentDefectReport implements Integration { private String configFile; /** * Constructor * * @param project * project name * @param days * defects detected in the past N number of days * @param isDryRun */ public NotifyComponentOwnersSummary(String project, int days, String configFile, boolean isDryRun) { super(project, days, isDryRun); this.configFile = configFile; } public boolean execute() throws Exception { // fetch component to user mapping and defects Map<String, List<String>> userComponents = getUserMapping(); Map<String, List<MergedDefectDataObj>> defectsByComponent = getProjectDefectsByComponent(); System.out.println("------------------"); // System.out.println(defectsByComponent.size() + " component(s) with new defects"); for (Map.Entry<String, List<MergedDefectDataObj>> componentDefectValues : defectsByComponent.entrySet()) { List<MergedDefectDataObj> componentDefects = componentDefectValues.getValue(); final String componentName = (String) componentDefectValues.getKey(); int componentNew = 0; int componentTriaged = 0; int componentResolved = 0; for (MergedDefectDataObj componentDefect : componentDefects) { // new if (componentDefect.getStatus().equals("New")) { componentNew++; } // outstanding else if (componentDefect.getStatus().equals("Triaged")) { componentTriaged++; } // resolved: note assume dismissed and fixed else if (componentDefect.getStatus().equals("Dismissed") || componentDefect.getStatus().equals("Fixed")) { componentResolved++; } } final int componentOutstanding = componentNew + componentTriaged; final String asOfDate = new SimpleDateFormat("MM/dd/yy HH:mm:ss").format(this.lastDetected.toGregorianCalendar().getTime()); System.out.println("component=" + componentName + ", as-of-date=" + asOfDate + ", outstanding=" + componentOutstanding); StringBuilder html = new StringBuilder(); html.append("<html><p>As of "); html.append(asOfDate); // set as-of-date if(componentOutstanding == 1) { html.append(". There is "); } else { html.append(". There are "); } html.append(componentOutstanding); if(componentOutstanding == 1) { html.append(" Coverity defect in the \""); } else { html.append(" Coverity defects in the \""); } html.append(componentName); StringBuilder projectUrl = new StringBuilder("http://"); projectUrl.append(ConfigurationManager.getInstance().getAddress()); projectUrl.append(':'); projectUrl.append(ConfigurationManager.getInstance().getPort()); projectUrl.append("/sourcebrowser.htm?projectId="); projectUrl.append(this.projectId); html.append("\" component.</p><p>Please check the CIM for the "); html.append("<a href='"); html.append(projectUrl); html.append("'>entire</a>"); html.append(" defect list.</p></html>"); System.out.println("Raw HTML=" + html); // e-mail to component owners if(userComponents.containsKey(componentName)) { final List<String> users = (List<String>)userComponents.get(componentName); if(this.isDryRun) { System.out.println("Recipients that would have received e-mail(s): " + users); } else { final String subject = "New Coverity defects"; List<String> recipients = CimProxy.getInstance().notify(users, subject, html.toString()); for(String recipient : recipients) { System.out.println("e-mail sucessfully sent to " + recipient); } } } System.out.println("------------------"); } return true; } private Map<String, List<String>> getUserMapping() throws Exception { DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); Document document = documentBuilder.parse(this.configFile); // get component mapping Map<String, List<String>> userMapping = new HashMap<String, List<String>>(); NodeList componentNode = document.getDocumentElement().getElementsByTagName("component"); for (int i = 0; i < componentNode.getLength(); i++) { Element compElem = (Element) componentNode.item(i); final String componentName = compElem.getAttribute("name"); // build list of users List<String> users = new ArrayList<String>(); NodeList userNodes = compElem.getElementsByTagName("user"); for (int j = 0; j < userNodes.getLength(); j++) { Element userNode = (Element) userNodes.item(j); users.add(userNode.getTextContent()); } // add entry userMapping.put(componentName, users); } return userMapping; } /* * Main command line driver. Please see class constructor for required * arguments. */ public static void main(String[] args) { try { if (args.length == 4) { NotifyComponentOwnersSummary notifyOwners = new NotifyComponentOwnersSummary( args[0], Integer.parseInt(args[1]), args[2], args[3].equalsIgnoreCase("true")); if (notifyOwners.execute()) { System.out.println("\nSuccessful!"); } else { System.out.println("\n*** Unsuccessful ***"); } } else { System.err.println("This program notifies component owners of new defects that have been detected in the last N number of days."); System.err.println("usage: java " + NotifyComponentOwnersSummary.class.getName() + " <project-name> <num-days> <comp-config-file> <is-dry-run>"); } } catch (Exception e) { e.printStackTrace(); } } }