# 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](https://marketplace.visualstudio.com/items?itemName=redhat.java), it now officially support `lombok` and [Lombok Annotations Support for VS Code](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-lombok) is no longer required.

Refer to [VSCode Java July Update](https://devblogs.microsoft.com/java/java-on-visual-studio-code-update-july-2022/) and [VSCode Java Lombok Support](https://github.com/redhat-developer/vscode-java/wiki/Lombok-support) for more information

# Background

I was trying to figure out how to use an immutable object using [Lombok](https://projectlombok.org/) [@Value](https://projectlombok.org/features/Value) and  [@Jacksonized](https://projectlombok.org/features/experimental/Jacksonized)  to play well with `Jackson` in `spring-boot` using `VSCode`

This is the sample code

```java
@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

```log
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

```java
@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](https://stackoverflow.com/questions/70597244/using-lombok-jacksonized-throws-invaliddefinitionexception) to see if I am missing out on anything obvious, and the first comment triggered me to look into [vscode-lombok-extension](https://marketplace.visualstudio.com/items?itemName=GabrielBB.vscode-lombok). So I went to [vscode-lombok-github-issues](https://github.com/GabrielBB/vscode-lombok/issues) to search for any similar issue (which I should have done way earlier), and I came across [this](https://github.com/GabrielBB/vscode-lombok/issues/61), [this](https://github.com/GabrielBB/vscode-lombok/issues/62) 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](https://projectlombok.org/download)
- 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
- Start `VSCode`
- Press `F1` or `Ctrl + Shift + P` and select `Java: 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` or `Ctrl + Shift + P` and select `Java: 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

```json
// 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
