Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ordering of mapstruct and lombok gradle annotationProcessor seems to matter #1581

Closed
kaweston opened this issue Aug 9, 2018 · 14 comments
Closed

Comments

@kaweston
Copy link

kaweston commented Aug 9, 2018

In a work project I have to ensure that the lombok annotationProcessor directive is listed after mapstruct otherwise various errors occur. Identical behaviour whether running gradle from command line or via the gradle window in Intellij.

// Good setup
dependencies {
   compile("org.mapstruct:mapstruct-jdk8")

    annotationProcessor("au.com.agic.api:commons-mapping")
    annotationProcessor("org.mapstruct:mapstruct-processor")
    annotationProcessor("org.projectlombok:lombok") <<=== Lombok after mapstruct

    testAnnotationProcessor("au.com.agic.api:commons-mapping")
    testAnnotationProcessor("org.mapstruct:mapstruct-processor")
    testAnnotationProcessor("org.projectlombok:lombok")

    // Other dependencies

}

// Error setup
 dependencies {
   compile("org.mapstruct:mapstruct-jdk8")

   annotationProcessor("org.projectlombok:lombok") <<=== Lombok before mapstruct
    annotationProcessor("au.com.agic.api:commons-mapping")
    annotationProcessor("org.mapstruct:mapstruct-processor")
   
   testAnnotationProcessor("org.projectlombok:lombok")
    testAnnotationProcessor("au.com.agic.api:commons-mapping")
    testAnnotationProcessor("org.mapstruct:mapstruct-processor")
    

    // Other dependencies

}

Sample of errors encountered. Not sure if this matters but both TravelWhatIfPrice and ContactDetails use lombok Builder annotations although the Builder class are already defined without methods so that I can use the Jackson library's @JsonPOJOBuilder(withPrefix="") annotation to match with lombok's setter naming convention.

......CsApi2TravelWhatIfPricesMapper.java:11: error: au.com.agic.api.travel.domain.TravelWhatIfPrice does not have an accessible parameterless constructor.
TravelWhatIfPrice toModel(final CsApi2TravelWhatIfPrice csApi2TravelWhatIfPrice);

....... CsApi2ContactDetailsMapper.java:23: error: Property "id" has no write accessor in au.com.agic.api.travel.domain.ContactDetails.
@mapping(target = "id", source = "id")

@filiphr
Copy link
Member

filiphr commented Aug 18, 2018

The order should not matter. However, I think that the problem with the builders is projectlombok/lombok#1538. During compilation we can't see the builder and the builder methods from Lombok. If you split the Lombok and MapSruct mappers into 2 different modules then it is going to work. You can chime in on the linked Lombok issue.

What do you mean with

Builder class are already defined without methods so that I can use the Jackson library's @JsonPOJOBuilder(withPrefix="") annotation to match with lombok's setter naming convention.

@kaweston
Copy link
Author

kaweston commented Aug 19, 2018

Some of these classes are de/serialized using the Jackson library. This is done using a combination of @JsonDeserializer annotation on the enclosing class and optionally the JsonPOJOBuilder on the builder class. By default Jackson expects builder class methods to have "with" as the prefix but this can be overridden to have no prefix by using annotating the builder with @JsonPOJOBuilder(withPrefix = ""). This means of course that to use the JsonPOJOBuilder annotation you need to supply the class declaration and have lombok add the rest of the details.

Using this approach also matches Mapstructs DefaultAccessorNamingStrategy behaviour which also doesn't expect any prefix.

As I said in the initial comment, it probably make no difference but I thought I'd supply as much contextual info as possible, just in case.

An example:

package au.com.agic.api.travel.dto.request;

