- You invoke iContract, a command line tool written in Java, by starting the class
com.reliablesystems.iContract.Tool
. - The argument
-minv,pre,post
specifies that we want to instrument our code with invariants and preconditions as well as postconditions. - The
-b"javac -classpath %CLASSPATH%;src"
option specifies the command to use to compile Java classes before iContract instruments them. iContract needs to do that because it uses Java reflection to extract some of the properties of your Java classes. So in order to do its job, it first needs a compiled version of the uninstrumented Java class. - The
-c"javac -classpath %CLASSPATH%;instr"
option specifies the Java compiler command to compile the instrumented Java files. - The
-n"javac -classpath %CLASSPATH%;_contract_db;instr"
option specifies the Java compiler to compile the repository classes. iContract uses a set of Java classes as a repository to resolve inheritance and interface implementation relationships. - The option
-oinstr/@p/@f.@e
specifies the location of the instrumented Java files.
As you can see, iContract commands can be quite involved, so it's best to create a small build script for every project.
After you execute that command, we will have two versions of the compiled class files of our sample project. The src
directory will contain the uninstrumented class, while the instr
directory will contain the instrumented version.
We can now run our example, then see the assertions in action. Let's run the uninstrumented version first:
$ java -cp ./src StackTest Exception in thread "main" java.util.NoSuchElementException at java.util.LinkedList.getLast(LinkedList.java:107) at StackImpl.top(StackImpl.java:24) at StackImpl.pop(StackImpl.java:17) at StackTest.main(StackTest.java:14)
As you can see, something went terribly wrong! Let's see if the instrumented code can shed some light.
$ java -cp ./instr StackTest Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: src/StackImpl.java:22: error: precondition violated (StackImpl::top()): (/*declared in Stack::top()*/ (!isEmpty())) at StackImpl.top(StackImpl.java:210) at StackImpl.pop(StackImpl.java:124) at StackTest.main(StackTest.java:15)
Well, that's better, isn't it? We now know exactly what went wrong. The precondition !isEmpty()
of the StackImpl.top()
method was violated. We were calling top()
on an empty Stack
.
Conclusion
I hope that you feel our little journey into DBC in Java was worthwhile. You should have a good idea of how useful iContract as a tool is to your development efforts.
Please be aware that iContract is still in beta (and has been for some time). Some bugs in the tool are laid out on the iContract Webpage.
You'll want to check out a number of iContract supporting tools:
- iDoclet integrates iContract assertions into the Javadoc documentation
- iDarwin builds on top of iContract to add constraints at the architectural level of software projects
This story, "iContract: Design by Contract in Java" was originally published by JavaWorld.