I'm a Software Developer with over a decade's worth of experience in the IT Industry. While primarily a Java Developer I've working in - among other things - everything from C to Visual Basic to JavaScript to Ruby to Objective-C to C#. I officially consider myself a member of the cult of Lisp which tends to colour the way I think about how code should be written Julian is a DZone MVB and is not an employee of DZone and has posted 11 posts at DZone. View Full User Profile

Using Google Contracts for Java with IntelliJ IDEA

02.21.2011
| 7070 views |
  • submit to reddit

The first step after obtaining the Google Contracts for Java Jar and adding it to your project, is to enable Annotation processing support in IDEA.

To do this go to open the Settings window (IntelliJ IDEA > preferences on Mac) and go to Compiler > Annotation Processors.

Now do the following steps:

  • Check the Enable Annotation processing option.
  • Select the Obtain processors from project classpath option.
  • In the Processed Modules table click the Add button and select the Module for which you want to enable Contracts for.
  • Hit Apply and you are ready to rock.


To test this you can add a basic contract annotation to a method in a class, here is one that I created using the @Requires annotation to ensure that the integer method parameter "c" has a value greater than zero:

import com.google.java.contract.Requires;

public class TestSomeContracts {

@Requires({"c > 0"})
public void testContract(int c) {

}

}

Now when you compile you wont get much feedback as to wether the annotation was processed or not, as the contract syntax is correct, so lets modify it a bit to generate a compile splat by changing the variable name of the precondition:

@Requires({"cs > 0"})
public void testContract(int c) {

}

When you build the module, you will now get a compilation failure which kinda integrates into IDEA, in that you can click on the error in the messages window and it will take you to the line that failed. Unfortunately IDEA wont highlight the line in your Editor or anything fancy like what you get in Eclipse, but it's good enough to work with.



Using a class with contracts.

After reverting and compiling the class I created a simple test case to test the contract by passing in data that violates the contract (ie. an integer less than 1):

import org.junit.Test;

public class TestSomeContractsTest {


@Test
public void testContract() {
new TestSomeContracts().testContract(-1);
}

}

I run the test… and... it passes!

In order to actually work, Google Contracts needs to do some bytecode shenanigans in order to actually enforce the contract definitions during runtime. Currently they have two modes of operation, an offline instrumenter which is a post compilation processor which weaves in the contracts into a compiled class, and a java agent.

For development the most convenient method to use is the java agent.

To use the agent in IDEA, click the Select Run/Debug Settings drop down and select the Edit configurations option. Expand the defaults entry and select the JUnit option (or TestNG or just plain Application) and add the following to the VM parameters field:

-javaagent:[path to the Google Contracts for Java jar file]

I Also remove an existing configuration so that it picks up the new option when I run the test again.

Now I run the test again and - voila - nice big splat when I run the test and pass invalid data to the method:

com.google.java.contract.PreconditionError: c > 0
at TestSomeContracts.com$google$java$contract$PH$TestSomeContracts$testContract(TestSomeContracts.java:9)
at TestSomeContracts.testContract(TestSomeContracts.java)
at TestSomeContractsTest.testContract(TestSomeContractsTest.java:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)

 

On a final note. Google contracts is still pretty fresh, so here's hoping IDE support will improve if the project takes off. I must also say that I'm not too fond of the current mechanisms for enforcing the contract. Hopefully Google might take a page from Lombok and do to weaving at compile time.

 

From http://dotneverland.blogspot.com/2011/02/using-google-contracts-for-java-with.html

Published at DZone with permission of Julian Exenberger, author and DZone MVB.

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

Tags: