4.10. Measurement: Structural coupling with DependencyFinder

A very well documented design principle is to minimize dependencies, or couplings between modules. If a system is "loosely coupled", then each component of the system relies on only a few others, which means that changes to that module have a minimal "ripple effect" on other modules. This makes understanding, debugging, and enhancement of the system easier. In contrast, "highly coupled systems" in which most components rely on many other components are much more difficult to understand, debug, and enhance. Measuring the level of structural coupling in a system provides useful information about the ease of future development. Monitoring coupling levels over time can help assess whether evolution is making the system easier or more difficult to modify in future.

Dependency Finder is a tool for measuring the level of coupling in Java-based systems. It determines the number of incoming or outgoing dependencies at the level of packages, classes, and methods.

4.10.1. DependencyFinder tool installation

To install Dependency Finder for use in StackyHack, download the .zip file containing the latest release from the DependencyFinder home page into a directory such as "java-lib", and unzip it. Then, define an environment variable called DEPENDENCYFINDER_HOME to point to the distribution directory. For example, "c:\java-lib\DependencyFinder". (Note that the file path should have no embedded spaces in it.)

4.10.2. The dependencyfinder.build.xml file

Example 4.19, “dependencyfinder.build.xml” shows the dependencyfinder.build.xml file, which defines the Ant targets used to invoke the DependencyFinder tool and the DependencyFinder sensor.

Example 4.19. dependencyfinder.build.xml

<project name="stackyhack.dependencyfinder" default="dependencyfinder">
  <description>
  Provides the DependencyFinder tool and the DependencyFinder sensor.
  </description>

  <import file="build.xml"/>
  <property environment="env"/>
  <property name="dependencyfinder.dir" location="${build.dir}/dependencyfinder" />
  <path id="dependencyfinder.path">
    <pathelement location="${env.DEPENDENCYFINDER_HOME}/classes" />
    <pathelement location="${env.DEPENDENCYFINDER_HOME}/lib/DependencyFinder.jar" />
    <pathelement location="${env.DEPENDENCYFINDER_HOME}/lib/jakarta-oro.jar" />
    <pathelement location="${env.DEPENDENCYFINDER_HOME}/lib/log4j.jar" />
  </path>

  <target name="dependencyfinder" depends="dependencyfinder.tool, dependencyfinder.report, dependencyfinder.sensor" 
   description="Runs DependencyFinder tool, report, and sensor."/>

  <target name="dependencyfinder.tool" depends="compile" description="Computes dependency metrics on the source code.">
    <!-- Verify that DependencyFinder is installed. -->
    <available file="${env.DEPENDENCYFINDER_HOME}/lib/DependencyFinder.jar" property="dependencyfinder.available"/>
    <fail unless="dependencyfinder.available" message="Error: DEPENDENCYFINDER_HOME not set or dependencyfinder.jar not found."/>
    <taskdef name="dependencyextractor" classname="com.jeantessier.dependencyfinder.ant.DependencyExtractor" classpathref="dependencyfinder.path"/>    
    <!-- Run the DependencyFinder tool. -->
    <mkdir dir="${dependencyfinder.dir}"/>
    <dependencyextractor destfile="${dependencyfinder.dir}/all.xml" xml="yes" dtdprefix="${env.DEPENDENCYFINDER_HOME}/etc">
      <path path="${build.dir}/classes" />
    </dependencyextractor>
  </target>

  <target name="dependencyfinder.report" description="Generates an HTML report from DependencyFinder data.">
    <!-- Create XML files with class2class and package2package info, plus HTML reports. -->
    <taskdef name="dependencyreporter" classname="com.jeantessier.dependencyfinder.ant.DependencyReporter">
      <classpath refid="dependencyfinder.path" />
    </taskdef>

    <xslt style="${env.DEPENDENCYFINDER_HOME}/etc/DependencyGraphToHTML.xsl" 
          in="${dependencyfinder.dir}/all.xml" out="${dependencyfinder.dir}/all/index.html" />
    <dependencyreporter srcfile="${dependencyfinder.dir}/all.xml" destfile="${dependencyfinder.dir}/class2class.xml" 
      c2c="yes" showall="yes" includes="edu/hawaii/" validate="true" xml="yes" dtdprefix="${env.DEPENDENCYFINDER_HOME}/etc" />

    <xslt style="${env.DEPENDENCYFINDER_HOME}/etc/DependencyGraphToHTML.xsl" 
          in="${dependencyfinder.dir}/class2class.xml" out="${dependencyfinder.dir}/class2class/index.html" />
    <dependencyreporter srcfile="${dependencyfinder.dir}/all.xml" destfile="${dependencyfinder.dir}/package2package.xml" 
      p2p="yes" showall="yes" includes="edu/hawaii/" validate="true" xml="yes" dtdprefix="${env.DEPENDENCYFINDER_HOME}/etc" />

    <xslt style="${env.DEPENDENCYFINDER_HOME}/etc/DependencyGraphToHTML.xsl" 
          in="${dependencyfinder.dir}/package2package.xml" out="${dependencyfinder.dir}/package2package/index.html" />
  </target>


  <target name="dependencyfinder.sensor" description="Sends Dependency data to Hackystat using the DependencyFinder sensor.">
    <!-- Define the dependencyfinder sensor taskdef, failing the build if the sensor is not installed. -->
    <available classname="org.hackystat.sensor.dependencyfinder.DependencyFinderSensor" property="dependencyfinder.sensor.available"/>
    <fail unless="dependencyfinder.sensor.available" message="Error: DependencyFinder sensor not installed."/>
    <taskdef name="hacky-dependency" classname="org.hackystat.sensor.dependencyfinder.DependencyFinderSensor" />    

    <hacky-dependency verbose="${hackystat.verbose.mode}"
                      class2ClassFile="${dependencyfinder.dir}/class2class.xml" 
                      package2packageFile="${dependencyfinder.dir}/package2package.xml">
      <fileset dir="${src.dir}" includes="**/*.java"/>
    </hacky-dependency>

  </target>
