Skip to content
This repository has been archived by the owner on Jan 7, 2020. It is now read-only.

GContracts in Spring and Grails Applications

andresteingress edited this page Nov 16, 2010 · 1 revision

One of the problems with GContracts is its lack of compatibility especially with Grails artifact classes, e.g. controllers, services, domain classes etc. This topic has been brought to me a few times and if you just want to apply let's say class invariants on Spring bean classes the Spring application container's initialization lifecycle can quickly be your enemy.

Let's consider the following service:

@Invariant({ anotherService != null })
class MyService {

    def anotherService

    static transactional = true

    // ...
}

MyService is a simple Grails service class, annotated with a class invariant which ensures that the reference to anotherService will never be null, during the entire life-time of the MyService bean. Sadly enough, whenever the Spring application container tries to create a new instance of a MyService bean it fails because GContracts checks the class invariant directly after the object's constructor has completed successfully.

From a theoretical perspective this is completely reasonable since an object's class invariant needs to be fulfilled by the object instance during its entire life-time. But from a practical perspective, this is horrible. It means that you have to duplicate the class invariant to all methods in the service class, e.g.

// avoid invariant checks during construction
class MyService {

    def anotherService

    static transactional = true

    @Requires({ anotherService != null })
    def myServiceMethod()  {
        // ...
    }

    @Requires({ anotherService != null })
    def yetAnotherServiceMethod()  {
        // ...
    }
}

It is not that Design by Contract can not be applied here, it is that Spring simply does define its own rules for object creation and destruction, meaning that whenever an object is created with its constructor the initialization process is not done in the case of the Spring application container.

This circumstance confuses programmers who simply want to apply DbC in their bean classes without thinking about Spring initialization mechanisms and assertion injection.

GContracts 1.1.3 is the starting point for a bunch of features which are targeted to iron out such integration issues. Of course it might be questionable whether it makes sense to focus primarily on the Spring framework, but that has been a practical decision (GContracts is for Groovy, Grails uses Groovy, Grails uses Spring...). Furthermore, I tried to make the integration as light as possible without actually introducing a classpath dependency on Spring framework classes.

The first feature which comes with 1.1.3 is a solution for the problem described above: class invariant checks on Spring beans. Henceforth, GContracts will not make class invariant checks on Spring beans, but will dynamically create a @PostConstruct method which overtakes the class invariant check. Since GContracts has no mechanism to know that the given class is a Spring-managed class at runtime, programmers need to annotate their artifact classes with appropriate stereotype annotations ([more]), e.g. a service class with @Service

@Service
@Invariant({ anotherService != null })
class MyService {

    def anotherService

    static transactional = true

    // ...
} 

The @Service stereotype annotation above triggers GContracts' modified Spring compliant mode. Whenever the service bean above has been constructed by the Spring application container it will automatically check the class invariant.

The programmer clearly needs to make a compromise in this case, because the class invariant won't be checked after a construction call anymore (e.g. in a test-case). But this is a consequence of handling object creation over to an application framework.

Hint: GContracts supports the -ea/-da VM parameters which you can use to deactivate assertion checking during application execution, whereas activating assertion checking only during test-cases.