Checkstyle makes it easy to automate code reviews once the code has been committed, but wouldn't it be better if programming errors never made it that far? In this second half of "Automated code reviews with Checkstyle," authors ShriKant Vashishtha and Abhishek Gupta show you how to be proactive about code quality. Find out how to use Checkstyle's custom rules to enforce code standards and catch mistakes -- long before code is committed to your code base. Level: Intermediate
In the first half of this article, you learned how easy it is to create and use custom Checkstyle rules for automated code reviews. The solution we presented in that article was only partial, however: it offers custom rules but no mechanism for keeping faulty code out of your code base. In this article we'll show you how to use Checkstyle to proactively enforce code standards, first by catching them as they're written, and then by catching them at the source repository level. Proactively enforcing code standards at these levels will make code reviews less time-consuming, and will allow your team to concentrate on the finer points of code quality.
The first example is based on a custom Eclipse plugin, which displays a warning when it spots a problematic line of code. The second example, especially good for teams not using Eclipse, shows you how to use Subversion's pre-commit hook to check for code violations. This approach bars developers from committing source code with un-fixed violations.
We'll start with building the custom Eclipse plugin to handle custom checks. If you're not familiar with creating custom rules in Checkstyle, you should probably read the first half of this article before continuing. If you're not using the Eclipse IDE, skip ahead to learn about enforcing code standards with Subversion's pre-commit hook.
Build an Eclipse plugin to enforce custom rules
The standard eclipse-cs Eclipse plugin works well for Checkstyle's built-in checks, but you'll need to create your own plugin to handle custom checks. For the sake of demonstration, we'll use
IllegalExceptionThrowsCheck, the simpler custom check from the first half of this article. The sample code for this article contains the source code for
IllegalExceptionThrowsCheck. Note that we assume familiarity with the Eclipse development environment and plugins.
Before you begin, you have the option to provide metadata in the form of a checkstyle-metadata.xml file for the custom checks; this makes it easier to use the capabilities of the Eclipse plugin's configuration editor. Place this file into the packages where your check classes reside. You'll be able to configure your custom check with the editor just as you can any standard Checkstyle module. Listing 1 contains the sample configuration for your custom check,
Listing 1. Changes in plugin.xml for custom checks
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE checkstyle-metadata PUBLIC "-//eclipse-cs//DTD Check Metadata 1.0//EN" "http://eclipse-cs.sourceforge.net/dtds/checkstyle-metadata_1_0.dtd"> <checkstyle-metadata> <rule-group-metadata name="Custom Checks" priority="900"> <rule-metadata name="Illegal Exception Throw" internal-name="IllegalExceptionThrowsCheck" parent="TreeWalker"> <alternative-name internal-name="com.abc.checkstyle.check. IllegalExceptionThrowsCheck" /> <description></description> <property-metadata name="illegalExceptionsInThrows" datatype="String" default-value="Exception,java.lang.Exception"> <description></description> </property-metadata> </rule-metadata> </rule-group-metadata> </checkstyle-metadata>
<checkstyle-metadata> tag can contain the metadata of multiple rule groups, each with a
<rule-group-metadata> tag. The
name attribute is used as the display name, and the
priority attribute influences the order in which the groups appear in the configuration editor.
rule-metadata elements contain the metadata information for a check module.
internal-name is the logical name of the module, and
parent determines whether the module is a
FileSet check or a regular
rule-metadata element must contain at least one
alternative-name element that provides the mapping of this check with the one available in the Checkstyle configuration file created for custom checks. (That is, custom_check.xml, in this case the name could be something else.) This file is used to configure the custom Checkstyle checks and is placed in the <path-to-my-eclipse>/plug-ins/com.atlassw.tools.eclipse.checkstyle_x.x.x directory. The
rule-metadata element may also contain
property-metadata elements, which are used to configure the check in the plugin configuration editor.
Next, you need to package the already created custom checks into a JAR file and place that file in the <path-to-my-eclipse>/plug-ins/com.atlassw.tools.eclipse.checkstyle_x.x.x/extension-libraries directory. Then copy custom_check.xml to the Checkstyle plugin folder. After that, you'll need to make some changes in plugin.xml, which is located in the Checkstyle plugin folder (<path-to-my-eclipse>/plug-ins/com.atlassw.tools.eclipse.checkstyle_x.x.x). You'll put the details of your extensions under the
<extension> tag, as shown in Listing 2.
Listing 2. Changes in plugin.xml
<!-- Standard plugin check configurations --> <extension id="checkstyle.CheckConfiguration" point="com.atlassw.tools.eclipse.checkstyle.configurations"> <check-configuration name="Sun Checks" location="sun_checks.xml" description="%SunChecks.description"/> <check-configuration name="Custom Checks" location="custom_check.xml" description="%CustomChecks.description"/> <check-configuration name="Sun Checks (Eclipse)" location="sun_checks_eclipse.xml" description="%SunChecksEclipse.description"/> </extension>
description attribute of the
check-configuration tag can be internationalized, it is externalized in <path-to-my-eclipse>/plug-ins/com.atlassw.tools.eclipse.checkstyle_x.x.x/plugin.properties, as shown in Listing 3.
Listing 3. Changes in plugin.properties
CustomChecks.description = Checkstyle configuration that checks the project specific coding conventions.
You will be able to see this configured description under the Description tab of the Checkstyle space in the Eclipse configuration, as illustrated in Figure 1.
Next, you need to restart Eclipse at the command prompt with the
-clean option, as follows:
<path-to-eclipse> eclipse -clean
Now, in Eclipse choose Windows > Preferences > Checkstyle. You'll see your custom checks in the editor, as illustrated in Figure 2. You can set this new set of checks as the default for an entire workspace, or can choose a subset of them for a specific project.
After you have configured your Eclipse project with the custom Checkstyle checks, code that violates the rules laid down in your Checkstyle configuration will cause an an error, along with message describing the problem. For example, if you configured a project with a rule limiting the number of methods that a class could have, going over that limit would result in a message like the one shown in Figure 3.
Subversion pre-commit hooks
What if your project uses an IDE other than Eclipse? Or, even if your team does use Eclipse, what if one of your developers uses some other text editor to create Java classes and commit them into Subversion? It's very difficult to track these kinds of issues unless you're using a continuous integration environment for continuous builds.
One solution is to apply Checkstyle rules at the repository level, using Subversion. This approach bypasses the IDE and prevents developers who don't comply with project-level coding standards from committing source code to the SCM repository. If you want to be extra proactive, you can combine this approach with compile-time build checks via the Eclipse plugin or at Ant-based build time. By default your build tool will be able to tell you if your source code has passed the checks or not. Even developers who escape build-time checks will be caught when they attempt to commit the non-compliant code into Subversion. All of this is possible using SVN's repository hooks.
What are repository hooks?
A hook is a program triggered by some repository event, such as the creation of a new revision or the modification of an unversioned property. Some hooks (so-called pre hooks) run in advance of a repository operation and provide a means by which to both report what is about to happen and to prevent it from happening at all. Other hooks (called post hooks) run after the completion of a repository event and are useful for performing tasks that examine -- but don't modify -- the repository. Each hook is given enough information to tell what an event is (or was), the specific repository changes proposed (or completed), and the username of the person who triggered the event.
The Subversion hooks subdirectory is, by default, filled with templates for various repository hooks, as you can see in Listing 4.
Listing 4. Default repository hooks
$ cd /home/shrikant/svn/myrepos/hooks $ ls post-commit.tmpl post-unlock.tmpl pre-revprop-change.tmpl post-lock.tmpl pre-commit.tmpl pre-unlock.tmpl post-revprop-change.tmpl pre-lock.tmpl start-commit.tmpl $
These are they types of hooks you can find in a Subversion repository:
start-commit: Notification of the beginning of a commit.
pre-commit: Notification just prior to commit completion.
post-commit: Notification of a successful commit.
pre-revprop-change: Notification of a revision property change attempt.
post-revprop-change: Notification of a successful revision property change.
pre-lock: Notification of a path lock attempt.
post-lock: Notification of a successful path lock.
pre-unlock: Notification of a path unlock attempt.
post-unlock: Notification of a successful path unlock.
As previously noted, the interesting hook for the purposes of this article is the pre-commit hook. This hook is run just before a commit transaction is promoted to a new revision. Typically, this hook is used to protect against commits that are disallowed due to content or location -- for example, the incoming log message should be non-empty, and the hook will prevent the commit if that's not the case.
If the pre-commit hook program returns a non-zero exit value, the commit is aborted, the commit transaction is removed, and anything printed to
stderr is marshaled back to the client.
It should be obvious at this point that a pre-commit hook can be used to check Java files against Checkstyle rules before those files are committed in Subversion. Pre-commit hooks are either shell scripts (on UNIX-like systems) or .bat/.exe files (on Windows systems). As soon as a new Subversion commit is processed, the Subversion kernel calls any repository hooks available. So, if a pre-commit hook could somehow call a Checkstyle command for each Subversion transaction (list of files) being committed, it would return a list of errors. For checking errors for a Java file, the Checkstyle command is as follows:
java -classpath checkstyle-customchecks.jar:checkstyle-all-<version>.jar com.puppycrawl.tools.checkstyle.Main -c <JavaFileName>
If a repository pre-commit hook could intercept files about to be committed, execute that command, format the errors, and display them to the developer, you'd be sitting pretty. Building something like that from scratch would take time and require knowledge of Subversion internals. Fortunately, some good open source projects are focused on the usage of Subversion hooks. One of them is Subversionchecker, a project based on Python. Subversionchecker is quite modular and provides you with sets of checks (Checkstyle, UnitTests, XMLValidator, AccessRights, Checkout, etc.) and handlers. For each kind of check, you can also configure what you want to do (send the message to console, for example, or to a file, or through e-mail); that comes under the handler's module.
For the purposes of this article, you'll use Checkstyle to check only the pre-commit hook of a repository. As you might have already guessed, Subversionchecker provides a configuration file to let you choose among checks and handlers. In the rest of this section, you'll configure Subversionchecker for your Subversion repository and custom Checkstyle check. Please note that all of these steps should be executed on your Subversion server machine.
Next, you need to check to see if a pre-commit hook script is already available in your Subversion repository in the hooks directory. If not, you may want to copy the existing pre-commit.tmpl file (which is located in that same hooks directory) to the pre-commit file. You'll want to make changes to the file as follows: