001 package org.hackystat.dailyprojectdata.resource.build; 002 003 import static org.hackystat.dailyprojectdata.server.ServerProperties.SENSORBASE_FULLHOST_KEY; 004 005 import java.io.StringWriter; 006 import java.util.List; 007 import java.util.Map; 008 import java.util.Set; 009 import java.util.logging.Logger; 010 011 import javax.xml.bind.JAXBContext; 012 import javax.xml.bind.Marshaller; 013 import javax.xml.datatype.XMLGregorianCalendar; 014 import javax.xml.parsers.DocumentBuilder; 015 import javax.xml.parsers.DocumentBuilderFactory; 016 import javax.xml.transform.Transformer; 017 import javax.xml.transform.TransformerFactory; 018 import javax.xml.transform.dom.DOMSource; 019 import javax.xml.transform.stream.StreamResult; 020 021 import org.hackystat.dailyprojectdata.resource.build.jaxb.BuildDailyProjectData; 022 import org.hackystat.dailyprojectdata.resource.build.jaxb.MemberData; 023 import org.hackystat.dailyprojectdata.resource.dailyprojectdata.DailyProjectDataResource; 024 import org.hackystat.sensorbase.client.SensorBaseClient; 025 import org.hackystat.sensorbase.resource.sensordata.jaxb.Properties; 026 import org.hackystat.sensorbase.resource.sensordata.jaxb.Property; 027 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData; 028 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex; 029 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef; 030 import org.hackystat.utilities.tstamp.Tstamp; 031 import org.restlet.Context; 032 import org.restlet.data.MediaType; 033 import org.restlet.data.Request; 034 import org.restlet.data.Response; 035 import org.restlet.resource.Representation; 036 import org.restlet.resource.Variant; 037 import org.w3c.dom.Document; 038 039 /** 040 * Implements the Resource for processing GET {host}/build/{user}/{project}/{timestamp} 041 * requests. 042 * Authenticated user must be the uriUser, or Admin, or project member. 043 * 044 * @author Philip Johnson 045 */ 046 public class BuildResource extends DailyProjectDataResource { 047 048 /** The optional type. */ 049 private String type; 050 051 /** 052 * The standard constructor. 053 * 054 * @param context The context. 055 * @param request The request object. 056 * @param response The response object. 057 */ 058 public BuildResource(Context context, Request request, Response response) { 059 super(context, request, response); 060 this.type = (String) request.getAttributes().get("Type"); 061 } 062 063 /** 064 * Returns a BuildDailyProjectData instance representing the build totals associated with the 065 * Project data, or null if not authorized. 066 * 067 * @param variant The representational variant requested. 068 * @return The representation. 069 */ 070 @Override 071 public Representation represent(Variant variant) { 072 Logger logger = this.server.getLogger(); 073 logger.fine("Build DPD: Starting"); 074 if (variant.getMediaType().equals(MediaType.TEXT_XML)) { 075 try { 076 // [1] get the SensorBaseClient for the user making this request. 077 SensorBaseClient client = super.getSensorBaseClient(); 078 // [2] Check the front side cache and return if the DPD is found and is OK to access. 079 String cachedDpd = this.server.getFrontSideCache().get(uriUser, project, uriString); 080 if ((cachedDpd != null) && client.inProject(uriUser, project)) { 081 return super.getStringRepresentation(cachedDpd); 082 } 083 // [2] get a SensorDataIndex of all Build data for this Project on the requested day. 084 XMLGregorianCalendar startTime = Tstamp.makeTimestamp(this.timestamp); 085 XMLGregorianCalendar endTime = Tstamp.incrementDays(startTime, 1); 086 logger.fine("Build DPD: Requesting index: " + uriUser + " " + project); 087 SensorDataIndex index = null; 088 index = client.getProjectSensorData(uriUser, project, startTime, 089 endTime, "Build"); 090 logger.fine("Build DPD: Got index: " + index.getSensorDataRef().size() + " instances"); 091 // [3] update the build data counter 092 MemberBuildCounter counter = new MemberBuildCounter(); 093 List<SensorDataRef> sensorDataRefList = index.getSensorDataRef(); 094 for (SensorDataRef sensorDataRef : sensorDataRefList) { 095 SensorData data = client.getSensorData(sensorDataRef); 096 String result = this.getPropertyValue(data, "Result"); 097 boolean valid = this.isValidData(data); 098 if (valid && "Success".equals(result)) { 099 counter.addSuccessfulBuild(data.getOwner()); 100 } 101 else if (valid && "Failure".equals(result)) { 102 counter.addFailedBuild(data.getOwner()); 103 } 104 } 105 logger.fine("Build DPD: retrieved all instances, now building the DPD."); 106 // [4] create and return the BuildDailyProjectData 107 BuildDailyProjectData build = new BuildDailyProjectData(); 108 String sensorBaseHost = this.server.getServerProperties().get(SENSORBASE_FULLHOST_KEY); 109 Map<String, Integer> successfulBuilds = counter.getSuccessfulBuilds(); 110 Map<String, Integer> failedBuilds = counter.getFailedBuilds(); 111 112 Set<String> members = counter.getMembers(); 113 for (String member : members) { 114 MemberData memberData = new MemberData(); 115 memberData.setMemberUri(sensorBaseHost + "users/" + member); 116 Integer failures = failedBuilds.get(member); 117 if (failures == null) { 118 // no mapping for member in failed builds, means user had no failed builds 119 failures = 0; 120 } 121 memberData.setFailure(failures); 122 123 Integer successes = successfulBuilds.get(member); 124 if (successes == null) { 125 // no mapping for member in successful builds, means user had no successful builds 126 successes = 0; 127 } 128 memberData.setSuccess(successes); 129 130 build.getMemberData().add(memberData); 131 } 132 133 build.setOwner(uriUser); 134 build.setProject(project); 135 build.setStartTime(startTime); 136 if (this.type == null) { 137 build.setType("*"); 138 } 139 else { 140 build.setType(this.type); 141 } 142 143 String xmlData = this.makeBuild(build); 144 if (!Tstamp.isTodayOrLater(startTime)) { 145 this.server.getFrontSideCache().put(uriUser, project, uriString, xmlData); 146 } 147 logRequest("Build", this.type); 148 return super.getStringRepresentation(xmlData); 149 } 150 catch (Exception e) { 151 setStatusError("Error creating Build DPD", e); 152 return null; 153 } 154 } 155 return null; 156 } 157 158 /** 159 * Determines if the sensor data matches the specified Type. 160 * 161 * @param data The sensor data to check for a valid Type. 162 * @return Returns true if the data should be processed or false if it does not match the 163 * Type. 164 */ 165 private boolean isValidData(SensorData data) { 166 // no type, or Type=* are wildcards for all data 167 if (this.type == null || "*".equals(this.type)) { 168 return true; 169 } 170 171 String typeValue = getPropertyValue(data, "Type"); 172 return this.type.equals(typeValue); 173 } 174 175 /** 176 * Gets the value for the given property name from the <code>Properties</code> object 177 * contained in the given sensor data instance. 178 * 179 * @param data The sensor data instance to get the property from. 180 * @param propertyName The name of the property to get the value for. 181 * @return Returns the value of the property or null if no matching property was found. 182 */ 183 private String getPropertyValue(SensorData data, String propertyName) { 184 Properties properties = data.getProperties(); 185 if (properties != null) { 186 List<Property> propertyList = properties.getProperty(); 187 for (Property property : propertyList) { 188 if (property.getKey().equals(propertyName)) { 189 return property.getValue(); 190 } 191 } 192 } 193 return null; 194 } 195 196 /** 197 * Returns the passed SensorData instance as a String encoding of its XML representation. 198 * 199 * @param data The SensorData instance. 200 * @return The XML String representation. 201 * @throws Exception If problems occur during translation. 202 */ 203 private String makeBuild(BuildDailyProjectData data) throws Exception { 204 JAXBContext buildJAXB = (JAXBContext) this.server.getContext().getAttributes().get( 205 "BuildJAXB"); 206 Marshaller marshaller = buildJAXB.createMarshaller(); 207 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 208 dbf.setNamespaceAware(true); 209 DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); 210 Document doc = documentBuilder.newDocument(); 211 marshaller.marshal(data, doc); 212 DOMSource domSource = new DOMSource(doc); 213 StringWriter writer = new StringWriter(); 214 StreamResult result = new StreamResult(writer); 215 TransformerFactory tf = TransformerFactory.newInstance(); 216 Transformer transformer = tf.newTransformer(); 217 transformer.transform(domSource, result); 218 return writer.toString(); 219 } 220 }