How to configure Lombok extension to play well with VSCode
26/7/22: Added VSCode Java July Update
15/1/22: Update to fix a typo
Update:
With the recent release of Language Support for Java(TM) by Red Hat 1.9, it now officially support lombok
and Lombok Annotations Support for VS Code is no longer required.
Refer to VSCode Java July Update and VSCode Java Lombok Support for more information
Background
I was trying to figure out how to use an immutable object using Lombok @Value and @Jacksonized to play well with Jackson
in spring-boot
using VSCode
This is the sample code
@Value
@Builder
@Jacksonized
public class Flight {
private String id;
private String destination;
private int flightTime;
private int flightDuration;
}
@RestController
public class FlightController {
@PostMapping("/flights")
public Flight create(@RequestBody Flight flight) {
return Flight.builder()
.id(UUID.randomUUID().toString())
.destination(flight.getDestination())
.flightTime(flight.getFlightTime())
.flightDuration(flight.getFlightDuration())
.build();
}
}
However, I kept getting the following error which I should not be getting
2022-01-06 01:13:01.899 DEBUG 14672 --- [nio-8080-exec-2] o.s.web.method.HandlerMethod : Could not resolve parameter [0] in public com.trial.lombokjackson.Flight com.trial.lombokjackson.FlightController.create(com.trial.lombokjackson.Flight): Type definition error: [simple type, class com.trial.lombokjackson.Flight]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.trial.lombokjackson.Flight` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 2, column: 5]
2022-01-06 01:13:01.902 DEBUG 14672 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Failed to complete request: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.trial.lombokjackson.Flight]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.trial.lombokjackson.Flight` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 2, column: 5]
2022-01-06 01:13:01.907 ERROR 14672 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.trial.lombokjackson.Flight]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.trial.lombokjackson.Flight` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 2, column: 5]] with root cause
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.trial.lombokjackson.Flight` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 2, column: 5]
// omitted the rest of the stack trace
@Jacksonized
is supposed to play well with @Builder
as mentioned in the docs
It automatically configures the generated builder class to be used by Jackson's deserialization
Attempt to fix
I tried to fall back to use @JacksonDeserialize
instead of @Jacksonized
to see if works
@Value
@Builder
@JsonDeserialize(builder = Flight.FlightBuilder.class)
public class Flight {
private String id;
private String destination;
private int flightTime;
private int flightDuration;
@JsonPOJOBuilder(withPrefix = "")
public static class FlightBuilder { }
}
And Voilà! it works perfectly, the error wasn't there anymore, and it deserializes as expected.
What happened?
So what happened? Based on the documentation for @Jacksonized
, it is supposed to reduce the boilerplate code, and replace @JsonDeserialize, @JsonPOJOBuilder, ...
and play well with @Builder
After trying for hours, I still wasn't able to figure it out and so I posted on stackoverflow to see if I am missing out on anything obvious, and the first comment triggered me to look into vscode-lombok-extension. So I went to vscode-lombok-github-issues to search for any similar issue (which I should have done way earlier), and I came across this, this and some others
TLDR; vscode-lombok
extension comes with v1.18.12
instead of the latest stable version (v1.18.22
) and @Jacksonized
was only added from v1.18.14
onwards
Solution
The fix was pretty straightforward once we know that it was caused by an older version of Lombok
. There are a couple of ways to resolve this:
#1 Replace Lombok jar
This solution is the recommended one and should be the easiest, as it only involves replacing the jar file and restarting of VSCode
- Exit
VSCode
- Download the latest stable version from lombok
- Navigate to
%userprofile%\vscode\extensions\gabrielbb.vscode-lombok-1.0.1\server
- delete the existing
lombok.jar
- copy the downloaded
lombok.jar
into the extension directory
- delete the existing
- Start
VSCode
- Press
F1
orCtrl + Shift + P
and selectJava: Clean Java Language Server Workspace
- Restart
VSCode
when prompted
Note that if the extension will to be updated, it might point to the older lombok version again, so you would have to repeat the same process again
#2 Configure java.jdt.ls.vmargs
This involves tweaking the current java.jdt.ls.vmargs
configuration to point to the latest lombok jar file in another directory
- Open
.vscode/settings.json
in the project directory - Add
"java.jdt.ls.vmargs": "-javaagent:/path/to/latest/lombok.jar
- Restart
VSCode
when prompted
If it does not work, you may need to run this step
- Press
F1
orCtrl + Shift + P
and selectJava: Clean Java Language Server Workspace
- Restart
VSCode
when prompted
Note:
java.jdt.ls.vmargs
configuration in the vscode global setting will also be updated to include the new setting
// original
"java.jdt.ls.vmargs": "-javaagent:\"c:\\Users\\Joseph\\.vscode\\extensions\\gabrielbb.vscode-lombok-1.0.1\\server\\lombok.jar\"",
// after
"java.jdt.ls.vmargs": "-javaagent:./server/lombok.jar -javaagent:\"c:\\Users\\Joseph\\.vscode\\extensions\\gabrielbb.vscode-lombok-1.0.1\\server\\lombok.jar\"",
However, if you removed the configuration from .vscode/settings.json
, the global settings will not be updated to remove the path. If the file no longer exist in the path, you will encounter error message The Language Support for Java server crashed 5 times in the last 3 minutes. The server will not be restarted.
. In that case, launch global settings and remove the added path to return to the original
configuration
#3 Run via build tool (gradle/maven/java)
Start the application using the following command
./gradlew bootRun
mvn spring-boot:run
java -jar myapp.jar
This will bypass the VSCode
and launch as the application where the lombok
version should be the latest. At the time of writing, I am using spring-boot-2.6.2
which comes with lombok-1.18.22
Lesson learned
Always remember to check the lombok
version installed for the IDE when encountered with weird behavior especially using newer feature(s). This applies to Eclipse
as well since lombok jar
is also installed and using an outdated lombok
would have caused the same issue