17.5. The SimpleSensor: Module Definition

17.5.1. Motivation

The previous section illustrated how to build a sensor using just Java code and the sensorshell.jar file. While the Simple Sensor is quite trivial, the point of the previous section was to show that it is possible to build a sensor of any complexity as long as you have access to a sensorshell.jar file. For many organizations, this very decoupled approach to sensor development may be perfectly appropriate. This section begins the explanation of how to build sensors that are more tightly integrated with the Hackystat Framework. It illustrates how to implement a Hackystat "module" containing the Simple Sensor.

There are several advantages to more tightly integrating your sensor with the Hackystat Framework: (a) the sensor can be included in configurations of Hackystat; (b) the sensor code can be easily compiled against the latest version of the sensorshell code base; facilitating early detection of integration errors; (c) various quality assurance tools are easily available, including Checkstyle and JUnit; (d) the sensor's installation and updating can be managed using HackyInstaller; (e) sensor documentation can be made available via the standard Hackystat DocBook documentation system.

The disadvantages of this tight coupling include: (a) you must download the Hackystat source distribution and learn how to build the system locally; (b) you must create and manage a number of different files, including the local.build.xml file; and (c) you must learn the conventions associated with the Hackystat module system for integration of new modules.

17.5.2. Prerequisites

In order to create a new module, you must first be able to download and build a Hackystat server from sources. For details on building Hackystat from sources, please see Chapter 8, Hackystat server installation. You will also want to familiarize yourself with the Hackystat build system, as discussed in Chapter 13, Anatomy of the Hackystat build system.

One you can successfully download and build Hackystat from sources, we highly recommend that you go through the tutorial in Chapter 14, Creating new Hackystat modules. This chapter provides an overview of the Hackystat module system, and if you master that material, you will find this section much easier to assimilate. The remainder of this section assumes you have studied that material, and thus will focus on the aspects of Hackystat modules that are particular to sensors.

17.5.3. The Simple Sensor module directory structure

Example 17.11, “The SimpleSensor module directories and files” shows the directory layout and files for the initial version of the Simple Sensor module.

Example 17.11. The SimpleSensor module directories and files

 
hackyDoc_SimpleSensor/
                      local.build.xml
                      src/org/hackystat/doc/simplesensor/
                                                         SimpleSensor.java
                                                         package.html

The top-level directory, hackyDoc_SimpleSensor, must be located in the same directory that contains all of the other Hackystat modules. By convention, sensors are normally provided in a directory whose name begins "hackySensor_" and their code is located in a subpackage of org.hackystat.sensor. In this case, we use the "hackyDoc_" prefix and the package org.hackystat.doc to show that this module does not implement a "real" sensor, and to illustrate that you can follow different conventions if you wish.

Inside the top-level directory must be a file called "local.build.xml". This is an Ant definition file which will contain Ant targets and properties required for this module to be compiled and built as part of the Hackystat build system.

The top-level directory also contains a src/ subdirectory containing the SimpleSensor implementation. We have refactored the original SimpleSensor code to be located in the org.hackystat.doc.simplesensor package. Also, we included a package.html file documenting the contents of this package, which is required by the Checkstyle QA tool that runs as part of the Hackystat build system.

17.5.4. The local.build.xml file

Example 17.12, “The SimpleSensor local.build.xml file” shows the contents of the local.build.xml file for the SimpleSensor.

