001 package org.hackystat.dailyprojectdata.resource.filemetric; 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 import org.hackystat.dailyprojectdata.resource.dailyprojectdata.DailyProjectDataResource; 017 import org.hackystat.dailyprojectdata.resource.filemetric.jaxb.FileData; 018 import org.hackystat.dailyprojectdata.resource.filemetric.jaxb.FileMetricDailyProjectData; 019 import org.hackystat.sensorbase.client.SensorBaseClient; 020 import org.hackystat.sensorbase.resource.sensordata.jaxb.Property; 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 035 * {host}/filemetric/{user}/{project}/{starttime} requests. Requires the 036 * authenticated user to be {user}, the Admin, or a member of {project}. 037 * 038 * @author Cam Moore, Philip Johnson 039 */ 040 public class FileMetricResource extends DailyProjectDataResource { 041 042 private String sizeMetric; 043 044 private String tool; 045 046 /** 047 * The standard constructor. 048 * 049 * @param context The context. 050 * @param request The request object. 051 * @param response The response object. 052 */ 053 public FileMetricResource(Context context, Request request, Response response) { 054 super(context, request, response); 055 this.sizeMetric = (String) request.getAttributes().get("sizemetric"); 056 this.tool = (String) request.getAttributes().get("tool"); 057 } 058 059 /** 060 * Returns an FileMetricDailyProjectData instance representing the FileMetric 061 * associated with the sizeMetric for the Project data, or null if not authorized. 062 * Authenticated user must be the uriUser, or Admin, or project member. 063 * 064 * @param variant The representational variant requested. 065 * @return The representation. 066 */ 067 @Override 068 public Representation represent(Variant variant) { 069 Logger logger = this.server.getLogger(); 070 logger.fine("FileMetric DPD: Starting"); 071 if (variant.getMediaType().equals(MediaType.TEXT_XML)) { 072 try { 073 // [1] get the SensorBaseClient for the user making this request. 074 SensorBaseClient client = super.getSensorBaseClient(); 075 // [2] Check the front side cache and return if the DPD is found and is OK to access. 076 String cachedDpd = this.server.getFrontSideCache().get(uriUser, project, uriString); 077 if ((cachedDpd != null) && client.inProject(uriUser, project)) { 078 return super.getStringRepresentation(cachedDpd); 079 } 080 // [2] Get the Snapshot containing the last sent FileMetric data for this Project. 081 XMLGregorianCalendar startTime = Tstamp.makeTimestamp(this.timestamp); 082 XMLGregorianCalendar endTime = Tstamp.incrementDays(startTime, 1); 083 logger.fine("FileMetric DPD: Requesting index: " + uriUser + " " + project); 084 SensorDataIndex snapshot = (this.tool == null) ? 085 // Get the latest snapshot from any tool. 086 client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 087 "FileMetric") : 088 // Get the latest snapshot from the specified tool. 089 client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 090 "FileMetric", this.tool); 091 092 logger.fine("FileMetric DPD: Got index: " + 093 snapshot.getSensorDataRef().size() + " instances. Now retrieving instances."); 094 // [3] create and return the FileMetricDailyProjectData instance. 095 double total = 0; 096 FileMetricDailyProjectData fileDpd = new FileMetricDailyProjectData(); 097 fileDpd.setOwner(uriUser); 098 fileDpd.setProject(project); 099 fileDpd.setStartTime(startTime); 100 fileDpd.setSizeMetric(this.sizeMetric); 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 Double value = getNumberProperty(data, this.sizeMetric); 108 if (value != null) { //NOPMD 109 FileData fileData = new FileData(); 110 fileData.setFileUri(data.getResource()); 111 fileData.setSizeMetricValue(value); 112 fileDpd.getFileData().add(fileData); 113 total += value; 114 } 115 } 116 } 117 fileDpd.setTotal(total); 118 String xmlData = makeFileMetric(fileDpd); 119 if (!Tstamp.isTodayOrLater(startTime)) { 120 this.server.getFrontSideCache().put(uriUser, project, uriString, xmlData); 121 } 122 logRequest("FileMetric", this.tool, this.sizeMetric); 123 return super.getStringRepresentation(xmlData); 124 } 125 catch (Exception e) { 126 setStatusError("Error creating Coverage 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 makeFileMetric(FileMetricDailyProjectData data) throws Exception { 142 JAXBContext fileMetricJAXB = (JAXBContext) this.server.getContext().getAttributes().get( 143 "FileMetricJAXB"); 144 Marshaller marshaller = fileMetricJAXB.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 a Double as the value of key in data, or null if not found. Also null if 161 * the property was found but could not be converted to a Double. 162 * @param data The sensor data instance. 163 * @param key The key whose Double value is to be retrieved. 164 * @return The value, as a double. 165 */ 166 private Double getNumberProperty(SensorData data, String key) { 167 String prop = getProperty(data, key); 168 if (prop == null) { 169 return null; 170 } 171 // If we got a property, then make it a Double. 172 Double metricValue = null; 173 try { 174 metricValue = Double.parseDouble(getProperty(data, this.sizeMetric)); 175 } 176 catch (Exception e) { 177 this.server.getLogger().info("In FileMetric Resource, parse into Double failed: " + 178 getProperty(data, this.sizeMetric)); 179 } 180 return metricValue; 181 } 182 183 /** 184 * Returns the string value associated with the specified property key. If no 185 * property with that key exists, null is returned. 186 * @param data The sensor data instance whose property list will be searched. 187 * @param key the property key to search for. 188 * @return The property value with the specified key or null. 189 */ 190 private String getProperty(SensorData data, String key) { 191 List<Property> propertyList = data.getProperties().getProperty(); 192 for (Property property : propertyList) { 193 if (key.equals(property.getKey())) { 194 return property.getValue(); 195 } 196 } 197 return null; 198 } 199 }