001 package org.hackystat.sensor.xmldata.option; 002 003 import java.io.File; 004 import java.util.HashMap; 005 import java.util.List; 006 import java.util.Map; 007 008 import javax.xml.bind.JAXBException; 009 import javax.xml.bind.Unmarshaller; 010 import javax.xml.datatype.XMLGregorianCalendar; 011 import javax.xml.namespace.QName; 012 013 import org.hackystat.sensor.xmldata.XmlDataController; 014 import org.hackystat.sensor.xmldata.jaxb.Entries; 015 import org.hackystat.sensor.xmldata.jaxb.Entry; 016 import org.hackystat.sensor.xmldata.jaxb.ObjectFactory; 017 import org.hackystat.sensor.xmldata.jaxb.XmlData; 018 import org.hackystat.sensorshell.SensorShellException; 019 import org.hackystat.sensorshell.SensorShellProperties; 020 import org.hackystat.sensorshell.Shell; 021 import org.hackystat.utilities.tstamp.Tstamp; 022 import org.hackystat.utilities.tstamp.TstampSet; 023 import org.xml.sax.SAXException; 024 025 /** 026 * The option used to send generic sensor data, via the sensorshell, to the 027 * sensorbase. This option accepts a list of files that contain the generic 028 * sensor information. 029 * @author aito 030 * 031 */ 032 public class FileOption extends AbstractOption { 033 /** The name of this option, which is "-file". */ 034 public static final String OPTION_NAME = "-file"; 035 036 /** 037 * Creates this option with the specified controller and parameters. 038 * @param controller the specified controller. 039 * @param parameters the specified parameters. 040 */ 041 public FileOption(XmlDataController controller, List<String> parameters) { 042 super(controller, OPTION_NAME, parameters); 043 } 044 045 /** 046 * Returns true if the the list of parameters contains 1 or more files that 047 * exist. 048 * @return true if this option's parameters are valid. 049 */ 050 @Override 051 public boolean isValid() { 052 if (this.getParameters().size() == 0) { 053 String msg = "The number of parameters must include at least 1 file. " 054 + "Ex: -file foo.xml foo2.xml"; 055 this.getController().fireMessage(msg); 056 return false; 057 } 058 059 for (String parameter : this.getParameters()) { 060 File file = new File(parameter); 061 if (!file.exists()) { 062 String msg = "The file '" + file + "' does not exist."; 063 this.getController().fireMessage(msg); 064 return false; 065 } 066 } 067 return true; 068 } 069 070 /** 071 * Executes this option by grabbing all information stored in the specified 072 * files, and sending them to the sensorbase. 073 */ 074 @Override 075 public void execute() { 076 try { 077 // First, lets get the correct shell instance. 078 Shell shell = OptionUtil.createShell(new SensorShellProperties(), this.getController()); 079 080 // Then, send data from each file. 081 XMLGregorianCalendar runtime = Tstamp.makeTimestamp(); 082 int entriesAdded = 0; 083 for (String filePath : this.getParameters()) { 084 this.getController().fireVerboseMessage("Sending data from: " + filePath); 085 Unmarshaller unmarshaller = OptionUtil.createUnmarshaller(ObjectFactory.class, 086 "xmldata.xsd"); 087 XmlData xmlData = (XmlData) unmarshaller.unmarshal(new File(filePath)); 088 Entries entries = xmlData.getEntries(); 089 090 // Only send data if the SDT is set or all entries have SDT attributes. 091 TstampSet tstampSet = new TstampSet(); 092 Object sdtName = this.getController().getOptionObject(Options.SDT); 093 if (sdtName != null || this.hasSdtAttributes(entries.getEntry())) { 094 for (Entry entry : entries.getEntry()) { 095 // Then, lets set the "required" attributes. 096 Map<String, String> keyValMap = new HashMap<String, String>(); 097 keyValMap.put("Tool", entry.getTool()); 098 keyValMap.put("Resource", this.getResource(entry)); 099 keyValMap.put("SensorDataType", (String) sdtName); 100 keyValMap.put("Timestamp", OptionUtil.getCurrentTimestamp(true, tstampSet) 101 .toString()); 102 103 // If the SetRuntimeOption is set, use the same runtime. 104 if (Boolean.TRUE.equals(this.getController().getOptionObject(Options.SET_RUNTIME))) { 105 keyValMap.put("Runtime", runtime.toString()); 106 } 107 108 // Next, add the optional attributes. 109 Map<QName, String> map = entry.getOtherAttributes(); 110 for (Map.Entry<QName, String> attributeEntry : map.entrySet()) { 111 String entryName = attributeEntry.getKey().toString(); 112 String entryValue = attributeEntry.getValue(); 113 114 // If entries contain tstamps, override the current tstamp. 115 if ("Timestamp".equals(entryName)) { 116 long timestamp = OptionUtil.getTimestampInMillis(entryValue); 117 Boolean isUnique = (Boolean) this.getController().getOptionObject( 118 Options.UNIQUE_TSTAMP); 119 entryValue = OptionUtil.massageTimestamp(isUnique, tstampSet, timestamp) 120 .toString(); 121 } 122 keyValMap.put(entryName, entryValue); 123 } 124 // Finally, add the mapping and send the data. 125 this.getController().fireVerboseMessage(OptionUtil.getMapVerboseString(keyValMap)); 126 shell.add(keyValMap); 127 entriesAdded++; 128 } 129 } 130 else { 131 String msg = "The -sdt flag must be specified for all entries or each " 132 + "xml entry must have the 'SensorDataType' attribute."; 133 throw new Exception(msg); 134 } 135 } 136 137 // Fires the send message and quits the sensorshell. 138 OptionUtil.fireSendMessage(this.getController(), shell, entriesAdded); 139 shell.quit(); 140 } 141 catch (JAXBException e) { 142 String msg = "There was a problem unmarshalling the data. File(s) " 143 + "may not conform to the xmldata schema."; 144 this.getController().fireMessage(msg, e.toString()); 145 } 146 catch (SAXException e) { 147 String msg = "The specified file(s) could not be parsed."; 148 this.getController().fireMessage(msg, e.toString()); 149 } 150 catch (SensorShellException e) { 151 String msg = "The sensorshell.properties file in your userdir/.hackystat " 152 + "directory is invalid or does not exist."; 153 this.getController().fireMessage(msg); 154 } 155 catch (Exception e) { 156 String msg = "The specified file(s) failed to load."; 157 this.getController().fireMessage(msg, e.toString()); 158 } 159 } 160 161 /** 162 * Returns the Resource value of the specified entry. This method is helpful 163 * when an option is specified that alters the resource associated with the 164 * Resource key. 165 * @param entry the entry whose Resource value is determined. 166 * @return the resource string value. 167 */ 168 private String getResource(Entry entry) { 169 if (this.getController().getOptionObject(Options.RESOURCE) == null) { 170 return entry.getResource(); 171 } 172 return this.getController().getOptionObject(Options.RESOURCE).toString(); 173 } 174 175 /** 176 * Returns true if the list of JAXB Entry object's each contain the sensor 177 * data type attribute. 178 * @param entries the list of entries to search. 179 * @return true if each entry has the sdt attribute, false if not. 180 */ 181 private boolean hasSdtAttributes(List<Entry> entries) { 182 for (Entry entry : entries) { 183 if (!this.hasSdtInAttributes(entry.getOtherAttributes())) { 184 return false; 185 } 186 } 187 return true; 188 } 189 190 /** 191 * Returns true if the specified map contains the sensor data type attribute. 192 * If the mapping does not have the sdt attribute, false is returned. 193 * @param attributeMap the map to search. 194 * @return true if the map has the sdt attribute, false if not. 195 */ 196 private boolean hasSdtInAttributes(Map<QName, String> attributeMap) { 197 for (QName name : attributeMap.keySet()) { 198 if ("SensorDataType".equals(name.toString())) { 199 return true; 200 } 201 } 202 return false; 203 } 204 }