001 package org.hackystat.dailyprojectdata.resource.complexity; 002 003 import java.io.StringWriter; 004 import java.util.List; 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.complexity.jaxb.ComplexityDailyProjectData; 018 import org.hackystat.dailyprojectdata.resource.dailyprojectdata.DailyProjectDataResource; 019 import org.hackystat.dailyprojectdata.resource.complexity.jaxb.FileData; 020 import org.hackystat.sensorbase.client.SensorBaseClient; 021 import org.hackystat.sensorbase.resource.sensordata.jaxb.Property; 022 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData; 023 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex; 024 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef; 025 import org.hackystat.utilities.tstamp.Tstamp; 026 import org.restlet.Context; 027 import org.restlet.data.MediaType; 028 import org.restlet.data.Request; 029 import org.restlet.data.Response; 030 import org.restlet.resource.Representation; 031 import org.restlet.resource.Variant; 032 import org.w3c.dom.Document; 033 034 /** 035 * Implements the Resource for processing GET 036 * {host}/complexity/{user}/{project}/{starttime}/{type} requests. Requires the 037 * authenticated user to be {user}, the Admin, or a member of {project}. 038 * 039 * @author Philip Johnson 040 */ 041 public class ComplexityResource extends DailyProjectDataResource { 042 043 private String type; 044 045 private String tool; 046 047 /** 048 * The standard constructor. 049 * 050 * @param context The context. 051 * @param request The request object. 052 * @param response The response object. 053 */ 054 public ComplexityResource(Context context, Request request, Response response) { 055 super(context, request, response); 056 this.type = (String) request.getAttributes().get("type"); 057 this.tool = (String) request.getAttributes().get("tool"); 058 } 059 060 /** 061 * Returns a ComplexityDailyProjectData instance representing the Complexity 062 * associated with each file for the Project data, or null if not authorized. 063 * Authenticated user must be the uriUser, or Admin, or project member. 064 * 065 * @param variant The representational variant requested. 066 * @return The representation. 067 */ 068 @Override 069 public Representation represent(Variant variant) { 070 Logger logger = this.server.getLogger(); 071 logger.fine("Complexity DPD: Starting"); 072 if (variant.getMediaType().equals(MediaType.TEXT_XML)) { 073 try { 074 // [1] get the SensorBaseClient for the user making this request. 075 SensorBaseClient client = super.getSensorBaseClient(); 076 // [2] Check the front side cache and return if the DPD is found and is OK to access. 077 String cachedDpd = this.server.getFrontSideCache().get(uriUser, project, uriString); 078 if ((cachedDpd != null) && client.inProject(uriUser, project)) { 079 return super.getStringRepresentation(cachedDpd); 080 } 081 // [2] Get the Snapshot containing the last sent FileMetric data for this Project. 082 XMLGregorianCalendar startTime = Tstamp.makeTimestamp(this.timestamp); 083 XMLGregorianCalendar endTime = Tstamp.incrementDays(startTime, 1); 084 logger.fine("Complexity DPD: Requesting index: " + uriUser + " " + project); 085 SensorDataIndex snapshot = (this.tool == null) ? 086 // Get the latest snapshot from any tool. 087 client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 088 "FileMetric") : 089 // Get the latest snapshot from the specified tool. 090 client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 091 "FileMetric", this.tool); 092 093 logger.fine("Complexity DPD: Got index: " + 094 snapshot.getSensorDataRef().size() + " instances. Now retrieving instances."); 095 // [3] create and return the ComplexityDailyProjectData instance. 096 ComplexityDailyProjectData fileDpd = new ComplexityDailyProjectData(); 097 fileDpd.setOwner(uriUser); 098 fileDpd.setProject(project); 099 fileDpd.setStartTime(startTime); 100 fileDpd.setType(this.type); 101 102 if (!snapshot.getSensorDataRef().isEmpty()) { 103 for (SensorDataRef ref : snapshot.getSensorDataRef()) { 104 SensorData data = client.getSensorData(ref); 105 fileDpd.setOwner(data.getOwner()); 106 fileDpd.setTool(data.getTool()); 107 String property = this.type + "ComplexityList"; 108 String complexities = getProperty(data, property); 109 if (complexities != null) { //NOPMD 110 FileData fileData = new FileData(); 111 fileData.setFileUri(data.getResource()); 112 fileData.setComplexityValues(complexities); 113 fileData.setTotalLines(getProperty(data, "TotalLines")); 114 fileDpd.getFileData().add(fileData); 115 } 116 } 117 } 118 String xmlData = makeComplexityMetric(fileDpd); 119 if (!Tstamp.isTodayOrLater(startTime)) { 120 this.server.getFrontSideCache().put(uriUser, project, uriString, xmlData); 121 } 122 logRequest("Complexity", this.tool, this.type); 123 return super.getStringRepresentation(xmlData); 124 } 125 catch (Exception e) { 126 setStatusError("Error creating Complexity DPD.", e); 127 return null; 128 } 129 } 130 return null; 131 } 132 133 /** 134 * Returns the passed SensorData instance as a String encoding of its XML 135 * representation. 136 * 137 * @param data The SensorData instance. 138 * @return The XML String representation. 139 * @throws Exception If problems occur during translation. 140 */ 141 private String makeComplexityMetric(ComplexityDailyProjectData data) throws Exception { 142 JAXBContext complexityJAXB = (JAXBContext) this.server.getContext().getAttributes().get( 143 "ComplexityJAXB"); 144 Marshaller marshaller = complexityJAXB.createMarshaller(); 145 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 146 dbf.setNamespaceAware(true); 147 DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); 148 Document doc = documentBuilder.newDocument(); 149 marshaller.marshal(data, doc); 150 DOMSource domSource = new DOMSource(doc); 151 StringWriter writer = new StringWriter(); 152 StreamResult result = new StreamResult(writer); 153 TransformerFactory tf = TransformerFactory.newInstance(); 154 Transformer transformer = tf.newTransformer(); 155 transformer.transform(domSource, result); 156 return writer.toString(); 157 } 158 159 /** 160 * Returns the string value associated with the specified property key. If no 161 * property with that key exists, null is returned. 162 * @param data The sensor data instance whose property list will be searched. 163 * @param key the property key to search for. 164 * @return The property value with the specified key or null. 165 */ 166 private String getProperty(SensorData data, String key) { 167 List<Property> propertyList = data.getProperties().getProperty(); 168 for (Property property : propertyList) { 169 if (key.equals(property.getKey())) { 170 return property.getValue(); 171 } 172 } 173 return null; 174 } 175 }