23.2. Anatomy of a Telemetry Reduction Function

23.2.1. Overview

Telemetry Reduction Functions aggregate low level software product and process data, and returns a collection of telemetry streams (a.k.a. a streams object). All Telemetry Reduction Functions must implement the TelemetryReducer interface. There is only one method in the interface, whose signature is below:

TelemetryStreamCollection compute(Project project, Interval interval, String[] parameters) 
  throws TelemetryReducerException;        

It is the responsibility of each Reduction Function implementation to determine what parameter or parameters are accepted. When the end user invokes a telemetry Reduction Function using the telemetry language, parameter values are specified in a comma separated list. The telemetry language parser unpacks this list, and passes it to the target Reduction Function in the String array. In the case where the Reduction Function invocation does not require any parameters, either an empty array or a null value is passed to the target Reduction Function.

The return value of a Reduction Function invocation is a telemetry streams object, and the Java class that models this object is TelemetryStreamCollection.

Defining a new Telemetry Reduction Function involves three basic activities: (1) defining a Java class that implements the TelemetryReducer interface, (2) defining a *.telemetry.def.xml file that will be processed by the Hackystat server at run time to dynamically discover this class, and (3) updating your module's local.build.xml file to include an Ant macro that will copy the *.telemetry.def.xml file to the appropriate place during the system build process. Each of these steps will be described in the following sections.

In the following sections, we will use SimpleSdtReducer telemetry reduction function as an example to illustrate how everything is put together. It is based on SimpleSdt. The code is provided in a module called hackyDoc_SimpleTelemetryFunction, which depends on both hackyCore_Telemetry and hackyDoc_SimpleSdt. hackyDoc_SimpleTelemetryFunction can be download here. hackyDoc_SimpleSdt can be download here. hackyCore_Telemetry can be checked out from the Hackystat source code repository.

23.2.2. An example TelemetryReducer class

Apart from implementing the TelemetryReducer interface, probably the most important thing you have to remember is that the Hackystat telemetry framework instantiates one and only one instance for each declared Telemetry Reduction Function. Since multiple requests for telemetry analysis might be received simultaneously, you must ensure that your implementation is thread-safe. Though it is not mandatory that the implementation must be stateless, under most cases, you want your reduction function to return the same value for the same inputs.

Example 23.1, “SimpleSdtReducer Implementation” illustrates the implementation of a reduction function.

Example 23.1. SimpleSdtReducer Implementation

/**
 * A telemetry reduction function that computes the total elapsed time from SimpleSdt.
 * This is to illustrate how to write a reduction function.
 * Don't ask me what SimpleSdt is, and don't ask me what elapsed time stands for.
 *
 * @author (Cedric) Qin Zhang
 * @version $Id$
 */
public class SimpleSdtReducer implements TelemetryReducer {

  /**
   * Performs the computation.
   *
   * @param project The project.
   * @param interval The interval over which the computation should be performed.
   * @param options Optional parameters. Not used.
   *
   * @return Telemetry stream collection.
   * @throws TelemetryReducerException If there is any error.
   */
  public TelemetryStreamCollection compute(Project project, Interval interval, String[] options)
      throws TelemetryReducerException {

    try {
      TelemetryStream telemetryStream = new TelemetryStream(null);
      
      //Use a utility class 'IntervalUtility' to break interval into periods.
      //One period corresponds to one data point on the telemetry stream.
      List periods = IntervalUtility.getPeriods(interval);
      for (Iterator i = periods.iterator(); i.hasNext(); ) {
        IntervalUtility.Period period = (IntervalUtility.Period) i.next();
        
        //Compute total elapsed time for the current period.
        //We make use of the DailyProjectSimpleSdt abstraction layer, instead of the raw data.
        //We simply add elapsed time for each data to get the total elapsed time for this period.
        int totalElapsedTimeInThisPeriod = 0;
        for (Day theDay = period.getStartDay(); theDay.compareTo(period.getEndDay()) <= 0; 
             theDay = theDay.inc(1)) {
          //Note that DailyProjectSimpleSdt documentation says that if there is no data, then
          //the return value of getTotalElapsedTime() is meaningless.
          //For simplicity, we omit the check here. But in real implementation, you should consider
          //all possibilities.
          totalElapsedTimeInThisPeriod 
              += DailyProjectSimpleSdt.getInstance(project, theDay).getTotalElapsedTime();
        }
        
        //Construct a TelemetryDataPoint, which contains the current period and its value.
        TelemetryDataPoint dp = new TelemetryDataPoint(
            period.getTimePeriod(), new Integer (totalElapsedTimeInThisPeriod));
        
        //Add the data point to the telemetry stream.
        telemetryStream.addDataPoint(dp);
      }
      
      //Wrap the telemetry stream in a telemetry stream collection, and return it.
      TelemetryStreamCollection streams = new TelemetryStreamCollection(null, project, interval);
      streams.add(telemetryStream);
      return streams;
      
    } catch (Exception e) {
      throw new TelemetryReducerException(e.getMessage());
    }
  }
}

23.2.3. An example *.telemetry.def.xml file

After implementing the reduction function, the next step is to provide an extension point declaration file so that the Hackystat telemetry infrastructure code knows the existence of the reduction function. An example declaration file is provided in Example 23.2, “Extension Declaration for SimpleSdtReducer”.

Example 23.2. Extension Declaration for SimpleSdtReducer

<TelemetryReducers>
  <Reducer name="SimpleSdt" 
           class="org.hackystat.doc.simpletelemetryfunction.SimpleSdtReducer"
           reducerDescription="Computes a single telemetry stream representing 
                               total elapsed time from SimpleSdt instances."
           optionDescription="This reduction function ignores any parameter values you might supply." 
  />
</TelemetryReducers>

Table 23.1, “Telemetry Reduction Function Declaration” shows the attributes in the extension point XML file.

Table 23.1. Telemetry Reduction Function Declaration

AttributeDescription
nameThe name of the telemetry Reduction Function. This is the name used in the telemetry language to invoke the Reduction Function.
classThe fully qualified Java class name. This is required by the Hackystat telemetry implementation to instantiate the Reduction Function through Java reflection mechanism.
reducerDescriptionA description of the Reduction Function. This information is shown to the end users, so that they know what the Reduction Function does.
optionDescriptionA description of the parameters the Reduction Function takes. This information is shown to the end users, so that they know how to invoke the Reduction Function using the telemetry language.

23.2.4. An example of an updated local.build.xml file

To copy the telemetry function declaration file to its final destination, you make use of a macro called hackyCore_Telemetry.installXmlDefinitions in your local.build.xml file. Example 23.3, “Segment of local.build.xml file” shows the related segment.

Example 23.3. Segment of local.build.xml file

<target name="hackyDoc_SimpleTelemetryFunction.install.pre-sensorshell"
        if="hackyDoc_SimpleTelemetryFunction.available" >
  ...
  <hackyCore_Telemetry.installXmlDefinitions module.src.dir
         ="${hackyDoc_SimpleTelemetryFunction.src.dir}" />
</target>