Example 17.12. The SimpleSensor local.build.xml file

 
<project name="hackyDoc_SimpleSensor.local" default="hackyDoc_SimpleSensor.default">
  <description> 
  Provides documentation code and packaging for the Sensor Design chapter in the developer guide.
  </description>
   
  <dirname property="hackyDoc_SimpleSensor.local.basedir" file="${ant.file.hackyDoc_SimpleSensor.local}"/>
  <property name="hackyDoc_SimpleSensor.src.dir" location="${hackyDoc_SimpleSensor.local.basedir}/src"/>
  <property name="hackyDoc_SimpleSensor.required.modules" value="hackyCore_Kernel, hackySdt_DevEvent"/> 
  
  <path id="hackyDoc_SimpleSensor.classpath">
    <pathelement location="${install.war.web-inf.classes.dir}" />
  </path>
   
  <target name="hackyDoc_SimpleSensor.compile" if="hackyDoc_SimpleSensor.available"
    description="Compiles changed classes to WEB-INF/classes directory.">
    <hackyCore_Build.javac srcdir="${hackyDoc_SimpleSensor.src.dir}" source="1.5">
       <classpath refid="hackyDoc_SimpleSensor.classpath"/>           
    </hackyCore_Build.javac>
  </target>
  
  <target name="hackyDoc_SimpleSensor.install.post-sensorshell" if="hackyDoc_SimpleSensor.available" 
    depends="hackyDoc_SimpleSensor.createJarFile"/>

  <target name="hackyDoc_SimpleSensor.checkJarsUptodate" 
    description="Defines hackyDoc_SimpleSensor.jars.uptodate if all sensor executables are newer than their sources.">
    <condition property="hackyDoc_SimpleSensor.jars.uptodate">
      <and>
        <uptodate targetfile="${install.war.download.dir}/sensor.simplesensor.jar">
          <srcfiles dir="${install.war.web-inf.classes.dir}" includes="org/hackystat/doc/simplesensor/**" />
          <srcfiles dir="${install.war.web-inf.classes.dir}" includes="org/hackystat/core/**" />
        </uptodate>
      </and>
    </condition>
  </target>

  <target name="hackyDoc_SimpleSensor.createJarFile" 
    depends="hackyDoc_SimpleSensor.checkJarsUptodate" 
    unless="hackyDoc_SimpleSensor.jars.uptodate" 
    description="Creates sensor.simplesensor.jar file and copies it to installation dir.">
    <!-- Create a tmp/ dir containing the classes we need to jar. -->
    <mkdir dir="${hackyDoc_SimpleSensor.tmp.dir}"/>
    <!-- Copy the Simple Sensor and the SensorShell files to the tmp directory. --> 
    <copy todir="${hackyDoc_SimpleSensor.tmp.dir}">
      <fileset dir="${install.war.web-inf.classes.dir}">
        <include name="org/hackystat/doc/simplesensor/**/*.class"/>
      </fileset>
      <fileset dir="${install.build.dir}/sensorshell/src"/>
    </copy>
    <!-- Create the jar file with the appropriate manifest. -->
    <jar jarfile="${install.war.download.dir}/sensor.simplesensor.jar" 
        basedir="${hackyDoc_SimpleSensor.tmp.dir}">
        <manifest>
          <attribute name="Main-Class" value="org.hackystat.doc.simplesensor.SimpleSensor" />
        </manifest>   
    </jar>
   </target>    
  
  <target name="hackyDoc_SimpleSensor.default" description="Does nothing. Required by Ant."/>
</project>

To understand this local.build.xml file, it is important that you understand the basic required targets in a Hackystat module's local.build.xml file and how they work together to build a configuration of the system. The general structure of a local.build.xml file is covered in detail in Section 14.4.1, “local.build.xml”. The following paragraphs provide sensor-specific details about these targets.

The first few lines of the local.build.xml file simply declare the standard project names and so forth:

<project name="hackyDoc_SimpleSensor.local" default="hackyDoc_SimpleSensor.default">
  <description> 
  Provides documentation code and packaging for the Sensor Design chapter in the developer guide.
  </description>
   
  <dirname property="hackyDoc_SimpleSensor.local.basedir" file="${ant.file.hackyDoc_SimpleSensor.local}"/>
  <property name="hackyDoc_SimpleSensor.src.dir" location="${hackyDoc_SimpleSensor.local.basedir}/src"/>

It is important that you replace all occurrences of "hackyDoc_SimpleSensor" with your own module's name in order to avoid name conflicts. The Hackystat build system imports all local.build.xml files into a single Ant namespace during a build.

All sensors send data conforming to one or more sensor data types. SimpleSensor sends data of the type DevEvent, and so it lists hackySdt_DevEvent as a required module in addition to hackyCore_Kernel:

  <property name="hackyDoc_SimpleSensor.required.modules" value="hackyCore_Kernel, hackySdt_DevEvent"/> 

It is crucial that the modules implementing the sensor data types used by your sensor be listed as required modules. If you do not do that, then it is possible that a sensorshell.jar will be created that does not contain the definition of the sensor data type needed by your sensor, resulting in errors when the sensor runs.

Compiling the SimpleSensor module using Ant only requires access to the most recently compiled classes associated with the SensorShell. The sensorshell code (along with other code for the current configuration) is found in the Ant property install.war.web-inf.classes.dir.

  <path id="hackyDoc_SimpleSensor.classpath">
    <pathelement location="${install.war.web-inf.classes.dir}" />
  </path>

