How Lombok can throw you off guard - Java Compiler fun

Working on a project on a customer of mine, I bumped into some very weird Lombok behavior which really caught me off guard. As it was caused by a not-so-easy to debug issue, I'd like to share with you all what happened and why.

Enter WTF

After making some changes to the Maven POM structure, and doing a compile of the code, maven reported the following errors:

[ERROR] SomeFile.java[20,43] cannot find symbol
[ERROR]    symbol: class __
[ERROR] SomeFile.java[17,32] cannot find symbol
[ERROR]    symbol: class __
[ERROR] SomeFile.java[21,33] cannot find symbol
[ERROR]    symbol: class __


[ERROR] SomeFile.java[78l,13] cannot find symbol
[ERROR]    symbol: method getValue()
[ERROR]    location: ....

Of course, I knew  we're using Lombok to generate Getter, setters etc for our classes, so for some reason Lombok was not being run, causing the @__ annotation still being present, and the getValue() method not being generated during compilation. What was even more odd was the fact that ANOTHER project with the same configuration (another micro-service) didn't have this issue. Typical WTF...

Image result for wtf

A debugging session brought me into the internals of the JavaCompiler (javax.tools and implementation classes stuff). Now, I've done my share of annotation processor creation in the past, so I thought I was up to the task, yet I didn't see this one coming.

The solution

In the end, after quite some debugging, I found the reason for this error was caused by the fact I misconfigured an annotation from MapStruct, another framework we use to generate mapping code between our domain (DDD) and JPA layer in the system. In a mapping configuration I had the following code:

@Mapping(source="...", ignore = true)
public abstract SomeClass mapTo(SomeOtherClass o)

Where MapStruct currently requires you to specify a target property on the class, so my annotation was just wrong. Fixing that, solved the issue and now Lombok is working fine again. And there was much rejoicing
Image result for much rejoicing

The Why

Ok, why was this the cause for these weird Lombok issues? The Java Compiler actually does a nifty trick while compiling especially with annotation processing. Basically the flow is as follows:

  1. Start parsing all the files and compiling them
  2. If there are unrecoverableErrors, stop and print error information
  3. Run all the annotation processors
The above sequence, is what's called a Round (you can see the RoundEnviroment being passed into the base class for annotation processors. The nifty trick is Java can deal with compile errors if an annotation processor can change the state of what's being compiled. Then basically the Java Compiler delays spamming you with errors and thinks "Meh, let's just hold off with me nagging to the developers, those nifty annotation processors might solve this issue".

Normally this is exactly what happens, when compiling with Lombok, we have the following Rounds:
  1. Compile all classes, whole bunch of errors occurred (recoverable ones), cause we call non-existing methods like our getValue().
    1. Annotation processors are being run on the current state of the compilation process, basically Lombok is doing it's stuff, altering the state of the compiler.
  2. Compile all change classes (those affected by Lombok, or new ones generated by the annotation processor (like @Builder). Now, the compiler errors are gone, cause getValue() is there.
Now clearly, this is where stuff broke when we had the issue in the @Mapping annotation. After Round 1, the Java Compiler encountered an unrecoverable error, and just spitted out all the errors which were found up to that point. Given the real cause (the unrecoverable error) is somewhere in the humongous pile of errors, it's quite easy to miss and these errors may lead you into thinking the Lombok configuration is wrong (which it isn't). 

Comments

  1. You just saved my day!

    I was just not looking into all error messages anymore cause they were to many and missed the actual problem.

    Now after reading your article I came back to the log and went through all errors carefully and found the actual issue.

    Good to know how the compiler actual works here.

    ReplyDelete

Post a Comment

Popular posts from this blog

Validations across micro-services

Bridging the world of Event Storming and BPMN

From Multi-Threaded applications to Multi-Time applications