Automatically implement design guidelines in your code

Add Hammurapi, a design-compliance solution, to your toolbox

As a J2EE architect, I'm required to deliver detailed designs to project teams. Along with the UML (Unified Modeling Language) model, I also deliver design guidelines that capture best practices. For example, in Struts applications, I recommend avoiding instance variables in Action classes because Action classes are singletons, and the potential for multiple threads simultaneously executing an Action class instance is very real. As another example, in any DAO (Data Access Object) application, an important design guideline is to close all open database resources. Failure to do so is always disastrous—especially in a production environment.

Intent is all very well; however, implementation is key. At present, the most common way to implement design guidelines is to do code reviews, where, typically, experienced members visually inspect code and catch noncompliant situations. Such situations might include cases where coding standards (indent level) are violated and/or cases where design standards (no instance variables in Action classes) are violated.

This is a very inefficient process on two counts: It requires at least two resources (the reviewer and the developer). Plus, quality, being a function of the reviewer, is not consistent. In addition, as per my experience, in their zeal to enforce standards, reviewers alienate developers, which is bad for team morale.

A couple of years ago, I came across Checkstyle, a tool for automatically enforcing coding standards. It integrates seamlessly into Ant and is driven by an XML-based configuration file.

Hammurapi does for design standards what Checkstyle does for coding standards. Hammurapi, open source software developed by Pavel Vlasov, is a tool that can analyze a codebase against a set of design guidelines. When the tool comes across a guideline violation, it flags the violation in a report. As with Checkstyle, it seamlessly integrates with Ant. And likewise, it is driven by an XML-based configuration file.

Running Hammurapi

You can run Hammurapi directly from the command line and also from Eclipse as a plug-in. In this article, however, I discuss how to run Hammurapi as an Ant task.

Note: You can download the source code that accompanies this article from Resources.

Integration with Ant is very straightforward, as the code below illustrates:

 1   <target name="design_review" depends="init">
 2      <taskdef name="hammurapi" classname="org.hammurapi.HammurapiTask">
 3         <classpath>
 4            <fileset dir="${hammurapi.home}\lib">
 5               <include name="**\*.jar"></include>
 6            </fileset>
 7         </classpath>
 8      </taskdef>
 9      <hammurapi>
10         <src dir="src"/>
11         <output dir="docs\review"/>
12         <classpath>
13            <pathelement location="${$log4j.home}\lib\log4j.jar"\/>
14            <pathelement location="${weblogic.home}\lib\weblogic.jar"\/>
15         </classpath>
16      </hammurapi>
17   </target>

Line 1 defines a target called design_review. It depends on another target called init, which initializes build properties (${hammurapi.home} in Line 4, for instance). Line 2 defines a new task called hammurapi. This Ant task is implemented by the class org.hammurap.HammurapiTask. The nested element classpath, beginning on Line 3, specifies classfiles required for defining this task.

Line 9 declares a hammurapi task previously defined in Line 2. Running this task is simple: define the nested element src, which tells the task where to look for source files; then define another nested element output, which tells the task where to write the report to. Line 10 tells the task to look under the src folder under the (Ant) project directory for source files to review. Line 11 tells the task to write the report to docs\review under the (Ant) project directory.

The classpath nested element beginning on Line 12 is optional. It specifies libraries on which source code is dependent.

Hammurapi comes with more than 100 built-in inspectors. Each inspector enforces a design guideline.

The Ant snippet illustrated in the previous code listing was used to run Hammurapi on log4j source code. This code was unzipped to src folder under the (Ant) project directory. Figure 1 shows the opening page of the generated report. Hammurapi's most exciting feature is the sophisticated report it generates.

Figure 1. Report. Click on thumbnail to view full-sized image.

Figure 1 illustrates report.html, which is the main page. The right frame shows three headings: Results, Severity Summary, and Files (see Figure 2).

The Results section (Figure 1) captures statistics that apply to the entire review. As shown in Figure 1, 26 packages and 179 files were reviewed; 6,344 violations were reported.

The Severity Summary section tabulates violations. Every violation belongs to a severity level. Predefined levels run from 1 through 5. Level 1 is the most severe.

The Files (Figure 2) section lists each reviewed file and its detected violations.

Figure 2. Files. Click on thumbnail to view full-sized image.

While the Severity Summary section can answer questions like: How many empty catch blocks were found?, the Files section can answer questions like: How many violations were reported in Appender.java? But, by far, the most useful feature of Hammurapi is its ability to display offending lines of code in the report. For example, let's analyze the design guideline, ER 002: empty catch block (Figure 2). Clicking on the hyperlink under the Number column displays the files that reported this violation, as shown in Figure 3.

Figure 3. Files violating guideline ER 002: empty catch block. Click on thumbnail to view full-sized image.

Among many other files, the report shows that LogRecord.java (3) reported this violation in Line 307. The hyperlink under the Line column takes you directly to source code, which is shown in Figure 4.

Figure 4. Line 307 in LogRecord.java showing a violation. Click on thumbnail to view full-sized image.

While running Hammurapi from scratch on the entire codebase is probably most common, it is also possible to incrementally review that subset of code that changed since the last review. This proves useful in situations where the codebase is large and, consequently, reviews are long-running.

Another useful mode of running Hammurapi is to zip source files and dependent libraries and run the review on the resulting zip file. This approach proves useful in situations where the development staff is spread across multiple geographic locations. In such situations, source code can be zipped and shipped to the remote location where reviews are conducted.

Other design review tools

