Jevgeni Kabanov

An embedded Java DSL for manipulating hierarchical JavaBeans

April 26th, 2008 | by Jevgeni Kabanov |

Just yesterday I was thinking on how to improve the DSLs I’m working on at the moment and got an idea, which wasn’t too useful in the context of SQL, but could be used for something else.

Imagine we have three JavaBeans (I’m omitting getters/setters for readability):

JAVA:
  1. class Company {
  2.   String name;
  3.   Address address;
  4.   Collection<Employee> employees;
  5. }
  6.  
  7. class Address {
  8.   String country;
  9.   String zip;
  10.   String town;
  11.   String street;
  12. }
  13.  
  14. class Employee {
  15.   String firstName;
  16.   String lastName;
  17.   Address address;
  18. }

It’s a straightforward task to build a DSL for manipulating this data. It could look something like that:

JAVA:
  1. Company c = new CompanyBuilder()
  2.   .name(“ZeroTurnaround”)
  3.   .address()
  4.      .country(“Estonia”)
  5.   .finish()
  6.   .addEmployee()
  7.     .firstName(“Jevgeni”)
  8.     .lastName(“Kabanov”)
  9.     .address()
  10.       .country(“Estonia”)
  11.     .finish()
  12.   .finish()
  13.   .toCompany();

The trick is that the AddressBuilder hiding behind the address() call is used in two contexts — as an employee address and as a company address. In a large domain such situations are very common and we would like the corresponding builders to be reused.

What we can do is make every builder keep in mind it’s parent context like this:

JAVA:
  1. public class EmployeeBuilder<PARENT extends Builder>
  2.     implements Builder {
  3.   PARENT parent;
  4.  
  5.   public EmployeeBuilder(Employee e, PARENT parent) {
  6.     this.parent = parent;
  7.   }
  8.  
  9.   //Field setters …
  10.  
  11.   public AddressBuilder<EmployeeBuilder<PARENT>>
  12.       addAddress() {
  13.     return
  14.       new AddressBuilder<EmployeeBuilder<PARENT>>(this);
  15.   }
  16.  
  17.   public PARENT finish() {
  18.     return parent;
  19.   } 
  20. }
  21.  
  22. public class AddressBuilder<PARENT extends Builder>
  23.     implements Builder {
  24.   PARENT parent;
  25.  
  26.   public AddressBuilder(Address a, PARENT parent) {
  27.     this.parent = parent;
  28.   }
  29.  
  30.   //Field setters …
  31.  
  32.   public PARENT finish() {
  33.     return parent;
  34.   } 
  35. }

This way the generic type becomes nested and holds the whole path to the root of the hierarchy in a typesafe way. Of course we also need to return the actual value independently of the parent, but it’s easy to do by passing a corresponding closure from the parent and calling it in the finish:

JAVA:
  1. public class AddressBuilder<PARENT extends Builder>
  2.     implements Builder {
  3.   //…
  4.  
  5.   interface Closure {
  6.     void address(Address address);
  7.   }
  8.  
  9.   public PARENT finish() {
  10.     addressClosure.address(address);
  11.     return parent;
  12.   } 
  13. }

This trick can be reused for any kind of hierarchical composition — JavaBeans, XML and so on. For XML you could generate the DSL from the Schema, which is a relatively painful process. However for JavaBeans you can generate a builder per bean in a very straightforward way from the reflection information. Such a builder could be used for modifying JavaBeans as well as for constructing them.

It would most probably be simplest to output the bytecode directly and generate .class files or JAR for this DSL, which makes it an ideal test case for the typesafe bytecode engineering DSL library I’m working on :) Most likely I’ll implement this language as an example of using typesafe bytecode engineering, so if you’re interested keep an eye on the blog.

  • Jevgeni Kabanov
    Guillaume: IDE will help wonderfully, since if you forget the finish() method the builder type will be wrong and you won't be able to assign the result to Company (or whatever it is you are building). Will Groovy help against me mistyping "adress"?
  • An IDE won't help you if you forget the finish() method, so autocompletion isn't enough. I guess neither solutions are ideal, and both have pros and cons! I'd still favour Groovy, obviously (I'm biased though), as you can go much further in terms of DSLs as you could do in Java.
  • Jevgeni Kabanov
    Guillaume: Groovy is very nice, but I love my autocompletion :) I think that static languages have a lot of advantage from this point, because the IDE shows you the DSL syntax at all time and you just have to select the next choice. Groovy is definitely more elegant, but it's a tradeoff, not a clear win.

    I must admit, that the lack of finish() method is nice. I could get rid of it with closures, but the anonymous classes just look too ugly at the moment to consider.
  • You may perhaps be interested in having a look at what Groovy and Grails have to offer in this area, with the ObjectGraphBuilder and the DomainBuilder:
    http://groovy.codehaus.org/ObjectGraphBuilder
    http://grails.org/DomainBuilder
    These work without the need of any finish() method, for instance, or without the need of creating a specific class for any new JavaBean object graph.
  • Bártfai Tamás
    The day when you posted this I implemented a similar ad-hoc DSL. Comment from my co-worker: "Builder pattern for President".
    Good stuff.
blog comments powered by Disqus