</project>

The dependencyfinder.build.xml defines the <dependencyfinder.tool> target to obtain coupling information. The <dependencyfinder.sensor> target uses the DependencyFinder sensor to read in the XML files (for package and class-level coupling) generated by DependencyFinder and send the output to Hackystat. The <dependencyfinder> target simply invokes both of these targets in sequence.

4.10.3. Running DependencyFinder to produce structural coupling data

Example 4.20, “dependencyfinder.tool and dependencyfinder.report invocation” shows the output from invoking the <dependencyfinder.tool> target to generate the all.xml file containing the dependency information. Next, the dependencyfinder.report target is called to produce the class2class.xml and package2package.xml files containing these two perspectives on the dependency information.

Example 4.20. dependencyfinder.tool and dependencyfinder.report invocation

C:\svn-csdl\StackyHack>ant -f dependencyfinder.build.xml dependencyfinder.tool
Buildfile: dependencyfinder.build.xml

compile:
    [mkdir] Created dir: C:\svn-csdl\StackyHack\build\classes
    [javac] Compiling 5 source files to C:\svn-csdl\StackyHack\build\classes

dependencyfinder.tool:
    [mkdir] Created dir: C:\svn-csdl\StackyHack\build\dependencyfinder
[dependencyextractor] Reading classes from path C:\svn-csdl\StackyHack\build\classes
[dependencyextractor] Saving dependency graph to C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml
     [xslt] Processing C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml to C:\svn-csdl\StackyHack\build\dependencyfinder\all\index.html
     [xslt] Loading stylesheet C:\java\DependencyFinder-1.1.0-beta3\etc\DependencyGraphToHTML.xsl
[dependencyreporter] Reading graph from C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml
[dependencyreporter] Saving dependency graph to C:\svn-csdl\StackyHack\build\dependencyfinder\class2class.xml
     [xslt] Processing C:\svn-csdl\StackyHack\build\dependencyfinder\class2class.xml to C:\svn-csdl\StackyHack\build\dependencyfinder\class2class\index.html
     [xslt] Loading stylesheet C:\java\DependencyFinder-1.1.0-beta3\etc\DependencyGraphToHTML.xsl
[dependencyreporter] Reading graph from C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml
[dependencyreporter] Saving dependency graph to C:\svn-csdl\StackyHack\build\dependencyfinder\package2package.xml
     [xslt] Processing C:\svn-csdl\StackyHack\build\dependencyfinder\package2package.xml to C:\svn-csdl\StackyHack\build\dependencyfinder\package2package\index.html
     [xslt] Loading stylesheet C:\java\DependencyFinder-1.1.0-beta3\etc\DependencyGraphToHTML.xsl

BUILD SUCCESSFUL
Total time: 2 seconds