import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@JsonDeserialize(builder = TravelDatesRequest.Builder.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@Builder(builderClassName = "Builder")
public final class TravelDatesRequest {
    private final LocalDate departureDate;
    private final LocalDate returnDate;

    @JsonPOJOBuilder(withPrefix = "")
    public static final class Builder {
    }
}

@filiphr
Copy link
Member

filiphr commented Aug 20, 2018

Thanks for providing more information. I'll dig into it to see what is happening. It seems that when you declare your builder there is something affecting the issue I linked. In any case I am fairly certain that the actual problem is the fact that MapStruct does not see the accessors of the builder, due to Lombok not exposing them

@AnkushNakaskar
Copy link

Hi @filiphr ,
I am also facing the same issue,Will you be able let me know, is it still in process?

@filiphr
Copy link
Member

filiphr commented May 4, 2019

@kaweston I checked this and for some reason when mapstruct is before lombok then we see all the builder methods, but when we are after lombok then we don't see the builder methods, but we do see the getters. I can't explain this. This is with the latest (1.18.6) version.

@AnkushNakaskar if you swap the order is it working? Which Lombok version are you using?

@reitzig
Copy link

reitzig commented Aug 4, 2020

We seem to have a similar issue. For us, this made the code compile (again):

image

Since we run mapstruct before Lombok already, this may be unrelated. If we find time, we'll provide an MWE in a separate issue.

mihaijulien added a commit to mihaijulien/spring-microservices that referenced this issue Apr 28, 2021
@hydra1983
Copy link

hydra1983 commented Nov 10, 2021

It does matter. @filiphr

Dependency order

// before
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.mapstruct:mapstruct-processor:${Versions.mapstructVersion}")

// after
annotationProcessor("org.mapstruct:mapstruct-processor:${Versions.mapstructVersion}")
annotationProcessor("org.projectlombok:lombok")

Generated code

// before
Task = new Task();

return task;

// after
TaskBuilder task = Task.builder();

task.id( source.getId() );
task.serialNumber( source.getSerialNumber() );

return task.build();

Lombok: 1.18.10
MapStruct: 1.4.2.Final

@filiphr
Copy link
Member

filiphr commented Nov 13, 2021

@hydra1983 the order should not matter. I would suggest upgrading Lombok and also adding the lombok-mapstruct-binding dependency as well. I tried your example in our mapstruct-lombok example and everything works fine.

I am going to close this until someone can provide an example project where this is reproducible

@filiphr filiphr closed this as completed Nov 13, 2021
@remal
Copy link

remal commented Nov 14, 2021

@filiphr I faced the same issue and fixed it with some Gradle tricks. Unfortunately, I can't provide you with an example because it's not an open-source project and Gradle config is really complex.

@hydra1983
Copy link

@filiphr Got the problem fixed. As it's caused by something like #2260. I ignored all the mappings, and set the value in decorator.

@hydra1983
Copy link

hydra1983 commented Mar 23, 2022

@remal FYI, this is my gradle kotlin configuration and it works fine. The order do matters.

// mapstruct + lombok
compileOnly("org.projectlombok:lombok:${Versions.lombokVersion}")
compileOnly("org.mapstruct:mapstruct:${Versions.mapstructVersion}")
annotationProcessor("org.projectlombok:lombok:${Versions.lombokVersion}")
annotationProcessor("org.mapstruct:mapstruct-processor:${Versions.mapstructVersion}")
annotationProcessor("org.projectlombok:lombok-mapstruct-binding:${Versions.lombokMapStructBindingVersion}")
java -version
openjdk version "1.8.0_265"
OpenJDK Runtime Environment (build 1.8.0_265-b01)
Eclipse OpenJ9 VM (build openj9-0.21.0, JRE 1.8.0 Mac OS X amd64-64-Bit Compressed References 20200728_646 (JIT enabled, AOT enabled)
OpenJ9   - 34cf4c075
OMR      - 113e54219
JCL      - c82ff0c20f based on jdk8u265-b01)
./gradlew --version

------------------------------------------------------------
Gradle 7.3.3
------------------------------------------------------------

Build time:   2021-12-22 12:37:54 UTC
Revision:     6f556c80f945dc54b50e0be633da6c62dbe8dc71

Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          1.8.0_265 (Eclipse OpenJ9 openj9-0.21.0)
OS:           Mac OS X 10.15.7 x86_64
const val mapstructVersion = "1.4.2.Final"
const val lombokVersion = "1.18.22"
const val lombokMapStructBindingVersion = "0.2.0"

@implermine
Copy link

Is there no further discussion? i've experienced same issue with @hydra1983 and solved problem by https://stackoverflow.com/a/68621947/16901221 , yet annotationProcessor's order has no matter?

@remal
Copy link

remal commented Mar 31, 2023

@hydra1983 I had to create my own Lombok Gradle plugin to fix it: https://github.com/remal-gradle-plugins/lombok

@hydra1983
Copy link

@hydra1983 I had to create my own Lombok Gradle plugin to fix it: https://github.com/remal-gradle-plugins/lombok

Cool, I will have a try. Thanks a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants