001    package org.hackystat.dailyprojectdata.resource.coupling;
002    
003    import java.io.StringWriter;
004    import java.math.BigInteger;
005    import java.util.List;
006    import java.util.logging.Logger;
007    
008    import javax.xml.bind.JAXBContext;
009    import javax.xml.bind.Marshaller;
010    import javax.xml.datatype.XMLGregorianCalendar;
011    import javax.xml.parsers.DocumentBuilder;
012    import javax.xml.parsers.DocumentBuilderFactory;
013    import javax.xml.transform.Transformer;
014    import javax.xml.transform.TransformerFactory;
015    import javax.xml.transform.dom.DOMSource;
016    import javax.xml.transform.stream.StreamResult;
017    
018    import org.hackystat.dailyprojectdata.resource.dailyprojectdata.DailyProjectDataResource;
019    import org.hackystat.dailyprojectdata.resource.coupling.jaxb.CouplingDailyProjectData;
020    import org.hackystat.dailyprojectdata.resource.coupling.jaxb.CouplingData;
021    import org.hackystat.sensorbase.client.SensorBaseClient;
022    import org.hackystat.sensorbase.resource.sensordata.jaxb.Property;
023    import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData;
024    import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex;
025    import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef;
026    import org.hackystat.utilities.tstamp.Tstamp;
027    import org.restlet.Context;
028    import org.restlet.data.MediaType;
029    import org.restlet.data.Request;
030    import org.restlet.data.Response;
031    import org.restlet.resource.Representation;
032    import org.restlet.resource.Variant;
033    import org.w3c.dom.Document;
034    
035    /**
036     * Implements the Resource for processing GET
037     * {host}/coupling/{user}/{project}/{starttime}/{type} requests. Requires the
038     * authenticated user to be {user}, the Admin, or a member of {project}.
039     * 
040     * @author Philip Johnson
041     */
042    public class CouplingResource extends DailyProjectDataResource {
043      
044      private String type;
045      
046      private String tool;
047    
048      /**
049       * The standard constructor.
050       * 
051       * @param context The context.
052       * @param request The request object.
053       * @param response The response object.
054       */
055      public CouplingResource(Context context, Request request, Response response) {
056        super(context, request, response);
057        this.type = (String) request.getAttributes().get("type");
058        this.tool = (String) request.getAttributes().get("tool");
059      }
060    
061      /**
062       * Returns a CouplingDailyProjectData instance representing the Coupling
063       * associated with each URI for the Project data, or null if not authorized.
064       * Authenticated user must be the uriUser, or Admin, or project member. 
065       * 
066       * @param variant The representational variant requested.
067       * @return The representation.
068       */
069      @Override
070      public Representation represent(Variant variant) {
071        Logger logger = this.server.getLogger();
072        logger.fine("Coupling DPD: Starting");
073        if (variant.getMediaType().equals(MediaType.TEXT_XML)) {
074          try {
075            // [1] get the SensorBaseClient for the user making this request.
076            SensorBaseClient client = super.getSensorBaseClient();
077            // [2] Check the front side cache and return if the DPD is found and is OK to access.
078            String cachedDpd = this.server.getFrontSideCache().get(uriUser, project, uriString);
079            if ((cachedDpd != null) && client.inProject(uriUser, project)) {
080              return super.getStringRepresentation(cachedDpd);
081            }
082            // [2] Get the Snapshot containing the last sent FileMetric data for this Project.
083            XMLGregorianCalendar startTime = Tstamp.makeTimestamp(this.timestamp);
084            XMLGregorianCalendar endTime = Tstamp.incrementDays(startTime, 1);
085            logger.fine("Coupling DPD: Requesting index: " + uriUser + " " + project);
086            SensorDataIndex snapshot = (this.tool == null) ?
087                // Get the latest snapshot from any tool.
088                client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 
089                "Coupling") :
090                  // Get the latest snapshot from the specified tool.
091                  client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 
092                      "Coupling", this.tool);
093    
094            logger.fine("Coupling DPD: Got index: " + 
095                snapshot.getSensorDataRef().size() + " instances. Now retrieving instances.");
096            // [3] create and return the CouplingDailyProjectData instance.
097            CouplingDailyProjectData couplingDpd = new CouplingDailyProjectData();
098            couplingDpd.setOwner(uriUser);
099            couplingDpd.setProject(project);
100            couplingDpd.setStartTime(startTime);
101            couplingDpd.setType(this.type);
102            
103            if (!snapshot.getSensorDataRef().isEmpty()) {
104              for (SensorDataRef ref : snapshot.getSensorDataRef()) {
105                SensorData data = client.getSensorData(ref);
106                couplingDpd.setOwner(data.getOwner());
107                couplingDpd.setTool(data.getTool());
108                Integer afferent = getIntegerProperty(data, "Afferent");
109                Integer efferent = getIntegerProperty(data, "Efferent");
110                if ((afferent != null) && (efferent != null)) { //NOPMD
111                  CouplingData couplingData = new CouplingData();
112                  couplingData.setUri(data.getResource());
113                  couplingData.setAfferent(BigInteger.valueOf(afferent));
114                  couplingData.setEfferent(BigInteger.valueOf(efferent));
115                  couplingDpd.getCouplingData().add(couplingData);
116                }
117              }
118            }
119            String xmlData = makeCouplingMetric(couplingDpd);
120            if (!Tstamp.isTodayOrLater(startTime)) {
121              this.server.getFrontSideCache().put(uriUser, project, uriString, xmlData);
122            }
123            logRequest("Coupling", this.tool, this.type);
124            return super.getStringRepresentation(xmlData);
125          } 
126          catch (Exception e) {
127            setStatusError("Error creating Coupling DPD.", e);
128            return null;
129          }
130        }
131        return null;
132      }
133    
134      /**
135       * Returns the passed SensorData instance as a String encoding of its XML
136       * representation.
137       * 
138       * @param data The SensorData instance.
139       * @return The XML String representation.
140       * @throws Exception If problems occur during translation.
141       */
142      private String makeCouplingMetric(CouplingDailyProjectData data) throws Exception {
143        JAXBContext complexityJAXB = (JAXBContext) this.server.getContext().getAttributes().get(
144            "CouplingJAXB");
145        Marshaller marshaller = complexityJAXB.createMarshaller();
146        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
147        dbf.setNamespaceAware(true);
148        DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
149        Document doc = documentBuilder.newDocument();
150        marshaller.marshal(data, doc);
151        DOMSource domSource = new DOMSource(doc);
152        StringWriter writer = new StringWriter();
153        StreamResult result = new StreamResult(writer);
154        TransformerFactory tf = TransformerFactory.newInstance();
155        Transformer transformer = tf.newTransformer();
156        transformer.transform(domSource, result);
157        return writer.toString();
158      }
159      
160      /**
161       * Returns the string value associated with the specified property key. If no
162       * property with that key exists, null is returned.
163       * @param data The sensor data instance whose property list will be searched.
164       * @param key the property key to search for.
165       * @return The property value with the specified key or null.
166       */
167      private String getProperty(SensorData data, String key) {
168        List<Property> propertyList = data.getProperties().getProperty();
169        for (Property property : propertyList) {
170          if (key.equals(property.getKey())) {
171            return property.getValue();
172          }
173        }
174        return null;
175      }
176      
177      /**
178       * Returns the Integer value for the given property, or null if not present or not 
179       * parsable to an Integer. 
180       * @param data The SensorData instance to inspect for the given property.
181       * @param property The property to retrieve.
182       * @return The property value as an Integer, or null if not found.
183       */
184      private Integer getIntegerProperty(SensorData data, String property) {
185        String prop = getProperty(data, property);
186        Double propertyNum = null;
187        try {
188          propertyNum = Double.parseDouble(prop);
189        }
190        catch (Exception e) { //NOPMD
191          // Don't do anything, ok to drop through.
192        }
193        return ((propertyNum == null) ? null : propertyNum.intValue());
194      }
195    }
196