Note that certain sensors, such as sensors for Java-based tools like Eclipse or Idea, may also require access to jar files associated with the tool in order to compile. In such cases, you will modify this path variable to include the additional jar files.

Compiling the SimpleSensor is quite straightforward. The target is named according to the Hackystat Framework conventions, uses the "hackyDoc_SimpleSensor.available" property to ensure that this target is only executed when it is defined in hackystat.properties, and uses the Framework preset hackyCore_Build.javac:

  <target name="hackyDoc_SimpleSensor.compile" if="hackyDoc_SimpleSensor.available"
    description="Compiles changed classes to WEB-INF/classes directory.">
    <hackyCore_Build.javac srcdir="${hackyDoc_SimpleSensor.src.dir}" source="1.5">
       <classpath refid="hackyDoc_SimpleSensor.classpath"/>           
    </hackyCore_Build.javac>
  </target>

Note that the code uses Java 5 constructs and so the preset must include 'source="1.5"'.

The next target is the "post-sensorshell" target, which runs after the code for the sensorshell has been gathered from all the modules and compiled to form the sensorshell implementation for this configuration. This target is actually quite small, and simply depends upon a target called hackyDoc_SimpleSensor.createJarFile:

  <target name="hackyDoc_SimpleSensor.install.post-sensorshell" if="hackyDoc_SimpleSensor.available" 
    depends="hackyDoc_SimpleSensor.createJarFile"/>

This is where things get a little interesting. It turns out that creating the sensor jar files is a time-consuming operation during the Hackystat build process, and it also turns out that a significant amount of Hackystat development occurs in "higher" layers of the system that have nothing to do with the sensors or the code the sensors depends upon. Thus, it is helpful to only create sensor jar files when the underlying code has actually changed. The post-sensorshell, createJarFile, and checkJarsUptodate targets are designed to accomplish this.

Let's first look at the checkJarsUptodate target:

  <target name="hackyDoc_SimpleSensor.checkJarsUptodate" 
    description="Defines hackyDoc_SimpleSensor.jars.uptodate if all sensor executables are newer than their sources.">
    <condition property="hackyDoc_SimpleSensor.jars.uptodate">
      <and>
        <uptodate targetfile="${install.war.download.dir}/sensor.simplesensor.jar">
          <srcfiles dir="${install.war.web-inf.classes.dir}" includes="org/hackystat/doc/simplesensor/**" />
          <srcfiles dir="${install.war.web-inf.classes.dir}" includes="org/hackystat/core/**" />
        </uptodate>
      </and>
    </condition>
  </target>

This target sets a property called hackyDoc_SimpleSensor.jars.uptodate in the case that the sensor.simplesensor.jar file is younger than the code that goes into it. In other words, when this property is set, there is no reason to build a new sensor.simplesensor.jar file.

The createJarFile target depends upon checkJarsUptodate, which results in the property hackyDoc_SimpleSensor.jars.uptodate being set if the sensor.simplesensor.jar file does not need to be created. It also uses the "unless" attribute which results in this target not being executed if the property was set. This combination of "depends" and "unless" means that the createJarFile target will only be run when it is necessary to create a sensor.simplesensor.jar file due to the code in that library being out of date.

  <target name="hackyDoc_SimpleSensor.createJarFile" 
    depends="hackyDoc_SimpleSensor.checkJarsUptodate" 
    unless="hackyDoc_SimpleSensor.jars.uptodate" 
    description="Creates sensor.simplesensor.jar file and copies it to installation dir.">
    <!-- Create a tmp/ dir containing the classes we need to jar. -->
    <mkdir dir="${hackyDoc_SimpleSensor.tmp.dir}"/>
    <!-- Copy the Simple Sensor and the SensorShell files to the tmp directory. --> 
    <copy todir="${hackyDoc_SimpleSensor.tmp.dir}">
      <fileset dir="${install.war.web-inf.classes.dir}">
        <include name="org/hackystat/doc/simplesensor/**/*.class"/>
      </fileset>
      <fileset dir="${install.build.dir}/sensorshell/src"/>
    </copy>
    <!-- Create the jar file with the appropriate manifest. -->
    <jar jarfile="${install.war.download.dir}/sensor.simplesensor.jar" 
        basedir="${hackyDoc_SimpleSensor.tmp.dir}">
        <manifest>
          <attribute name="Main-Class" value="org.hackystat.doc.simplesensor.SimpleSensor" />
        </manifest>   
    </jar>
   </target>    