Hammurapi is not the only tool available that reviews code. Metrics is a rather popular tool available as a plug-in for Eclipse. However, it has two major drawbacks. The first is its tight integration with Eclipse. Integrating it with Ant is rather cumbersome. Integration requires Eclipse libraries, which means that projects that don't use Eclipse as the IDE cannot use Metrics. Also, you can't build custom inspectors with Metrics—Hammurapi allows custom inspectors—which limits users to using built-in inspectors, quite a restrictive condition.

Another tool available to the Java community is PMD. Like Hammurapi, it integrates seamlessly with Ant and allows custom inspectors. Unfortunately, reports generated by PMD are not as comprehensive as the reports generated by Hammurapi.

How Hammurapi works

Code analysis tools like Hammurapi work with language parsers. A language parser is a tool that accepts a (programming) language as input and generates an abstract syntax tree as output. The nodes of this tree are language tokens. For example, consider the simple arithmetic expression: 3 + 4. A language parser parses it into a tree as shown in Figure 5. In this tree, the node + is an operator token. The nodes 3 and 4 are operand tokens.

Figure 5. Abstract syntax tree

Under the hood, Hammurapi uses ANTLR (ANother Tool for Language Recognition) as its language parser. However, the ANTLR API is rather low-level. To improve productivity, Hammurapi uses another API, JSEL (Java Source Engineering Library), built on top of ANTLR, to access the abstract syntax tree.

Once the tree is constructed, a tree-walker algorithm is used to visit each node in the tree. Each time a node is visited, a callback mechanism (Visitor pattern for you pattern buffs) is used to inform interested inspectors. In these callback methods, inspectors collect information to determine whether a violation has occurred.

Building a custom inspector

A custom inspector can help further explain the Hammurapi framework. As mentioned previously, a Struts best practice is avoiding instance variables in Action classes. So we will build a custom inspector, ActionClassInspector, that scans source code for Action classes. If an Action class is found, then it will be scanned for instance variables. If one or more instance variables are found, then it will be flagged as a violation.

Figure 6 illustrates the variables and methods of the ActionClassInspector class. All inspectors inherit from org.hammurapi.InspectorBase.

Figure 6. ActionClassInspector

Figure 7 illustrates a sequence diagram showing the Hammurapi framework invoking callback methods on the ActionClassInspector class. The framework parses source code to construct an abstract syntax tree. It then visits every node in the tree. When a class node is visited, it invokes the visit( c:Class ) method. When a variable node is visited, it invokes the visit( v:VariableDefinition ) method. Since a typical class might include multiple variables, this method could potentially be invoked multiple times.

Figure 7. Framework in action

The code listing below shows the visit( c:Class ) method:

 1   public void visit( com.pavelvlasov.jsel.Class c ) throws Exception
 2   {
 3      isActionClass = c.isKindOf( "org.apache.struts.action.Action");
 4
 5      return;
 6   }

This method is responsible for determining whether a particular class is an Action class. This test is performed in Line 3. If the test is positive, then isActionClass is set to true. This guideline applies only to Action classes.

The following listing illustrates code for the visit( v:VariableDefinition ) method:

 1   public void visit( com.pavelvlasov.jsel.VariableDefinition v ) throws Exception
 2   {
 3      List modifiers;
 4      Scope scope;
 5
 6      if( this.isActionClass )
 7      {
 8         modifiers = v.getModifiers( );
 9         scope = v.getEnclosingScope( );
10         if( scope instanceof com.pavelvlasov.jsel.impl.ClassImpl )
11         {
12            if( modifiers.contains( "static" ) )
13            {
14               ;//Do nothing; this class is compliant.
15            }
16            else
17            {
18               context.reportViolation( (SourceMarker)d, "Violation" );
19            }
20         }
21      }
22
23      return;
24   }

This method is responsible for determining whether a particular variable is an instance variable. If it is, a violation is reported. Line 10 determines the variable's scope. This test filters out variables of method scope. Only variables of class scope (static) or instance scope are considered for further processing. Line 12 determines whether the variable is static. If this test is negative, then a violation is reported, as illustrated in Line 18.

Running custom inspectors requires a minor modification to the Ant script illustrated at the beginning of this article. The code listing below highlights the modifications:

 1   <hammurapi>
10      <src dir="src"/>
 5      <output dir="docs\review"/>
 6      <classpath>
 7         <pathelement location="${weblogic.home} \lib\weblogic.jar"></pathelement>
 8         <fileset dir="${basedir}\lib">
 9            <include name="**\*.jar"></include>
10         </fileset>
11         <pathelement location="${class.dir}"></pathelement>
12      </classpath>
13      <inspectors file="config/inspectors.xml">
14      </inspectors>
17   </hammurapi>

In Line 13, the optional inspectors element is included. This element's file attribute tells the task to look for inspectors in the file inspectors.xml, under the config directory. The listing for file inspectors.xml is illustrated below:

 1   <?xml version="1.0" encoding="UTF-8"?>
 2   <inspector-set>
 3      <inspector-descriptor>
 4         <name>GDL-004</name>
 5         <enabled>yes</enabled>
 6         <severity>3</severity>
 7         <inspector type="hammurapi.ActionClassInspector"></inspector>
 8         <description>
 9            No instance variables in Action classes.
10         </description>
11         <waivable>yes</waivable>
12         <rationale>
13            Action classes must not have instance variables.
14         </rationale>
15            <resources>Struts best practices.</resources>
16      </inspector-descriptor>
17   </inspector-set>

Note that it also possible to specify the inspectors.xml file using a URL. In such a scenario, the inspectors.xml file would be available on a remote Web server.

1 2 Page 1
Page 1 of 2