Simon has posted 6 posts at DZone. View Full User Profile

Assert Yourself With Chex4j

05.30.2011
| 5619 views |
  • submit to reddit

Chex4j is a framework for documenting and enforcing the pre- and post-conditions of Java method calls.  To use chex4j you add @Contract, @Pre and @Post conditions to the methods of your Java classes and interfaces:

    @Contract
public class SimpleBankAccount {

@Pre(value="amount.doubleValue() >= 0.0d", message="Can only withdraw a postive amount.")
@Post(value="$_.doubleValue() >= 0.0d", message="Cannot go overdrawn.")
public BigDecimal withdraw(BigDecimal amount) {
this.balance = this.balance.subtract(amount);
return this.balance;
}
... // other members
}

The chex annotations act as additional documentation about your code. If you add chex to your interfaces then any class which implements your interface will have the logic injected into it at load-time. This will work even if it is 3rd party code where you do not have access to the source code.

Alternatively you can leave the annotation value property blank and chex4j will automatically resolve and invoke a $Pre or $Post method:

    @Contract
public class LimitedMaximumBankAccount {
@Pre(message="Negative deposit.") // invokes deposit$Pre(amount)
@Post(message="Balance exceeds 25k.") // invokes deposit$Post(amount)
public BigDecimal deposit(BigDecimal amount) {
this.balance = this.balance.add(amount);
return this.balance;
}

@SuppressWarnings("unused")
private boolean deposit$Pre(BigDecimal amount){
return amount.doubleValue() >= 0.0d;
}

@SuppressWarnings("unused")
private boolean deposit$Post(BigDecimal amount){
return this.balance.doubleValue() <= 25000;
}
... // other members
}


When you enable the chex4j javaagent within your IDE (or test server) the annotation value (the "chex") will be injected into the body of the method. Should the chex expression return false an assertion error will be thrown naming the chex which failed; this reflects the fact that it is a programmatic error to violate the constraint.

   
The syntax of the injected code can be any valid Java code. You can also use Javassist variables of the annotated method. A selection of useful method variables are shown below:  

$0, $1, $2, ...
Actual parameters by position (consider just using the method argument name)
$args
Array of parameters. The type of $args is Object[]
$$
All actual parameters. For example, m($$) is equivalent to m($1,$2,...)
$r
The return type. It is used in a cast expression.
$_
The resulting value (the anonymous return variable).

Running It - Online Mode
Chex4j supplies a javaagent which instruments classes at load-time using the JBoss Javassist toolkit. With online mode the Javassist jar has to be available when your classes are loaded.
   
The correct version of Javassist to use is named within the chex4j pom.xml. It can be made accessible to the chex4j javaagent with a JVM argument:

-Xbootclasspath/a:/some/path/to/javassist-x.y.z.jar


Note the "/a:" is not a drive letter it is the jvm flag meaning "append to boot classpath".

The chex4j javaagent is enabled with a VM parameter such as:

-javaagent:target/chex4j-core-1.0.2.jar=net.sf.chex4j...,chex4j.test...


where after the '=' is a comma separated list of package names for the javaagent to instrument. Note that you must add the "..." to each package name. This is the syntax used by the the standard JVM -ea flag.


Running It - Offline Mode

The javaagent is very useful when running your junit tests within an IDE as it will be immediate. If you want to deploy the code with the chex enabled (and without using a javaagent) you can use the ChexOfflineMain class as part of your build. The chex4j source code provides an example ant file "ant-compile-time-chex4j.xml" which runs ChexOfflineMain to bake the chex bytecode into the class files for easy deployment onto an application server

When running in offline mode there is no requirement for either a javaagent nor the javassist library. There will be a runtime dependency on the chex4j jar file to supply the assertion throwing behaviour.

 

Published at DZone with permission of its author, Simon Massey.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Mohamed El-beltagy replied on Tue, 2011/05/31 - 6:22am

Great introduction and framework.

Chex4j home page is here: http://chex4j.sourceforge.net/

Michael Schnell replied on Tue, 2011/05/31 - 7:20am

Check out Contracts for Java (Cofoja)

It's a rewrite of ModernJass that is based on JML, a very mature Design by Contract framework.

Vladimír Oraný replied on Tue, 2011/05/31 - 12:36pm

nice lib but GContracts does the same job in more straightforward way. and the ability to write contracts for interfaces is just amazing.

Simon Massey replied on Tue, 2011/05/31 - 1:47pm in response to: Vladimír Oraný

Does it require groovy?

Josh Chappelle replied on Fri, 2011/06/03 - 12:51pm

This looks like overkill for what you are getting and it could foster bad coding practices. In the first example you are putting variable names in a String so you are bypassing compile time checking. So if you do a local rename on your "amount" variable then you will get a runtime exception if you don't remember to change your Pre value to reflect that change. Because of how easy it is to write your own static utilities for this kind of thing I don't think it warrants adding another jar to the classpath and adding one more tool for developers on the team to have to learn how to use.

That's my two cents anyway.

Simon Massey replied on Wed, 2011/06/08 - 3:22pm in response to: Vladimír Oraný

@Vladimír As mentioned in the article "If you add chex to your interfaces then any class which implements your interface will have the logic injected into it at load-time. This will work even if it is 3rd party code where you do not have access to the source code." So chex4j has the amazing feature you advertise without the complexity or learning curve to use groovy to make things "simpler".

Simon Massey replied on Wed, 2011/06/08 - 3:29pm in response to: Josh Chappelle

@Josh thanks for your time for your considered response. The second snippet is exactly to suite what you say; so your IDE sees the variables. The first approach is a bit more "inline self documenting". It would not scale to a complex chex. We should have junit tests which test that the chex works; to protect against the risk you point out.

I would shy away from static methods as they are not very OO as Java lacks the extension method feature of C#. I would prefer a standard "assert Expression1 : Expression2 ;" expression which is a little known java language feature and use logic in the current class. Add -ea to the JVM flags and at class load time the assert expression is added and throws an AssertionError if the first expression fails passing the second expression result into the error constructor as the message. Anyone running without -ea and the class loader strips out the byte code. So you can enable it only in QA or your IDE.

Chex4j is the assert++. You add it to your interfaces and the assert is injected into any class at load time; even if you dont have the code which claims to impliment your interface. This interface feature is not possible normally with regular code nor with normal assert.

The chex is above the method to not clutter the body of the code with the optional guard logic which may not be enabled at runtime. Annotations are meta-data about your code which is the essence of a chex; optional enforcement is enabled at runtime (or build time with offline mode).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.