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    }