The remainder of the target is pretty straightforward. It creates a tmp directory, copies the required files to that directory, and jars them up into the file sensor.simplesensor.jar. This file must be placed in the directory referenced by the Ant property install.war.download.dir. The manifest provides the location of the class whose main method will be executed when the sensor is invoked using 'java -jar sensor.simplesensor.jar'.

17.5.5. The SimpleSensor.java file

Example 17.13, “The SimpleSensor.java file” shows the SimpleSensor.java file, which is almost unchanged from its initial version.

Example 17.13. The SimpleSensor.java file

 
package org.hackystat.doc.simplesensor;
import java.util.Date;

import org.hackystat.core.kernel.admin.SensorProperties;
import org.hackystat.core.kernel.shell.SensorShell;

/**
 * Provides the SimpleSensor sensor implementation.
 * @author Philip M. Johnson
  */
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 only changes are a class-level JavaDoc and the package declaration.

17.5.6. The package.html file

Example 17.14, “The package.html file” shows the package.html file, which provides a brief bit of documentation. In the Hackystat Framework, all packages must include a package.html file, and this is checked for during the build process by the Checkstyle tool.

Example 17.14. The package.html file

 
<body>
Contains the SimpleSensor sensor, which illustrates how a sensor can be constructed.
</body>

17.5.7. Installing and running the hackyDoc_SimpleSensor module

