May 22, 2016

Delombok: Enterprise-safe Java AST transformations



Project Lombok brings abstract syntax tree (AST) transformations to Java, allowing you to generate boilerplate code at compile time through simple annotations. With the delombok Maven plugin, this becomes a safe choice even for enterprise projects.

This blog post describes how to easily apply Java code generation for typical boilerplate tasks using Project Lombok – and how to safely introduce it to a Java project of any scale, in a non-intrusive way.

You’ll find the source code of a nicely configured (NetBeans) project in the accompanying GitHub repository.

Why should you use it?

Project Lombok allows you to use simple Java annotations to modify or extend Java source code on compile time. This is a feature well-known from Groovy projects, which comes with numerous so-called AST transformations out-of-the-box, to e.g. generate equals() / hashCode() methods, constructors, or even implement advanced features such as method-delegation. With Lombok, these features are brought to vanilla Java.

This is not about saving time when coding. That’s what static code generators (e.g. in the IDE) are for. Actual coding is done once only. Afterwards, you’ll need to be able to read your code, and to keep it up to date. This is were static code generation fails as the generated boilerplate becomes part of the source code file, and it gets in the way every time you have to revise is. Statically generated code by its very nature is potentially out-of-date as soon as it was generated. This never happens with code generated at compile time.

Code generation with Lombok

In order to use Lombok, you need to declare its Maven dependency:
<dependencies> 
 <dependency> 
  <groupId>org.projectlombok</groupId> 
  <artifactId>lombok</artifactId> 
  <version>1.14.8</version>
 </dependency>
 ...
</dependencies>
Note: At the time of writing (May 2016), this does not work with Lombok’s most recent release (1.16.8): When used in NetBeans (8.1), the IDE itself recognizes the code generated by Lombok (e.g. in autocomplete), but compilation will fail nonetheless when trying to access generated code. According to this stackoverflow answer, the current solution is a downgrade to Lombok 1.14.8.
Then you can for instance have getters and setters generated at compile time:
public class GetterSetterExample {
    @Getter @Setter
    private int age = 10;
 ...
}
and use them statically typed:
GetterSetterExample example = new GetterSetterExample();
example.getAge();

Why wouldn’t you use it?

Lombok helps you keep your Java code concise, DRY, and overall clean.

There are still viable reasons why you may be still reluctant to apply it in your project: It introduces a dependency on a third party tool, and in this case, this comes with two drawbacks:
  • A new programming style is introduced which may not be endorsed by future code maintenance personal.
  • You become dependent on a tool which may break with future Java releases.
  • (Plus, its current version indeed seems to be broken at the moment, at least when used in NetBeans.)
The second point becomes a pressing concern when you read Lombok’s backstory which clearly states that it actually relies on internal VM APIs and as such may be considered a hack:

“There is no guarantee the private APIs won't change in a later JDK and break Project Lombok.”

It does so by lack of official support for annotation processors for Java:

“If what lombok does could be done without resorting to internal API, we'd have done something else, but it can't be done, so we resort to internal API usage.”
At least, it would be favorable to have a “revert to vanilla” option with Lombok. Luckily, there is one. Enter Delombok, which is an official part of Lombok.

Safe code generation with Delombok

In order to activate Delombok, you need to add its plugin to your pom:
<plugins>
 <plugin>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok-maven-plugin</artifactId>
  <version>1.16.8.0</version>
  <executions>
   <execution>
    <phase>generate-sources</phase>
    <goals>
     <goal>delombok</goal>
    </goals>
   </execution>
  </executions>
 </plugin>
 ...
</plugins>
Note that this method supports the most recent Lombok release (even in NetBeans):
<dependencies> 
 <dependency> 
  <groupId>org.projectlombok</groupId> 
  <artifactId>lombok</artifactId> 
  <version>1.16.8</version>
 </dependency>
 ...
</dependencies>
You should then create a separate source folder to you Java files on which you want to apply Lombok AST transformations:
<resources>
    <resource>
        <directory>src/main/lombok</directory>
        <includes>
            <include> **/*.java </include>
        </includes>
    </resource>
</resources>
That way, you get a project structure like this:
  • Project
    • src
      • java: Source code to which Delombok is not applied (optional)
      • lombok: Source code to which Delombok is applied
    • target
      • generated-sources
        • lombok: Source code generated by Delombok
You’ll need to create the src/lombok folder by hand. NetBeans IDE will then automatically detect this resources directory as an additional source folder and the <plugin>’s <phase>generate-sources</phase>/<goal>delombok</goal> sub-folder as an additional “Generated Sources” folder.
Then, apply Lombok AST transformations in your src/lombok code and use it statically typed, same as above.

Note that in this case, the modified source code is indeed placed into the target/generated-sources/lombok folder.

Because the src/lombok folder is not one of Maven’s source class folders, there will be no “duplicate class” errors, whilst you’re still perfectly able to reference “delomboked” classes from “untouched” ones, and vice versa.

Of course, this configuration is not recommended for the actual development process as the additional step of generating “delomboked” source code introduces additional complexity and, more importantly, a compile time penalty. Rather, take it as your safeguard in case you’ll ever need to revoke your decision to opt for Lombok in a project: Then, delombokify your code again, and go on with manual coding. What a pity!

Before coming to a decision, you may also want to check readability of the actual code generated during a delombok run: I found that at least for the most prominent AST transformations, namely getters / setters or contructor generation, it doesn’t look at all worse than what your average IDE generates. Also not that the output of lombok’s AST transformations are highly configurable.

Conclusion

I’m a huge advocate of the strategy to generate all non-trivial code artifacts. I do believe that creating less code is better, in accordance with DRY, as long as KISS is respected. That’s part of the reason why I also like Groovy very much, and its AST transformations in particular, and that’s as well why I wholeheartedly recommend Lombok for Java projects.

Since it’s both easy and safe to apply, there’s really hardly any reason to miss this opportunity for a clean code boost.

Again, you can download the NetBeans project skeleton from its GitHub repository. Note that this project comes in two branches:
  • The delombok branch (also the HEAD revision): A demo setup for use with delombok.
  • The lombok branch: A demo setup for use with lombok.
Let me know in the comments section whether you agree with my assertions, or post what other reservations against Lombok you have found.

You may also be interested in