Mapstruct: Ordering of mapstruct and lombok gradle annotationProcessor seems to matter

Created on 10 Aug 2018  路  6Comments  路  Source: mapstruct/mapstruct

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")

Most helpful comment

@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?

All 6 comments

The order should not matter. However, I think that the problem with the builders is https://github.com/rzwitserloot/lombok/issues/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.

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 {
    }
}

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

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

@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?

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.

Was this page helpful?
0 / 5 - 0 ratings