Skip to main content
  • Sign in (or register)
  • English
  •  [userid]

By clicking Submit, you agree to the developerWorks terms of use.

  • Need an IBM ID?
  • Forgot your IBM ID?
  • Forgot your password?
  • Change your password

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

My developerWorks:

  • My profile
  • My home
  • My groups

My notifications:

  • {[num_notify] new notifications}([num_notify] new notification)
  • {[num_invite] network requests}([num_invite] network request)
  • Sign out

Select a language:

  • English
  • 中文
  • 日本語
  • Русский
  • Português (Brasil)
  • Español
  • Việt
  • IBM home
  • Solutions
  • Software
  • Software services
  • Support
  • Product information
  • Redbooks

Technical topics

  • Agile transformation
  • AIX and UNIX
  • Business analytics
  • Business process management
  • Cloud computing
  • Commerce
  • IBM i
  • Industries
  • Information management
  • Java technology
  • Linux
  • Lotus
  • Mobile development
  • Open source
  • Rational
  • Security
  • Service management
  • SOA and web services
  • Tivoli
  • Web development
  • WebSphere
  • XML
  • Technical library
  • Products A to Z

Evaluation software

  • Information management
  • Lotus
  • Rational
  • Tivoli
  • WebSphere
  • More IBM products

Community

  • My home
  • Profiles
  • Groups
  • Blogs
  • Bookmarks
  • Forums
  • Wikis
  • Files
  • Activities
  • Podcasts
  • IBM Champion program

Events

  • Briefings
  • Webcasts
  • Find events (briefings, webcasts, conferences...)
  • developerWorks
  • Technical topics
  • Java technology
  • Technical library

AOP@Work: Component design with Contract4J

Improve your software with Design by Contract and AspectJ

Dean Wampler, PhD (dean@aspectprogramming.com), Principal, Aspect Research Associates, Inc.
Dean Wampler provides AspectJ, Enterprise Java, and Ruby on Rails consulting through his company Aspect Research Associates. He is the founder of the Contract4J open source project.

Summary:  Design by Contract is a proven technique for clarifying component design details, documenting proper usage for clients, and testing usage compliance programmatically. In this final article in the AOP@Work series, Dean Wampler introduces Contract4J, a Design by Contract tool that specifies contracts using Java™ 5 annotations and evaluates them at run time using AspectJ aspects. Along with being a strong addition to your AOP toolkit, Contract4J offers insight into emerging trends in aspect-oriented design.

View more content in this series

Tag this!

Date:  11 Apr 2006
Level:  Introductory
Also available in:   Japanese

Activity:  756 views
Comments:  

Suppose you've just joined a project that is building a banking application. Browsing the code, you find the following interface for a (simplistic) BankAccount:

interface BankAccount {

  float getBalance(); 

  float deposit(float amount);

  float withdraw(float amount);
  ...
}

While succinct, the above interface leaves a lot of questions unanswered. Can the amount argument to deposit() or withdraw() be negative or zero? Are negative balances (overdrafts) allowed? What happens in deposit() or withdrawal() if an invalid amount is specified?

Clearly, it's important to be able to answer these questions, both for implementers of the interface and for clients of the components that expose the interface. One way to specify the behavior implicitly is to use unit tests written with JUnit (see Resources). With JUnit tests, you can invoke the methods with various legal and illegal arguments and make assertions that the expected resulting behavior occurs. Another approach is Design by Contract, a proven technique for clarifying component design details.

In this final article in the AOP@Work series, I introduce you to Contract4J, an AspectJ-based tool that supports Design by Contract. I show you how to use Contract4J to implicitly specify component behavior, document proper component usage for clients, and programmatically test for compliance. At the end of the article, I discuss how Contract4J fits into emerging trends in aspect-oriented design.

About this series

The AOP@Work series is intended for developers who have some background in aspect-oriented programming and want to expand or deepen what they know. As with most developerWorks articles, the series is highly practical: you can expect to come away from every article with new knowledge that you can put immediately to use.

Each of the authors contributing to the series has been selected for his leadership or expertise in aspect-oriented programming. Many of the authors are contributors to the projects or tools covered in the series. Each article is subjected to a peer review to ensure the fairness and accuracy of the views expressed.

Please contact the authors individually with comments or questions about their articles. To comment on the series as a whole, you may contact series lead Nicholas Lesiecki. See Resources for more background on AOP.

Overview of Design by Contract

With Design by Contract, you can use programmatic expressions to specify the requirements on inputs to a component, as well as on the returned results. The expressions are evaluated at run time during developer and QA testing, and if a test fails, program execution instantly terminates. A termination comes with useful diagnostic information, forcing you to fix the bug immediately.

It may seem onerous to force termination immediately. Why not just issue an error message and keep going? While carrying on seems more productive, it actually isn't. First, if you're not forced to deal with the bug immediately, you're likely to put off fixing it, so bugs tend to accumulate. Second, a failed test should indicate that something unexpected happened (for example, a reference is null) and normal execution can't continue. You could put in "contingency handling" code, but that would complicate the implementation for a condition that should never occur, thereby increasing the complexity of the code and the risk of more bugs.

