Eat, Code, Love.

Generating Java Code With Maven, QDox and StringTemplate

Introduction: why would you ever want to generate Java code?

In my current project at work we have quite a few value objects with
giant constructor methods; these behemoths take 20 to 30 arguments
each and it’s crucial that you get all of them in the right order,
which is a pain in those cases when you’re only really interested in
setting one or two of them. Just as an example, let’s just say that
these classes looks something like this:

publicclassValueObj{

privateintvalue1;

privateintvalue2;

privateintvalue3;

...

privateObjectobj1;

privateObjectobj1;

...

publicValueObj(intvalue1,intvalue2,intvalue2,...Object

obj1,Objectobj2,...){

this.value1=value1;

this.value2=value2;

this.value2=value3;

...

this.obj1=obj1;

this.obj2=obj2;

...

}

publicintgetValue1(){

returnthis.value1;

}

...(othergettermethodsfollow)

}

About a year ago or so we started using the
Builder pattern, creating a separate Builder class for
each value object class. The builder has one setter method for each
parameter, and one create() method that instantiates the new object.

publicclassValueObjBuilder{

privateintvalue1;

privateintvalue2;

privateintvalue3;

...

privateObjectobj1;

privateObjectobj1;

...

publicValueObjBuildervalue1(intvalue1){

this.value1=value1;

returnthis;

}

publicValueObjBuildervalue2(intvalue2){

this.value2=value2;

returnthis;

}

...

publicValueObjcreate(){

returnnewValueObj(value1,value2,...);

}

}

Since the setter methods return a reference to the builder itself it
is possible to chain invocations:

ValueObjobj=newValueObjBuilder().value1(23).value2(47).create();

However convenient these builder classes are to use, I very quickly
tired of maintaining them. Every time you add or remove a property to
a value object, you also need to make the corresponding change to the
builder. Keeping two classes in sync doesn’t seem like too much work,
but it is unnecessary, and it occurred to me that I could probably
write something to generate the builders automatically.

Writing a Maven plugin

We use Maven for our builds. Ok, ok, I know. Maven is horrible,
maven downloads the whole universe, maven is braindead. Yes, I know
all these things. But here’s the ting: Maven is like democracy: It’s
not perfect, but it’s the best we have.

One of the nice things about maven is that it has a well-defined
standard build lifecycle. The build process is divided into steps, and
during one of the steps, generate-sources, a maven plugin has the
opportunity to generate source that will then be compiled during the
compile step.

For each matching constructor, generate a new source file under
target/generated-sources/builderbuilder/ containing the Builder
class.

The plugin is invoked before the compilation step, meaning that all
the generated code will be included in the final output.

As an example, this code (in src/main/java/example/ValueObj.java):

packageexample;

publicclassCat{

privateStringname;

privateintage;

/**

* @builder

*/

publicCat(Stringname,intage){

this.name=name;

this.age=age;

}

}

Will generate the following (in
target/generated-sources/builderbuilder/example/CatBuilder.java) when run through our plugin:

packageexample;

publicclassCatBuilder{

privateStringname;

privateintage;

publicCatBuildername(Stringname){

this.name=name;

returnthis;

}

publicCatBuilderage(intage){

this.age=age;

returnthis;

}

publicCatcreate(){

returnnewCat(name,age);

}

}

For those of you who like to read along, the code for the whole plugin
is on github. The finished version has some extra bells and
whistles, like the ability to generate abstract builder classes and
explicitly the name of the create() method.

Parsing Java code

Okay, so let’s say we want to pick apart the java class files and for
each constructor found we want to generate a helper class as discussed
above. Now, normally we’d use the introspection facilities already
built into java, but now we have one problem: our code-generating code
will run during the generate-sources step, before any compilation
has actually taken places.

This is a problem. What we need is a java parser that can pull apart
the relevant bits of the source files and give us enough information
that so we can generate the new classes. Preferably the level of
detail should be on par with with that of the java.util.reflect.*
functions, and it should also be fast. I actually started writing
something like that, but that’s another story. Instead,
let’s check out QDox, an amazing little library used
internally by maven.

Using it is pretty simple. First, create a new JavaDocBuilder object
and tell it where the source code you want to parse lives:

this.docBuilder=newJavaDocBuilder();

for(Stringr:sources){

docBuilder.addSourceTree(newFile(r));

}

Then loop over all known classes and generate the builder:

for(JavaClassjc:docBuilder.getClasses()){

generateBuilderFor(jc);

}

Generating the output

This is the generateBuilderFor() method:

publicvoidgenerateBuilderFor(JavaClassjc)throwsIOException{

for(JavaMethodm:jc.getMethods()){

if(m.isConstructor()){

DocletTagdc=m.getTagByName("builder");

if(dc!=null){

StringbuilderName=dc.getNamedParameter("name");

if(builderName==null)

builderName=jc.getName()+"Builder";

StringpackageName=dc.getNamedParameter("package");

if(packageName==null)

packageName=jc.getPackageName();

StringTemplatest=templates.getInstanceOf("builder");

st.setAttribute("packageName",packageName);

st.setAttribute("builderName",builderName);

st.setAttribute("resultClass",jc.asType().toString());

List<Param>ps=newLinkedList<Param>();

for(JavaParameterp:m.getParameters()){

ps.add(newParam(p.getType().toGenericString(),p.getName()));

}

st.setAttribute("parameters",ps);

Filepd=newFile(outputDirectory,packageName.replaceAll("\\.","/"));

pd.mkdirs();

FileWriterout=newFileWriter(newFile(pd,builderName+".java"));

try{

out.append(st.toString());

}finally{

out.flush();

out.close();

}

}

}

}

}

publicstaticclassParam{

publicfinalStringtype;

publicfinalStringname;

publicParam(Stringtype,Stringname){

this.type=type;

this.name=name;

}

}

This is all pretty straightforward; we loop through the methods for
the class, looking for a constructor that has the @builder javadoc
tag.

Lines 6-12 checks if the tag has any values specified for name
and package, reverting to default values if not.

Lines 14-23 instantiates a new StringTemplate object and supplies it
with values for the packageName, builderName and resultClass
attributes (the latter being the class that we want the builder to
actually build). Lines 19-23 loops through the constructor arguments
and creates value object holding the name & type of each.

Finally, a new file is created (lines 25-26) and written (lines
28-34).

Templating

There are an abundance of templating frameworks out there. I choose to
go with StringTemplate for this project, since it makes it easy
to use subtemplates in a functional way, and generally fits my way of
thinking. I could just as easily have used something like
FreeMarker or Velocity, though.

The templates are not that interesting, so I won’t go into it
here. You can check them out on github if you are curious.

Conclusion

Automatic code generation in java projects is not only feasible, but
also quite convenient with maven. We’ve been using BuilderBuilder
internally in production for a few months now, and it greatly cuts
down on code maintenance.

It would be nice, though, if there was a more general approach to code
generation. Just for BuilderBuilder, I had to dive into Maven
internals (which are surprisingly poorly documented), learn
QDox (ditto) and write all the java code that solved this
particular problem. Imagine instead if there was some kind of
transformation language that operated on java sources, kind of like
what XSLT does for XML? Now, that would be cool.