17.4. The SimpleSensor: An example Java-based sensor tool

While the XSLT/XmlData sensor approach described above is generally the quickest way to implement a new sensor, in certain situations, such as interactive tools like editors, it is more appropriate to build a "plugin" to the tool. The next several sections illustrate the basic steps and code required to implement a sensor called the "SimpleSensor". While this sensor is actually a stand-alone tool and not a plug-in, it provides a good example of the tool-independent characteristics of Hackystat sensors.

Implementing a trivial Hackystat sensor is, as one would hope, trivial. Example 17.10, “The SimpleSensor class” shows the initial implementation of the SimpleSensor sensor.

Example 17.10. The SimpleSensor class

 
import java.util.Date;
import org.hackystat.core.kernel.admin.SensorProperties;
import org.hackystat.core.kernel.shell.SensorShell;

public class SimpleSensor {

  /**
   * The "Hello World" sensor.  Sends a DevEvent when invoked from command line.
   * Accepts one argument, the DevEvent "type" string, which defaults to "Invoked"
   * Uses the current directory as the DevEvent "path" value. 
   * @param args A single string providing the DevEvent "type".
   */
  public static void main(String[] args) {
    String type = (args.length > 0) ? args[0] : "Invoked";
    SensorProperties sensorProperties = new SensorProperties("SimpleSensor");
    SensorShell shell = new SensorShell(sensorProperties, false, "SimpleSensor");
    shell.doCommand(new Date(), "DevEvent", "add", 
        "tool=SimpleSensor", 
        "type=" + type,  
        "path=" + System.getProperty("user.dir"));
    shell.send();
  }
}

The purpose of this sensor is to send a single DevEvent to the server when this code is invoked from the command line. The command line can include one argument, which is the "type" field of the DevEvent. The "path" field is set to the directory in which this sensor was invoked. The sensor requires the sensorshell.jar file to be in the classpath so that it can access the SensorProperties and SensorShell classes, which do the work of sending the data. After compiling the SimpleSensor.java file, the following line will invoke it (assuming that the current directory contains both the SimpleSensor.class and sensorshell.jar files):

$ java -cp .:sensorshell.jar SimpleSensor Run

Figure 17.2, “ List Sensor Data showing the DevEvent data sent by SimpleSensor ” illustrates the result of invoking this command: a single DevEvent sensor data instance is sent to the server.

Figure 17.2.  List Sensor Data showing the DevEvent data sent by SimpleSensor


List Sensor Data showing the DevEvent data sent by SimpleSensor

It's helpful to go through this code line-by-line. Let's begin with the initialization code:

public static void main(String[] args) {
  String type = (args.length > 0) ? args[0] : "Invoked";

As with any public static void main() method, this one takes an array of Strings that contain whatever arguments were passed to this system on the command line. The second line initializes the "type" variable to either the first argument passed on the command line, or if there were no arguments passed, to the string "Invoked".

The next line creates a SensorProperties instance:

SensorProperties sensorProperties = new SensorProperties("SimpleSensor");

As you notice, there is no explicit reference in this SimpleSensor code to the location of the Hackystat host or to the user key associated with this user, yet the data does get sent to a specific user and hackystat host. Host and user key information is kept in a file called "sensor.properties" in the user's .hackystat directory. The purpose of the SensorProperties class is to hide the details of how this information is represented and accessed; as a sensor designer, you simply need to make an instance of this class and that information is now available. This constructor takes a String parameter indicating the name of the tool that is being monitored by the sensor. In this case, we'll just pass "SimpleSensor" as the tool name.

The next line creates a SensorShell instance:

SensorShell shell = new SensorShell(sensorProperties, false, "SimpleSensor");

If you are not familiar with the purpose of the SensorShell, you can learn more about it in Chapter 16, The SensorShell. The SensorShell constructor requires a SensorProperties instance, from which it determines the user key and hackystat host. The second argument, the boolean false, indicates that the sensorshell is not being used interactively. The final argument is a string indicating the tool that is invoking the SensorShell. This string is used to name the log file created by the SensorShell that records its actions and which is stored in the .hackystat/logs directory.

The next lines invoke the doCommand() method of SensorShell:

shell.doCommand(new Date(), "DevEvent", "add", 
    "tool=SimpleSensor", 
    "type=" + type,  
    "path=" + System.getProperty("user.dir"));

This version of the doCommand() method accepts a variable number of arguments. The first argument is a Date instance to be used as the timestamp for this sensor data. In this case, we create a new Date instance which contains the current time. The second argument specifies the Sensor Data Type associated with this sensor data. In this case, we specify the "DevEvent" SDT. The third argument specifies the method from the SDT's wrapper class that will be used to process this sensor data. In this case, we specify the "add" method. (For more details on SDT wrapper classes, see Section 15.6, “Anatomy of SimpleSdt: the "wrapper class"”.) The remaining arguments are strings in the form <key>=<value>, where the key is the SDT attribute and the value is its value. In this case, the DevEvent SDT contains three required fields: tool, type, and path, and we supply three strings containing the values to be used for this sensor data.

The last line simply sends this data to the server:

shell.send();

Note that the SensorShell is designed to buffer the data provided to it and implicitly invoke the send() method at periodic intervals to transmit all accumulated sensor data to the server in one http/soap request. This dramatically lowers the overhead on both client and server by reducing the setup and teardown costs of http/soap transmission. By default, the SensorShell will all accumulated sensor data at intervals of 10 minutes. However, the SensorShell cannot know when the application is exiting and thus know to invoke the send() method one last time. Thus, the sensor designer must ensure that when the sensor (or its corresponding tool) is about to exit, the send() method will be explicitly invoked.

The code in this section works and is useful for illustrating the fundamental activities in every sensor plugin: create a SensorProperties instance (to get access to the Hackystat host and user key); create a SensorShell instance (for sensor data transmission); determine the attributes values for the SDT of interest, and provide that data to the SensorShell. In addition, it shows that as long as you have the sensorshell.jar file, you can create a functioning sensor with no other Hackystat-related infrastructure.

However, this approach to sensor implementation does not utilize several important features possible in the Hackystat Framework:

The next several sections will show how this initial SimpleSensor implementation can be enhanced to incorporate all of these additional capabilities.

[Note]Time for a Spike Solution?

If you are tasked with developing a sensor for a specific tool, you might want to stop right now and start implementing a prototype "spike solution" using the sensorshell.jar file as the Hackystat interface library, as outlined in this section. This gives you the opportunity to focus on the tool-sensor interface with minimal distraction from the other components of the Hackystat Framework. Once you've got your prototype working, then you can add the infrastructure discussed in the following sections to improve its developer-level usability and maintainability.