package com.coverity.ps.integrations.reporting; 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.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 NotifyComponentOwners 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 NotifyComponentOwners(String project, int days, String configFile, boolean isDryRun) { super(project, days, isDryRun); this.configFile = configFile; } public boolean execute() throws Exception { Map<String, List<MergedDefectDataObj>> defectsByComponent = getProjectDefectsByComponent(); // 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(); // TODO: Map<String, List<String>> componentUsers = getUserMapping(); // System.out.println("Component=" + componentDefectValues.getKey()); if (componentDefects.size() > 0) { StringBuilder html = new StringBuilder(); html.append("<html><body><p>The following defects have been detected in the "); html.append(componentName); html.append(" component within the past "); if (days == 1) { html.append("24 hours.</p>"); } else { html.append(days + " days.</p>"); } Map<String, List<MergedDefectDataObj>> defectsByChecker = new HashMap<String, List<MergedDefectDataObj>>(); for (MergedDefectDataObj componentDefect : componentDefects) { // System.out.println("cid=" + componentDefect.getCid() + // ", component=" + componentName); List<MergedDefectDataObj> checkerDefects = (List<MergedDefectDataObj>) defectsByChecker .get(componentDefect.getCheckerName()); if (checkerDefects == null) { checkerDefects = new ArrayList<MergedDefectDataObj>(); defectsByChecker.put(componentDefect.getCheckerName(), checkerDefects); } checkerDefects.add(componentDefect); } // process checker information for current component html.append("<style type='text/css'>"); html.append("td.datacellone {"); html.append(" background-color: #F2F2F2; color: black;"); html.append("}"); html.append("td.datacelltwo {"); html.append(" background-color: #FFFFFF; color: black;"); html.append("}"); html.append("td.datacellthree {"); html.append(" background-color: #BDBDBD; color: black;"); html.append("}"); html.append("</style>"); html.append("<br/><table border=\"1\" cellpadding=\"3\"><tr> <th>Checker</th> <th>New</th> <th>Outstanding</th> <th>Resolved</th></tr>"); // column totals int totalNew = 0; int totalOutstanding = 0; int totalResolved = 0; int i = 0; for (Map.Entry<String, List<MergedDefectDataObj>> checkerDefectEntries : defectsByChecker .entrySet()) { List<MergedDefectDataObj> checkerDefects = checkerDefectEntries .getValue(); final String checkerName = (String) checkerDefectEntries .getKey(); int checkerNew = 0; int checkerOutstanding = 0; int checkerResolved = 0; for (MergedDefectDataObj checkerDefect : checkerDefects) { // new if (checkerDefect.getStatus().equals("New")) { checkerNew++; } // outstanding else if (checkerDefect.getStatus().equals("Triaged")) { checkerOutstanding++; } // resolved: note assume dismissed and fixed else if (checkerDefect.getStatus().equals("Dismissed") || checkerDefect.getStatus().equals("Fixed")) { checkerResolved++; } } /*System.out.println("\tchecker=" + checkerName + ", new=" + checkerNew + ", outstanding=" + checkerOutstanding + ", resolved=" + checkerResolved);*/ String td; if (i % 2 == 0) { td = "<td class='datacellone'>"; } else { td = "<td class='datacelltwo'>"; } html.append("<tr>"); html.append(td); html.append(checkerName); html.append("</td>"); html.append(td); html.append(checkerNew); html.append("</td>"); html.append(td); html.append(checkerOutstanding); html.append("</td>"); html.append(td); html.append(checkerResolved); html.append("</td></tr>"); // update totalNew += checkerNew; totalOutstanding += checkerOutstanding; totalResolved += checkerResolved; i++; } // totals String td = "<td class='datacellthree'>"; html.append("<tr>"); html.append(td); html.append("Total</td>"); html.append(td); html.append(totalNew); html.append("</td>"); html.append(td); html.append(totalOutstanding); html.append("</td>"); html.append(td); html.append(totalResolved); html.append("</td></tr>"); html.append("</table></body></html>"); // System.out.println("Raw HTML=" + html); // e-mail to component owners if (componentUsers.containsKey(componentName)) { List<String> notifyUsers = (List<String>) componentUsers .get(componentName); for (String notifyUser : notifyUsers) { if (this.isDryRun) { System.out.println("DRY-RUN recipient=" + notifyUser + ", html=" + html); } else { final String subject = "New defects assigned to you in Coverity"; String recipient = CimProxy.getInstance().notify(notifyUser, subject, html.toString()); if (recipient.length() > 0) { System.out.println("e-mail sucessfully sent to " + recipient); } else { System.out.println("*** Unable to e-mail " + recipient + " ***"); } } } } else { System.out.println("*** Unable to find users assigned to component \"" + componentName + "\" ***"); } } else { System.out.println("\tNo new defects"); } } 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 checker mapping Map<String, List<String>> userMapping = new HashMap<String, List<String>>(); NodeList checkerNode = document.getDocumentElement() .getElementsByTagName("component"); for (int i = 0; i < checkerNode.getLength(); i++) { Element compElem = (Element) checkerNode.item(i); final String checkerName = 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(checkerName, 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) { NotifyComponentOwners notifyOwners = new NotifyComponentOwners( 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 " + NotifyComponentOwners.class.getName() + " <project-name> <num-days> <comp-config-file> <is-dry-run>"); } } catch (Exception e) { e.printStackTrace(); } } }