001 package org.hackystat.telemetry.analyzer.configuration; 002 003 import java.io.File; 004 import java.io.FileInputStream; 005 import java.io.InputStream; 006 import java.util.Collection; 007 import java.util.HashMap; 008 import java.util.List; 009 import java.util.Map; 010 import java.util.logging.Logger; 011 012 import javax.xml.bind.JAXBContext; 013 import javax.xml.bind.Unmarshaller; 014 015 import org.hackystat.sensorbase.resource.users.jaxb.User; 016 import org.hackystat.telemetry.analyzer.configuration.jaxb.TelemetryDefinition; 017 import org.hackystat.telemetry.analyzer.configuration.jaxb.TelemetryDefinitions; 018 import org.hackystat.utilities.logger.HackystatLogger; 019 import org.hackystat.utilities.stacktrace.StackTrace; 020 021 /** 022 * Implements a persistent Telemetry definition manager. 023 * Reads in Telemetry definitions from the telemetrydefinitions.xml file. 024 * <p> 025 * All public methods in the class are thread-safe. 026 * <p> 027 * In Version 7, this class also was responsible for persisting dynamically defined definitions. 028 * It does not currently support dynamic persistent definition in Version 8. 029 * 030 * @author (Cedric) Qin Zhang 031 */ 032 class PersistentTelemetryDefinitionManager extends TelemetryDefinitionManager { 033 034 private Map<TelemetryDefinitionType, TelemetryDefinitionInfoRepository> defInfoRepositoryMap = 035 new HashMap<TelemetryDefinitionType, TelemetryDefinitionInfoRepository>(); 036 037 private Logger logger; 038 039 private TelemetryDefinitions definitions; 040 041 /** 042 * Creates a new Persistent Telemetry Definition Manager, reading in the pre-defined definitions 043 * from the telemetrydefinitions.xml file. 044 * @param defDirString A string defining the directory where additional telemetry definitions 045 * can be found, or null if not available. 046 */ 047 PersistentTelemetryDefinitionManager(String defDirString) { 048 this.defInfoRepositoryMap.put(TelemetryDefinitionType.STREAMS, 049 new TelemetryDefinitionInfoRepository()); 050 this.defInfoRepositoryMap.put(TelemetryDefinitionType.CHART, 051 new TelemetryDefinitionInfoRepository()); 052 this.defInfoRepositoryMap.put(TelemetryDefinitionType.YAXIS, 053 new TelemetryDefinitionInfoRepository()); 054 this.defInfoRepositoryMap.put(TelemetryDefinitionType.REPORT, 055 new TelemetryDefinitionInfoRepository()); 056 057 this.definitions = new TelemetryDefinitions(); 058 059 this.logger = HackystatLogger.getLogger("org.hackystat.telemetry"); 060 // Read in the telemetry.definitions.xml file from the jar. 061 this.logger.info("Loading built-in telemetry chart/report/stream/y-axis definitions."); 062 InputStream defStream = getClass().getResourceAsStream("telemetry.definitions.xml"); 063 TelemetryDefinitions builtin = getDefinitions(defStream); 064 if (builtin != null) { 065 updateRepository(builtin); 066 } 067 068 if (defDirString == null || !((new File(defDirString)).exists())) { 069 this.logger.info("Telemetry directory not available: " + defDirString); 070 } 071 else { 072 // Read in the any telemetry definitions from the definitions/ directory. 073 File defDir = new File(defDirString); 074 File[] xmlFiles = defDir.listFiles(new ExtensionFileFilter(".xml")); 075 FileInputStream fileStream = null; 076 for (int i = 0; i < xmlFiles.length; i++) { 077 try { 078 // Get the TelemetryDefinitions instance from the file. 079 this.logger.info("Reading telemetry definitions from: " + xmlFiles[i].getName()); 080 fileStream = new FileInputStream(xmlFiles[i]); 081 TelemetryDefinitions defs = getDefinitions(fileStream); 082 if (defs != null) { 083 updateRepository(defs); 084 } 085 fileStream.close(); 086 } 087 catch (Exception e) { 088 this.logger.info("Error reading definitions from: " + xmlFiles[i] + " " + e); 089 try { 090 fileStream.close(); 091 } 092 catch (Exception f) { 093 this.logger.info("Failed to close: " + fileStream.toString() + " " + e); 094 } 095 } 096 } 097 } 098 } 099 100 /** 101 * Given a TelemetryDefinitions instance, updates the internal data structures with this info. 102 * @param definitions The TelemetryDefinitions instance. 103 */ 104 private void updateRepository(TelemetryDefinitions definitions) { 105 // Now update the repository maps. 106 for (TelemetryDefinition definition : definitions.getTelemetryDefinition()) { 107 String defType = definition.getDefinitionType(); 108 String definitionString = definition.getSourceCode(); 109 ShareScope globalScope = ShareScope.getGlobalShareScope(); 110 User user = new User(); 111 user.setEmail("TelemetryDefinitions@hackystat.org"); 112 // A bit of stuff to make the logging message pretty. 113 String oneLineDef = definition.getSourceCode().replace("\n", ""); 114 int equalsPos = oneLineDef.indexOf('='); 115 this.logger.info(" " + oneLineDef.substring(0, equalsPos)); 116 117 try { 118 if (TelemetryDefinitionType.STREAMS.toString().equalsIgnoreCase(defType)) { 119 this.add(new TelemetryStreamsDefinitionInfo(definitionString, user, globalScope)); 120 this.definitions.getTelemetryDefinition().add(definition); 121 } 122 else if (TelemetryDefinitionType.CHART.toString().equalsIgnoreCase(defType)) { 123 this.add(new TelemetryChartDefinitionInfo(definitionString, user, globalScope)); 124 this.definitions.getTelemetryDefinition().add(definition); 125 } 126 else if (TelemetryDefinitionType.YAXIS.toString().equalsIgnoreCase(defType)) { 127 this.add(new TelemetryChartYAxisDefinitionInfo(definitionString, user, globalScope)); 128 this.definitions.getTelemetryDefinition().add(definition); 129 } 130 else if (TelemetryDefinitionType.REPORT.toString().equalsIgnoreCase(defType)) { 131 this.add(new TelemetryReportDefinitionInfo(definitionString, user, globalScope)); 132 this.definitions.getTelemetryDefinition().add(definition); 133 } 134 else { 135 this.logger.warning("Unknown definition type: " + defType); 136 } 137 } 138 catch (Exception e) { 139 this.logger.warning("Error defining a telemetry construct: " + StackTrace.toString(e)); 140 } 141 } 142 } 143 144 /** 145 * Returns a TelemetryDefinitions object constructed from defStream, or null if unsuccessful. 146 * @param defStream The input stream containing a TelemetryDefinitions object in XML format. 147 * @return The TelemetryDefinitions object. 148 */ 149 private TelemetryDefinitions getDefinitions (InputStream defStream) { 150 // Read in the definitions file. 151 try { 152 JAXBContext jaxbContext = JAXBContext 153 .newInstance(org.hackystat.telemetry.analyzer.configuration.jaxb.ObjectFactory.class); 154 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 155 return (TelemetryDefinitions) unmarshaller.unmarshal(defStream); 156 } 157 catch (Exception e) { 158 this.logger.severe("Failed to get TelemetryDefinitions from stream" + StackTrace.toString(e)); 159 } 160 return null; 161 } 162 163 /** 164 * Gets the telemetry definition information by name. 165 * 166 * @param owner The owner under which to find the telemetry definition object. 167 * @param name The name of the telemetry definition. 168 * @param includeShared If true, then those telemetry definitions owned by other users, 169 * but is shared will also be returned. 170 * @param type The definition type. 171 * 172 * @return The object if found, or null. 173 */ 174 @Override 175 public synchronized TelemetryDefinitionInfo get( 176 User owner, String name, boolean includeShared, TelemetryDefinitionType type) { 177 TelemetryDefinitionInfoRepository repository 178 = this.defInfoRepositoryMap.get(type); 179 if (repository == null) { 180 throw new RuntimeException("Unknow telemetry definition type " + type); 181 } 182 else { 183 return repository.find(owner, name, includeShared); 184 } 185 } 186 187 /** 188 * Gets all telemetry definitions that this user has access to. 189 * 190 * @param owner The owner of the telemetry definitions returned. 191 * @param includeShared If true, then those telemetry definitions owned by 192 * other users, but is shared will also be returned. 193 * @param type The definition type. 194 * 195 * @return A collection of found objects. 196 */ 197 @Override 198 public synchronized Collection<TelemetryDefinitionInfo> getAll(User owner, boolean includeShared, 199 TelemetryDefinitionType type) { 200 TelemetryDefinitionInfoRepository repository 201 = this.defInfoRepositoryMap.get(type); 202 if (repository == null) { 203 throw new RuntimeException("Unknow telemetry definition type " + type); 204 } 205 else { 206 return repository.findAll(owner, includeShared); 207 } 208 } 209 210 /** 211 * Adds information about a definition. 212 * 213 * @param defInfo Information about the definition to be added. 214 * 215 * @throws TelemetryConfigurationException If there is duplicated definition. 216 */ 217 @Override 218 public final synchronized void add(TelemetryDefinitionInfo defInfo) 219 throws TelemetryConfigurationException { 220 TelemetryDefinitionInfoRepository repository 221 = this.defInfoRepositoryMap.get(defInfo.getType()); 222 if (repository == null) { 223 throw new RuntimeException("Unknow telemetry definition: " + defInfo.getClass().getName()); 224 } 225 else { 226 //check global namespace constraint. 227 if (this.isDefinition(defInfo.getName())) { 228 throw new TelemetryConfigurationException( 229 "All telemetry definitions (chart, report) share a global namespace. The name '" 230 + defInfo.getName() + "' is already used by either you or other user."); 231 } 232 //add 233 repository.add(defInfo); 234 //this.write(defInfo.getOwner()); 235 } 236 } 237 238 /** 239 * Checks whether the name has already been used by any type of telemetry definition. 240 * 241 * @param name The name. 242 * @return True if the name has already been used. 243 */ 244 final synchronized boolean isDefinition(String name) { 245 for (TelemetryDefinitionInfoRepository rep : this.defInfoRepositoryMap.values()) { 246 if (rep.exists(name)) { 247 return true; 248 } 249 } 250 return false; 251 } 252 253 /** 254 * Deletes a telemetry object definition. Does nothing if the definition does not exist. 255 * 256 * @param owner The owner of the definition. 257 * @param name The name of the definition. 258 * @param type The definition type. 259 */ 260 @Override 261 public synchronized void remove(User owner, String name, TelemetryDefinitionType type) { 262 TelemetryDefinitionInfoRepository repository 263 = this.defInfoRepositoryMap.get(type); 264 if (repository == null) { 265 throw new RuntimeException("Unknow telemetry definition type " + type); 266 } 267 else { 268 repository.remove(owner, name); 269 //this.write(owner); 270 } 271 } 272 273 /** 274 * Returns the list of telemetry definitions. 275 * @return The list of definitions. 276 */ 277 @Override 278 public List<TelemetryDefinition> getDefinitions() { 279 return this.definitions.getTelemetryDefinition(); 280 } 281 }