001    package org.hackystat.telemetry.analyzer.reducer;
002    
003    import java.io.InputStream;
004    import java.util.Collection;
005    import java.util.Map;
006    import java.util.TreeMap;
007    import java.util.logging.Logger;
008    
009    import javax.xml.bind.JAXBContext;
010    import javax.xml.bind.Unmarshaller;
011    
012    import org.hackystat.telemetry.analyzer.model.TelemetryStreamCollection;
013    import org.hackystat.telemetry.analyzer.reducer.jaxb.ReducerDefinition;
014    import org.hackystat.telemetry.analyzer.reducer.jaxb.ReducerDefinitions;
015    import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
016    import org.hackystat.sensorbase.resource.projects.jaxb.Project;
017    import org.hackystat.utilities.logger.HackystatLogger;
018    import org.hackystat.utilities.stacktrace.StackTrace;
019    import org.hackystat.utilities.time.interval.Interval;
020    
021    /**
022     * Provides a singleton manager for telemetry reducers. 
023     * 
024     * @author Qin ZHANG, Philip Johnson
025     */
026    public class TelemetryReducerManager {
027    
028      /** The singleton. */
029      private static TelemetryReducerManager theInstance = new TelemetryReducerManager();
030      /** Maps reducer names to a data structure with information about them. */ 
031      private Map<String, TelemetryReducerInfo> reducerMap = 
032        new TreeMap<String, TelemetryReducerInfo>();
033      
034      /**
035       * Gets the singleton instance of this class.
036       * 
037       * @return An instance of this class.
038       */
039      public static TelemetryReducerManager getInstance() {
040        return theInstance;
041      }
042    
043      /**
044       * Finds and defines built-in telemetry reducer functions. 
045       * If the reducer.definitions.xml file cannot be found, or the file specifies
046       * a class that cannot be instantiated, then logging messages will the displayed but no
047       * errors will be thrown.
048       */
049      private TelemetryReducerManager() {
050        Logger logger = HackystatLogger.getLogger("org.hackystat.telemetry");
051        ReducerDefinitions definitions = null;    
052        try {
053          logger.info("Loading built-in telemetry reduction function definitions.");
054          //InputStream defStream = getClass().getResourceAsStream("impl/reducer.definitions.xml");
055          InputStream defStream = 
056            TelemetryReducerManager.class.getResourceAsStream("impl/reducer.definitions.xml");
057          JAXBContext jaxbContext = JAXBContext
058          .newInstance(org.hackystat.telemetry.analyzer.reducer.jaxb.ObjectFactory.class);
059          Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
060          definitions = (ReducerDefinitions) unmarshaller.unmarshal(defStream);
061        } 
062        catch (Exception e) {
063          logger.severe("Could not find built-in telemetry reduction function definitions! " 
064              + StackTrace.toString(e));
065          return;
066        }
067    
068        for (ReducerDefinition definition : definitions.getReducerDefinition()) {
069          String name = definition.getName();
070          try {
071            logger.info("Defining built-in telemetry reduction function " + name);
072            Class<?> clazz = Class.forName(definition.getClassName());
073            TelemetryReducer reducer = (TelemetryReducer) clazz.newInstance();
074            TelemetryReducerInfo reducerInfo =  new TelemetryReducerInfo(reducer, definition);
075            this.reducerMap.put(name, reducerInfo);
076          }
077          catch (Exception classEx) {
078            logger.severe("Unable to define " 
079                + definition.getClassName() + ". Entry ignored. " + classEx.getMessage());
080            continue;
081          }
082        }
083      }
084    
085     
086      /**
087       * Invokes the telemetry reducer to perform computations and generate a TelemetryStreamCollection.
088       * 
089       * @param reducerName The name of the reducer to be invoked.
090       * @param project The project which defines the scope of metrics to be used in the computation.
091       * @param dpdClient The DPD Client.
092       * @param interval The time interval.
093       * @param parameters Parameters passed to reducer implementation. In case a reducer does not
094       *        need any parameter, either null or an empty array may be passed.
095       * @return The resulting instance of <code>TelemetryStreamCollection</code>. 
096       * @throws TelemetryReducerException If anything is wrong.
097       */
098      public TelemetryStreamCollection compute(String reducerName, DailyProjectDataClient dpdClient,
099          Project project, Interval interval, String[] parameters) throws TelemetryReducerException {
100        TelemetryReducerInfo reducerInfo = this.reducerMap.get(reducerName);
101        if (reducerInfo == null) {
102          throw new TelemetryReducerException("Telemetry reducer " + reducerName + " not defined.");
103        }
104        return reducerInfo.getReducer().compute(project, dpdClient, interval, parameters);
105      }
106    
107      /**
108       * Determines whether a particular telemetry reducer is defined.
109       * 
110       * @param reducerName Telemetry reducer name.
111       * @return True if the specified telemetry reducer is defined.
112       */
113      public boolean isReducer(String reducerName) {
114        return this.reducerMap.containsKey(reducerName);
115      }
116    
117      /**
118       * Gets telemetry reducer information by name.
119       * 
120       * @param reducerName The name of the reducer.
121       * @return The telemetry reducer information, or null if the reducer is not defined.
122       */
123      public TelemetryReducerInfo getReducerInfo(String reducerName) {
124        return this.reducerMap.get(reducerName);
125      }
126    
127      /**
128       * Returns information about all defined TelemetryReducers. 
129       * 
130       * @return A collection of <code>TelemetryReducerInfo</code> instances.
131       */
132      public Collection<TelemetryReducerInfo> getAllReducerInfo() {
133        return this.reducerMap.values();
134      }
135    }