Specifying component behavior

Design by contract is a tool for finding and fixing logic bugs in code. It does not address other issues such as performance, user input mistakes, etc. Design by contract uses three types of tests to specify and ensure component behavior:

  • Tests on component inputs (such as parameters passed to methods) are called precondition tests. They specify conditions that the component requires to be true to perform the requested operation. The client must meet these requirements to use the component.

  • Postcondition tests are guarantees that the component promises to satisfy when it completes the operation, assuming the preconditions are met. Postconditions are often expressed as assertions about method return values.

  • Finally, invariant tests assert conditions that never change. A class invariant must hold before and after all method calls (once the object is constructed). A method invariant must hold before and after the method executes; a field invariant must be true for a field for the life of the object.

Note that Design by Contract tests can be turned off in production deployments to remove their overhead.

Unit testing and Design by Contract

Design by Contract has some advantages over unit testing, but the two methods are complementary. One of the major benefits of Design by Contract is that it provides explicit information within the interface or class itself about expected behavior. The result is essential documentation for component developers and clients in a format that can be programmatically tested. Design by Contract also makes explicit the contract definition that is more implicit in unit tests. I tend to use the two techniques in tandem, especially when working with technologies that are difficult to unit test, like EJB2 beans. As you'll soon see, Contract4J has the added feature of enforceable usage constraints, which is a considerable advantage over the informal documentation that is implicit in JUnit tests.


Back to top

Introducing Contract4J

Contract4J is an open source developer tool that implements design by contract using Java 5 annotations (see Resources). Behind the scenes, it uses aspects to insert "advice" at the program join points (for example, calls to methods) where the tests should be evaluated, and it handles any failure of those tests by terminating program execution.

Consider the BankAccount interface again, but this time with Contract4J annotations. Note that I've highlighted the original code in bold and some strings have been wrapped to fit the column and to aid clarity:


Listing 1. BankAccount with Contract4J annotations
@Contract
@Invar("$this.balance > = 0.0")
interface BankAccount {

  @Post("$return >= 0.0")
  float getBalance(); 

