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