The previous subsections showed the directory structure layout for the hackyDoc_SimpleSensor module and the contents of the three files in that module. You can quickly create that module locally by simply cutting and pasting the code from this section into the appropriate files in the appropriate locations. (If you don't want to cut and paste, then a later section in this Chapter will provide access to a downloadable zip file containing a more elaborate implementation of this module.)

Once you have created these files, you should go through the standard steps for any new module: (a) build and test a baseline configuration; (b) add the module to the hackystat.build.properties file; (c) run autoconfig to add the module to the Hackystat build system; and (d) build and test the augmented configuration. Let's quickly run through each of these steps.

The baseline configuration includes all of the modules required for this module except for the module itself. In this case, that includes all of the Core modules and the hackySdt_DevEvent module. To create this baseline configuration, edit the hackystat.build.properties file so that only the properties associated with these modules are not commented out:

hackyCore_Build.available=true
hackyCore_Common.available=true
hackyCore_Installer.available=true
hackyCore_Kernel.available=true
hackyCore_Report.available=true
hackyCore_Statistics.available=true
hackyCore_Telemetry.available=true
hackySdt_DevEvent.available=true

Next, bring up Tomcat, and invoke the freshStart and all.junit targets to test that this baseline configuration builds and tests without errors:

c:\svn\hackyCore_Build>ant -q freshStart all.junit
     [echo] (16:14:09) Completed hackyCore_Build.checkModuleAvailability
     [echo] (16:14:10) Completed hackyCore_Build.hotUndeployHackystat
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
     [echo] (16:14:27) Completed all.compile
     [echo] (16:14:45) Completed all.checkstyle
     [echo] (16:14:51) Completed all.install.pre-sensorshell
     [echo] (16:15:01) Completed hackyCore_Build.unjarSensorShellFiles
     [echo] (16:15:04) Completed hackyCore_Build.installSensorShell
     [echo] (16:15:30) Completed all.install.post-sensorshell
     [echo] (16:15:30) Completed hackyCore_Build.deployTestData
     [echo] (16:15:37) Completed hackyCore_Build.hotDeployHackystat
     [echo] (16:15:51) Completed hackyCore_Kernel.junit.
     [echo] (16:15:51) Completed hackyCore_Statistics.junit.
     [echo] (16:16:03) Completed hackyCore_Report.junit.
     [echo] (16:16:10) Completed hackyCore_Common.junit.
     [echo] (16:16:12) Completed hackyCore_Telemetry.junit.
     [echo] (16:16:19) Completed hackyCore_Installer.junit.
     [echo] (16:16:21) Completed hackySdt_DevEvent.junit.
     [echo] (16:16:21) Completed all.junitReport
     [echo] (16:16:21) Completed all.junit

BUILD SUCCESSFUL
Total time: 2 minutes 16 seconds
Sending build result to Hackystat server... Done!
c:\svn\hackyCore_Build>

Now, add the new module. This involves two steps. First, add the module to the hackystat.build.properties file:

hackyCore_Build.available=true
hackyCore_Common.available=true
hackyCore_Installer.available=true
hackyCore_Kernel.available=true
hackyCore_Report.available=true
hackyCore_Statistics.available=true
hackyCore_Telemetry.available=true
hackySdt_DevEvent.available=true
hackyDoc_SimpleSensor.available=true

Second, invoke "ant -f autoconfig.build.xml". This invokes a Hackystat program that parses all of the local.build.xml files and integrates the modules found into the Hackystat Framework build system:

c:\svn\hackyCore_Build>ant -f autoconfig.build.xml
Buildfile: autoconfig.build.xml

run:
    [mkdir] Created dir: C:\svn\hackyCore_Build\build\autoconfig
     [echo] [AutoConfig] Generated modules.build.xml and sample.hackystat.build.properties.

autoconfig.build.default:

BUILD SUCCESSFUL
Total time: 1 second
c:\svn\hackyCore_Build>

Now you should be ready to build and test the hackyDoc_SimpleSensor module as part of the Hackystat Framework build process. To do this, just re-execute the freshStart all.junit targets:

c:\svn\hackyCore_Build>ant -q freshStart all.junit
     [echo] (16:25:54) Completed hackyCore_Build.checkModuleAvailability
     [echo] (16:25:55) Completed hackyCore_Build.hotUndeployHackystat
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
[hackyCore_Build.javac] Note: Some input files use unchecked or unsafe operations.
[hackyCore_Build.javac] Note: Recompile with -Xlint:unchecked for details.
     [echo] (16:26:12) Completed all.compile
     [echo] (16:26:31) Completed all.checkstyle
     [echo] (16:26:37) Completed all.install.pre-sensorshell
     [echo] (16:26:46) Completed hackyCore_Build.unjarSensorShellFiles
     [echo] (16:26:49) Completed hackyCore_Build.installSensorShell
     [echo] (16:27:11) Completed all.install.post-sensorshell
     [echo] (16:27:12) Completed hackyCore_Build.deployTestData
     [echo] (16:27:19) Completed hackyCore_Build.hotDeployHackystat
     [echo] (16:27:32) Completed hackyCore_Kernel.junit.
     [echo] (16:27:33) Completed hackyCore_Statistics.junit.
     [echo] (16:27:45) Completed hackyCore_Report.junit.
     [echo] (16:27:51) Completed hackyCore_Common.junit.
     [echo] (16:27:54) Completed hackyCore_Telemetry.junit.
     [echo] (16:28:00) Completed hackyCore_Installer.junit.
     [echo] (16:28:02) Completed hackySdt_DevEvent.junit.
     [echo] (16:28:03) Completed all.junitReport
     [echo] (16:28:03) Completed all.junit

BUILD SUCCESSFUL
Total time: 2 minutes 12 seconds
Sending build result to Hackystat server... Done!
c:\svn\hackyCore_Build>

Interestingly, there is no difference in the output after this module is added.

Since we have not yet created any JUnit tests for this module, testing it involves invoking the sensor.simplesensor.jar file manually and checking to see if it sent data to the server successfully. To that, change directories to the build/war/download directory, where you should find a copy of the freshly built sensor.simplesensor.jar file:

C:\svn\hackyCore_Build\build\war\download>dir
 Volume in drive C has no label.
 Volume Serial Number is 0891-1F61

 Directory of C:\svn\hackyCore_Build\build\war\download

02/20/2007  04:27 PM    <DIR>          .
02/20/2007  04:27 PM    <DIR>          ..
02/20/2007  04:26 PM         2,175,109 DotNetSensorShellWrapper.zip
02/20/2007  04:26 PM    <DIR>          eclipse
02/20/2007  04:26 PM             7,705 hackyDoc_NewModule.zip
02/20/2007  04:26 PM            10,246 hackyDoc_SimpleSdt.zip
02/20/2007  04:26 PM             7,418 hackyDoc_SimpleTelemetryFunction.zip
02/20/2007  04:27 PM         5,725,508 hackyInstaller.jar
10/27/2005  03:25 PM             1,711 sensor.properties
02/20/2007  04:27 PM         4,288,392 sensor.simplesensor.jar
02/20/2007  04:26 PM         3,560,060 sensorshell.jar
02/20/2007  04:26 PM            21,944 stackyhack.zip
               9 File(s)     15,798,093 bytes
               3 Dir(s)  45,711,761,408 bytes free

C:\svn\hackyCore_Build\build\war\download>

Now we can invoke this sensor from the command line using the java -jar syntax. Unlike our initial version of the SimpleSensor which required the sensorshell.jar file to be on the classpath, this version incorporates the sensorshell code into the sensor.simplesensor.jar file, making the invocation of the sensor a bit easier:

C:\svn\hackyCore_Build\build\war\download>java -jar sensor.simplesensor.jar Run

C:\svn\hackyCore_Build\build\war\download>

To ensure that data from this sensor was received by the server, you must login to the Hackystat server where this data was sent, and run the List Sensor Data command on the Extras page.

17.5.8. Integration with tool-specific library files

The SimpleSensor sensor does not illustrate one frequent and important aspect of sensor design: the need to use a tool-specific library file. Consider, for example, sensors for Java-based tools such as JEdit, Eclipse, and JBuilder. In each of these cases, the sensor will utilize API calls provided by the IDE to generate sensor data when the user interacts with the IDE in a certain way.

For example, in JBuilder, the sensor code needs to invoke classes in the com.borland.primetime package and its subpackages. In order to compile the JBuilder sensor, the Ant compile target needs to reference the jbuilder.jar and primetime.jar file located in a JBuilder installation.

One possible approach to accessing these jar files is to create a lib/ directory in your module and copy all required library jar files into that location. There are two problems with this approach. First, some tools may have licensing restrictions on distribution of their executables which makes it illegal to distribute them in a Hackystat module. Second, even if the tool allows unrestricted distribution of its code, this approach bloats the size of the Hackystat distribution with library files.

The preferred solution in Hackystat is to design your local.build.xml file to reference the library files via a tool-specific OS environment "home" variable. For example, the hackySensor_JBuilder local.build.xml file refers to the JBUILDER_HOME environment variable, which must be defined and point to a local JBuilder installation directory in order to compile the JBuilder sensor successfully. The Eclipse sensor requires an ECLIPSE_HOME environment variable, the Jira sensor requires a JIRA_HOME sensor, and so forth. Example 17.15, “A local.build.xml with tool-specific library code ” shows an excerpt from the JBuilder local.build.xml file which illustrates how this can be accomplished.

Example 17.15. A local.build.xml with tool-specific library code

 
 <!-- Check if JBuilder jar file is available. -->
  <condition property = "JBuilder.jar.available">
      <available file="${env.JBUILDER_HOME}/lib/jbuilder.jar"/>
  </condition>
        
  <path id="hackySensor_JBuilder.classpath">
    <pathelement location="${install.war.web-inf.classes.dir}"/>
    <pathelement location="${tomcat.servlet.jar}"/>     
    <fileset dir="${install.war.web-inf.lib.dir}">
      <include name="*.jar"/>
    </fileset>
    <!-- JBuilder X (client-side) sensor libraries (if available) -->
    <pathelement location="${env.JBUILDER_HOME}/lib/jbuilder.jar"/>
    <pathelement location="${env.JBUILDER_HOME}/lib/primetime.jar"/>
  </path>

  <target name="hackySensor_JBuilder.compile"  if="hackySensor_JBuilder.available" 
    description="Compiles changed classes to WEB-INF/classes directory.">
    <fail message="Missing JBuilder libraries. Define JBUILDER_HOME." unless="JBuilder.jar.available"/>
    <hackyCore_Build.javac srcdir="${hackySensor_JBuilder.src.dir}">
      <classpath refid="hackySensor_JBuilder.classpath" />
    </hackyCore_Build.javac>
  </target>

Note that there are three steps to this integration. The first step is to create a property (in this case, JBUILDER_HOME.available) which indicates whether the tool-specific library files are available. The second step is to use the environment variable (in this case, via the automatically defined Ant property env.JBUILDER_HOME) to define the classpath used in compilation. The third step is to invoke the "fail" task in the compile target to help the user understand the need to define the appropriate environment variable if it is missing.

17.5.9. Summary

This section showed the basic files and steps required to define our Simple Sensor as a Hackystat module and integrate it into the Hackystat build system. Now that it is a Hackystat module, we can now incorporate more features that will make our sensor easier to use and maintain: JUnit tests, HackyInstaller integration, and DocBook documentation. Each of these will be covered in the next several sections.