A blog about software development and other software related matters

Blog Archive

Monday, January 19, 2009

Injecting Closures with Groovy BeanBuilder

Programmatic Spring configuration has always appealed to me (can't wait for Spring JavaConfig to come out), the clarity and ability to cut down repetitive configuration meta data is the way to go.

Groovy eco system has got a lot of builders to configure any thing from build to you'v guessed it right Spring beans.
The BeanBuilder is a Groovy builder (google the pattern) class that enables us to define Spring beans in pure Groovy code, for example:


class AnyPojo {
def c
}

def builder=new BeanBuilder()
def value="yummy"
builder.anyPojo(AnyPojo){
c=value
}
def context = builder.createApplicationContext()
assert context.getBean("anyPojo").c.equals("yummy")

Note that an external value is injected into the bean (its not a spring bean), this might useful in cases that we want to pass behavior into our Spring beans like so:

def builder=new BeanBuilder()
def value={"yummy"}// returns "yummy"
builder.anyPojo(AnyPojo){
c=value
}
def context = builder.createApplicationContext()
assert context.getBean("anyPojo").c.call().equals("yummy")

This should work!, however upon running this code we get this exception:

.. Error creating bean with name '(inner bean)': Instantiation of bean failed; nested exception is java.lang.IllegalStateException: No bean class specified on bean definition

The builder thinks that we are trying to created a nested inner bean:

builder.anyPojo(AnyPojo){
c={ SomePojo p->
//..
}
}

No matter what as soon as the builder gets a closure as value for a property it fails.
The best solution that iv found (without altering the BeanBuilder code) is to wrap the closure in a delegating class like so:

class ClosureDelegate {
def c

ClosureDelegate(c){
this.c=c
}

def call(){
c.call()
}
}

def builder=new BeanBuilder()
def value=new ClosureDelegate({"yummy"})// the diff
builder.anyPojo(AnyPojo){
c=value
}
def context = builder.createApplicationContext()
assert context.getBean("anyPojo").c.call().equals("yummy")// this passes as well

This way we avoid the call to inner bean builder method (=(Closure c)).
This solution is partial (sometimes closures are applied without a call the the call method), but than again it should do it for most cases (oh and BTW you can get BeanBuilder without Grails from spring spring jar).

2 comments:

matt said...

I would add a method with an object array on it so you can pass variables like this:


class ClosureDelegate {
Closure c

ClosureDelegate(c){
this.c=c
}
def call(){
c.call()
}
def call(Object... args){
c.call(args)
}
}



this is quite annoying to have to do every time you need to pass a closure. surely the grails team can fix it..

ronen said...

Yeap adding an array is reasonable, maybe I should try taking a look into the builder source to figure out a patch.