C:\svn-csdl\StackyHack>ant -f dependencyfinder.build.xml dependencyfinder.report
Buildfile: dependencyfinder.build.xml

dependencyfinder.report:
     [xslt] Processing C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml to C:\svn-csdl\StackyHack\build\dependencyfinder\all\index.html
     [xslt] Loading stylesheet C:\java-tools\DependencyFinder-1.1.1\etc\DependencyGraphToHTML.xsl
[dependencyreporter] Reading graph from C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml
[dependencyreporter] Saving dependency graph to C:\svn-csdl\StackyHack\build\dependencyfinder\class2class.xml
     [xslt] Processing C:\svn-csdl\StackyHack\build\dependencyfinder\class2class.xml to C:\svn-csdl\StackyHack\build\dependencyfinder\class2class\index.html
     [xslt] Loading stylesheet C:\java-tools\DependencyFinder-1.1.1\etc\DependencyGraphToHTML.xsl
[dependencyreporter] Reading graph from C:\svn-csdl\StackyHack\build\dependencyfinder\all.xml
[dependencyreporter] Saving dependency graph to C:\svn-csdl\StackyHack\build\dependencyfinder\package2package.xml
     [xslt] Processing C:\svn-csdl\StackyHack\build\dependencyfinder\package2package.xml to C:\svn-csdl\StackyHack\build\dependencyfinder\package2package\index.html
     [xslt] Loading stylesheet C:\java-tools\DependencyFinder-1.1.1\etc\DependencyGraphToHTML.xsl

BUILD SUCCESSFUL
Total time: 2 seconds
C:\svn-csdl\StackyHack>

4.10.4. Running the DependencyFinder sensor to send Dependency data to Hackystat

After the DependencyFinder tool and report targets have been invoked to generate data regarding the structural coupling in the system, the DependencyFinder sensor can be invoked to read in the resulting class2class and package2package XML files produced by DependencyFinder and send the resulting data to Hackystat as Dependency sensor data.

To accomplish this, you must first install the DependencyFinder sensor, which can process the XML files output by DependencyFinder. General instructions on installing sensors are provided in Chapter 2, Client-side configuration: Tool sensor installation; instructions for specific sensors are provided in Chapter 26, Sensors. After installation, your hackyInstaller DependencyFinder configuration window should look something like the screen image in Figure 4.12, “ HackyInstaller configuration screen for DependencyFinder sensor ”.

Figure 4.12.  HackyInstaller configuration screen for DependencyFinder sensor


HackyInstaller configuration screen for DependencyFinder sensor

Example 4.21, “dependencyfinder.sensor invocation” illustrates the invocation of the DependencyFinder sensor.

Example 4.21. dependencyfinder.sensor invocation

C:\svn-csdl\StackyHack>ant -f dependencyfinder.build.xml dependencyfinder.sensor
Buildfile: dependencyfinder.build.xml

dependencyfinder.sensor:
[hacky-dependency] DependencyFinderSensor verbose mode.
[hacky-dependency] Sensor enabled?: true
[hacky-dependency] Processing dependency xml file: C:\svn-csdl\StackyHack\build\dependencyfinder/class2class.xml
[hacky-dependency] Processed Class Dependency: 5
[hacky-dependency] Processing dependency xml file: C:\svn-csdl\StackyHack\build\dependencyfinder/package2package.xml
[hacky-dependency] Processed Package Dependency: 1
[hacky-dependency] Hackystat data on 6 Dependency Finder dependency entries sent to http://hackystat.ics.hawaii.edu/ (2 secs.)

BUILD SUCCESSFUL
Total time: 3 seconds

Once you've invoked the sensor, you can verify that this data was received at the server by using the List Sensor Data command. Figure 4.13, “ List Sensor Data with StackyHack DependencyFinder data ” illustrates what this page might look like after receiving the StackyHack data.

Figure 4.13.  List Sensor Data with StackyHack DependencyFinder data


List Sensor Data with StackyHack DependencyFinder data

As you can see, coupling data was collected by DependencyFinder for each of the five of the classes in the StackyHack system, plus for the package that contains StackyHack. The data lists each of the classes that the given class depends upon, as well as each of the classes that depends upon this class. This system appears to be relatively loosely coupled, since it contains no more than 2 in-bound or out-bound dependencies per class, but that is no great accomplishment given the simplicity of the StackyHack system.