  @Pre("amount >= 0.0")
  @Post("$this.balance == $old($this.balance)+amount 
         && $return == $this.balance")
 float deposit(float amount);

  @Pre("amount >= 0.0 &&
        $this.balance -- amount >= 0.0")
  @Post("$this.balance == $old($this.balance)-amount 
         && $return == $this.balance")
  float withdraw(float amount);
  ...
}

Table 1 defines the keywords you see Listing 1:


Table 1. A sample of Contract4J keywords
KeywordDefinition
$thisThe object under test.
$targetCurrently used only for fields in field-invariant tests (you can also refer to fields using $this.field_name). Future use may extend $target to other contexts.
$returnThe object (or primitive value) returned by a method. Valid only in postcondition tests.
$args[n]The nth parameter passed to a method, counting from 0. The parameters can also be referred to by name.
$oldThe "old" value (before a join point is actually executed) of the contents within the parentheses. Valid only for invariant and postcondition tests. Because the Java language doesn't require that all classes support "cloning," Contract4J cannot know whether it can clone a particular object. Therefore, expressions within $old(...) should contain only primitive values or objects that won't change. Otherwise, the "old" value may change when the join point executes, possibly yielding unexpected results. Example expressions include $old("$this.userName") and $old("$this.calcMin(x,y)"). The Contract4J documentation describes the allowed expressions in detail.

The BankAccount contract

Based on what you learned in the previous section, the annotations in Listing1 shouldn't be mysterious to you. The @Contract annotations indicate that the interface (or class) has a contract specification. The @Pre, @Post, and @Invar annotations define precondition, postcondition, and invariant tests, respectively. You'll also note that the tests in Listing 1 are Java expressions defined as strings, evaluating to true or false.

If you omit the test expression, Contract4J uses reasonable defaults, depending on the context. For example, the default invariant condition for a field requires the field to be non-null. Similarly, the default method precondition requires all input arguments that aren't primitives to be non-null, and the default method postcondition requires the return value to be non-null.

The contract specification for BankAccount includes a class-wide invariant that the balance must always be greater than or equal to zero (sorry, no overdrafts allowed!). The getBalance() method has a postcondition that it must always return a value greater than or equal to 0. Notice that the invariant and other tests refer to an implied balance field, even though an interface can't define fields. However, because the interface does define a corresponding JavaBean accessor method, Contract4J infers the existence of the field. Note that the postcondition test on getBalance() may seem redundant with the class invariant, but it partially tests the assumption that this method actually returns the balance.

The contract also has preconditions that require clients to pass in an amount greater than or equal to zero to both withdraw() and deposit(). withdraw() has the additional precondition requirement that the amount can't exceed the existing balance. Finally, withdraw() and deposit() have similar postcondition requirements; the value returned must equal the new balance and the new balance must equal the old (original) balance minus or plus the input amount, respectively.

General notes

The README in the Contract4J distribution (see below) discusses its syntax in great detail, including known limitations and idiosyncrasies. Contract4J's own unit tests in the distribution provide extensive examples of valid, as well as invalid, test expressions.

You can also write contract tests on classes or aspects, where you can define tests on constructors and invariant conditions on instance fields. (The class invariant above was effectively a field-invariant specification!) Methods and constructors can also have invariant tests.

Because the contract affects discrete points of execution as seen by component users and subclasses, Contract4J treats the public, protected, and package-visible methods as "atomic." This means that a test can be violated temporarily during the method, as long as the conditions are satisfied when it completes. Note that nested calls to other methods or fields with tests will trigger those tests, with some special-case exceptions to prevent infinite recursions in aspect code, etc. Also, Contract4J currently disallows defining tests on private methods because they are not visible to external clients. Tests on static methods also are not supported because they don't affect object state. However, these two "theoretical" restrictions may be eliminated in a future release.

Finally, field-invariant tests are only evaluated after field reads and writes, to permit lazy evaluation. Similarly, field-invariant tests are never evaluated during object construction, but they are evaluated after construction completes.

Alternatives to Contract4J

You don't really need Contract4J to write Design by Contract tests. You could just write your own aspects (as discussed in a previous article in this series; see Resources). The advantage of Contract4J is that developers with no AspectJ experience can use it; only a straightforward build process change is required, which I discuss below. Contract4J also provides a very succinct way of defining tests, using familiar Java constructs, without having to define lots of additional AspectJ "boilerplate." Not only are the contracts executable, they are also part of the user-visible code and documentation, information that would be more obscure if captured in separate aspects.

As previously noted, it is true that unit testing and Design by Contract accomplish similar objectives by different means. A Design by Contract tool like Contract4J is most helpful in situations where unit testing is sparse or difficult. Integration and burn-in tests are a good place to capture those obscure integration problems often missed by lower-level testing. Whether or not you use Contract4J with your unit tests, thinking about the contract of your components will improve your designs.


Back to top

Contract4J under the hood

At run time, Contract4J uses built-in aspects to advise the join points where the tests should be executed. The pointcuts in these aspects look for the appropriate annotations. Precondition tests are handled by before advice, which is executed just before the corresponding method execution join point. The before advice uses the Apache Jakarta Commons JEXL interpreter to convert the special keywords in the test strings to the appropriate objects and to evaluate the resulting expressions, returning true (pass) or false (fail). If a test fails, an error message is reported, indicating the point of failure, and program execution halts.

In Listing 1, for example, if withdraw() is called, just before executing the method, Contract4J evaluates the expression amount >= 0, with the input value for amount. If amount = -1.0, for example, then the test fails, an error reporting the location of the failure with a stack trace is reported, and the application exits.

Similarly, postcondition tests correspond roughly to after advice. However, to support the $old keyword, around advice is actually used, where the "subexpressions" in the $old keywords are evaluated, the results are saved, the original join point is executed, and the full test expression is then evaluated with the "old" values inserted appropriately.

Finally, invariant tests use around advice, where the tests are evaluated both before and after join point execution with the exceptions noted previously.

Invoking an interpreter like JEXL does add nontrivial overhead, but because Contract4J is designed for use only during development and testing, the overhead is not a serious issue most of the time. However, you may find that some frequently executed code blocks should not have tests.


Back to top

Adopting Contract4J

Because Contract4J's contract tests are written using familiar Java conventions, adoption into a Java environment is straightforward, consisting of four steps:

  1. Download Contract4J and unzip or untar it somewhere convenient. Unless you want to rebuild it (following the instructions in the included README), all you need is the contract4j5.jar file.

  2. Add the location of the contract4j5.jar file to your build CLASSPATH.

  3. Download and install AspectJ.

  4. Switch from your current Java compiler to the AspectJ "ajc" compiler, which also compiles Java code. Details are provided on the AspectJ home page and the Ant scripts that come with the distribution. Or, if you prefer to continue using your Java compiler, you have two additional options:
    • You can introduce an ajc "weaving" step at the end of the build that weaves the aspects from contract4j5.jar into your precompiled classes or JARs.

    • You can "weave" the contracts at load time, as explained in the AspectJ documentation.


  5. Start adding the Contract4J annotations to your source code to define your contracts!

Customizing Contract4J

Using property files or API calls at run time, you can enable or disable all tests, just precondition tests, just postcondition tests, or just invariant tests. Normally, for production deployments, you will build without the contract4j5.jar so that you incur no run-time overhead.

Extensive customization is possible using API calls, including "plug-in hooks" for inserting your own Java classes that implement different behaviors. You can even replace the JEXL expression interpreter.

The Contract4J home page (see Resources) provides extensive documentation on the API and other customization options, as well as allowed test expressions, known limitations, and idiosyncrasies. You can also build the "ant docs" target in the distribution to generate complete Javadocs.


Back to top

Contract4J and AOP

Besides being a useful tool for developers, Contract4J is interesting for two other reasons. First, it is one of a growing number of Java development tools that use aspects under the hood, more or less transparently to the developer. Another example is the Glassbox Inspector discussed previously in this series. Additionally, the Spring framework uses pure-Ja

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.