001 package org.hackystat.dailyprojectdata.resource.codeissue; 002 003 import java.io.StringWriter; 004 import java.util.Set; 005 import java.util.logging.Logger; 006 007 import javax.xml.bind.JAXBContext; 008 import javax.xml.bind.Marshaller; 009 import javax.xml.datatype.XMLGregorianCalendar; 010 import javax.xml.parsers.DocumentBuilder; 011 import javax.xml.parsers.DocumentBuilderFactory; 012 import javax.xml.transform.Transformer; 013 import javax.xml.transform.TransformerFactory; 014 import javax.xml.transform.dom.DOMSource; 015 import javax.xml.transform.stream.StreamResult; 016 017 import org.hackystat.dailyprojectdata.resource.codeissue.jaxb.CodeIssueDailyProjectData; 018 import org.hackystat.dailyprojectdata.resource.codeissue.jaxb.CodeIssueData; 019 import org.hackystat.dailyprojectdata.resource.dailyprojectdata.DailyProjectDataResource; 020 import org.hackystat.sensorbase.client.SensorBaseClient; 021 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData; 022 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex; 023 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef; 024 import org.hackystat.utilities.tstamp.Tstamp; 025 import org.restlet.Context; 026 import org.restlet.data.MediaType; 027 import org.restlet.data.Request; 028 import org.restlet.data.Response; 029 import org.restlet.resource.Representation; 030 import org.restlet.resource.Variant; 031 import org.w3c.dom.Document; 032 033 /** 034 * Implements the Resource for processing GET {host}/codeissue/{user}/{project}/{timestamp} 035 * requests. 036 * 037 * Authenticated user must be the uriUser, or Admin, or project member. 038 * 039 * @author Philip Johnson, Julie Sakuda. 040 */ 041 public class CodeIssueResource extends DailyProjectDataResource { 042 043 /** The optional code issue tool. */ 044 private String tool; 045 046 /** The optional type. */ 047 private String type; 048 049 /** 050 * The standard constructor. 051 * 052 * @param context The context. 053 * @param request The request object. 054 * @param response The response object. 055 */ 056 public CodeIssueResource(Context context, Request request, Response response) { 057 super(context, request, response); 058 this.tool = (String) request.getAttributes().get("Tool"); 059 // get the type and remove any spaces 060 this.type = (String) request.getAttributes().get("Type"); 061 if (this.type != null) { 062 this.type = type.replaceAll(" ", ""); 063 } 064 } 065 066 /** 067 * Returns a CodeIssueDailyProjectData instance representing the CodeIssues associated with 068 * the Project data, or null if not authorized. 069 * 070 * @param variant The representational variant requested. 071 * @return The representation. 072 */ 073 @Override 074 public Representation represent(Variant variant) { 075 Logger logger = this.server.getLogger(); 076 logger.fine("CodeIssue DPD: Starting"); 077 if (variant.getMediaType().equals(MediaType.TEXT_XML)) { 078 try { 079 // [1] get the SensorBaseClient for the user making this request. 080 SensorBaseClient client = super.getSensorBaseClient(); 081 // [2] Check the front side cache and return if the DPD is found and is OK to access. 082 String cachedDpd = this.server.getFrontSideCache().get(uriUser, project, uriString); 083 if ((cachedDpd != null) && client.inProject(uriUser, project)) { 084 return super.getStringRepresentation(cachedDpd); 085 } 086 // [2] get a SensorDataIndex of all CodeIssue data for this Project on the requested day. 087 XMLGregorianCalendar startTime = Tstamp.makeTimestamp(this.timestamp); 088 XMLGregorianCalendar endTime = Tstamp.incrementDays(startTime, 1); 089 logger.fine("CodeIssue DPD: Requesting index: " + uriUser + " " + project); 090 SensorDataIndex index = client.getProjectSensorData(uriUser, project, startTime, 091 endTime, "CodeIssue"); 092 logger.fine("CodeIssue DPD: Got index: " + index.getSensorDataRef().size() + " instances"); 093 094 // [3] Create a MultiToolSnapshot generated from all CodeIssue sensor data for this day. 095 MultiToolSnapshot snapshot = new MultiToolSnapshot(); 096 for (SensorDataRef ref : index.getSensorDataRef()) { 097 SensorData data = client.getSensorData(ref); 098 snapshot.add(data); 099 } 100 logger.fine("CodeIssue DPD: retrieved all instances. Now building DPD."); 101 // [4] Create the codeIssue DPD. 102 CodeIssueDailyProjectData codeIssue = new CodeIssueDailyProjectData(); 103 104 // [4.1] Case 1: tool and type are null. Add an entry for all CodeIssueTypes in all tools. 105 if ((this.tool == null ) && (this.type == null)) { 106 for (String tool : snapshot.getTools()) { 107 Set<SensorData> toolSnapshot = snapshot.getSensorData(tool); 108 IssueTypeCounter counter = new IssueTypeCounter(toolSnapshot, logger); 109 for (String issueType : counter.getTypes()) { 110 codeIssue.getCodeIssueData().add(makeCodeIssueData(tool, issueType, counter)); 111 } 112 // Add a zero entry if necessary. 113 if (counter.getTypes().isEmpty()) { //NOPMD 114 codeIssue.getCodeIssueData().add(makeZeroCodeIssueData(tool)); 115 } 116 } 117 } 118 119 // [4.2] Case 2: tool is specified, type is null. Add entry for all types for this tool. 120 if ((this.tool != null ) && (this.type == null)) { 121 Set<SensorData> toolSnapshot = snapshot.getSensorData(this.tool); 122 IssueTypeCounter counter = new IssueTypeCounter(toolSnapshot, this.getLogger()); 123 for (String issueType : counter.getTypes()) { 124 codeIssue.getCodeIssueData().add(makeCodeIssueData(this.tool, issueType, counter)); 125 } 126 // Add a zero entry if we have data for this tool, but no issues. 127 if (!toolSnapshot.isEmpty() && counter.getTypes().isEmpty()) { //NOPMD 128 codeIssue.getCodeIssueData().add(makeZeroCodeIssueData(tool)); 129 } 130 } 131 132 // [4.3] Case 3: type is specified, tool is null. Add entry for all occurrences of this type 133 if ((this.tool == null ) && (this.type != null)) { 134 for (String tool : snapshot.getTools()) { 135 Set<SensorData> toolSnapshot = snapshot.getSensorData(tool); 136 IssueTypeCounter counter = new IssueTypeCounter(toolSnapshot, this.getLogger()); 137 for (String issueType : counter.getTypes()) { 138 if (issueType.equals(this.type)) { //NOPMD 139 codeIssue.getCodeIssueData().add(makeCodeIssueData(tool, issueType, counter)); 140 } 141 } 142 } 143 // Not sure how to indicate 'zero' in this case. So don't try. 144 } 145 146 // [4.4] Case 4: tool and type are specified. Add entry for this tool and this type. 147 if ((this.tool != null ) && (this.type != null)) { 148 Set<SensorData> toolSnapshot = snapshot.getSensorData(this.tool); 149 IssueTypeCounter counter = new IssueTypeCounter(toolSnapshot, this.getLogger()); 150 for (String issueType : counter.getTypes()) { 151 if (this.type.equals(issueType)) { //NOPMD 152 codeIssue.getCodeIssueData().add(makeCodeIssueData(this.tool, issueType, counter)); 153 } 154 } 155 // Add a zero entry if we have data for this tool, but no issues of that type 156 if (!toolSnapshot.isEmpty() && !counter.getTypes().contains(this.type)) { //NOPMD 157 codeIssue.getCodeIssueData().add(makeZeroCodeIssueData(tool)); 158 } 159 } 160 161 // Now finish building the structure 162 codeIssue.setStartTime(startTime); 163 codeIssue.setOwner(uriUser); 164 codeIssue.setProject(project); 165 codeIssue.setUriPattern("**"); // we don't support UriPatterns yet. 166 167 String xmlData = this.makeCodeIssue(codeIssue); 168 if (!Tstamp.isTodayOrLater(startTime)) { 169 this.server.getFrontSideCache().put(uriUser, project, uriString, xmlData); 170 } 171 logRequest("CodeIssue", this.tool, this.type); 172 return super.getStringRepresentation(xmlData); 173 } 174 catch (Exception e) { 175 setStatusError("Error creating CodeIssue DPD.", e); 176 return null; 177 } 178 } 179 return null; 180 } 181 182 183 /** 184 * Creates a returns a CodeIssueData instance. 185 * @param tool The Tool. 186 * @param issueType The Issue Type. 187 * @param counter The CodeIssueCounter. 188 * @return The CodeIssueData instance. 189 */ 190 private CodeIssueData makeCodeIssueData(String tool, String issueType, IssueTypeCounter counter) { 191 CodeIssueData issueData = new CodeIssueData(); 192 issueData.setTool(tool); 193 issueData.setIssueType(issueType); 194 issueData.setNumIssues(counter.getCount(issueType)); 195 return issueData; 196 } 197 198 /** 199 * Creates a zero CodeIssueData entry for the specified tool. 200 * @param tool The tool with zero issues. 201 * @return The "zero" CodeIssueData entry for this tool. 202 */ 203 private CodeIssueData makeZeroCodeIssueData(String tool) { 204 CodeIssueData issueData = new CodeIssueData(); 205 issueData.setTool(tool); 206 issueData.setNumIssues(0); 207 return issueData; 208 } 209 210 /** 211 * Returns the passed SensorData instance as a String encoding of its XML representation. 212 * 213 * @param data The SensorData instance. 214 * @return The XML String representation. 215 * @throws Exception If problems occur during translation. 216 */ 217 private String makeCodeIssue(CodeIssueDailyProjectData data) throws Exception { 218 JAXBContext codeIssueJAXB = (JAXBContext) this.server.getContext().getAttributes().get( 219 "CodeIssueJAXB"); 220 Marshaller marshaller = codeIssueJAXB.createMarshaller(); 221 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 222 dbf.setNamespaceAware(true); 223 DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); 224 Document doc = documentBuilder.newDocument(); 225 marshaller.marshal(data, doc); 226 DOMSource domSource = new DOMSource(doc); 227 StringWriter writer = new StringWriter(); 228 StreamResult result = new StreamResult(writer); 229 TransformerFactory tf = TransformerFactory.newInstance(); 230 Transformer transformer = tf.newTransformer(); 231 transformer.transform(domSource, result); 232 return writer.toString(); 233 } 234 }