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    }