0

I've got an up-the-middle Spring Boot + Lombok project that works like a champ from the IDE but errors strangely when I run it from the command line through mvn spring-boot:run. Its a pretty recent version Spring Boot...

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

Lombok is unremarkable and came from the Spring Initializr thing (https://start.spring.io/).

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

The JavaBean its complaining about is equally boring...

import lombok.Builder;
import lombok.Data;

import java.math.BigDecimal;

@Data
@Builder(toBuilder = true)
public class TemplateLineItemInput {
  private final String partMasterId;
  private final String partMasterIdPath;
  private final String actionType;
  private final BigDecimal actionQuantity;
}

The API of this Boot project is GraphQL but when I execute the following mutation from a mvn spring-boot:run invocation it always comes back as an error (nothing on the console...the framework is kicking it out somehow).

Request...

mutation createTemplateLineItem($tbomId: ID!) {
  createTemplateLineItem(
    tbomId: $tbomId
    input: {
      partMasterId: "2"
      partMasterIdPath: "808863036.1"
      actionType: "ADD"
      actionQuantity: "2"
    }) {
    ...TBomFrag
  }
}
...
{
  "partMasterId": "5025489768",
  "tbomId": "a4688d22-9d99-41a2-9777-6acc75b2aab9",
  "lineItemId": "9e460199-34fb-432c-b971-8cd8321d3283"
}

Response...
{
  "errors": [
    {
      "message": "No primary or single public constructor found for class aero.blue.ems.boa.domain.TemplateLineItemInput - and no default constructor found either",
      "locations": [
        {
          "line": 20,
          "column": 3
        }
      ],
      "path": [
        "createTemplateLineItem"
      ],
      "extensions": {
        "classification": "INTERNAL_ERROR"
      }
    }
  ],
  "data": {
    "createTemplateLineItem": null
  }
}

My spring-boot-maven-plugin is configured like...

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.4</version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <configuration>
                            <classifier>exec</classifier>
                        </configuration>
                    </execution>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

...also from Spring Initializr

When I run the Application from the IDE directly, no issue. The mutation works fine.

Is there something missing from my spring-boot:run config in the pom.xml or something? Did I have to clue the plugin into annotation processing? This is really confusing.

Bob Kuhar
  • 10,838
  • 11
  • 62
  • 115
  • Oh....my...a clue. https://stackoverflow.com/questions/34241718/lombok-builder-and-jpa-default-constructor My `@Builder` usage may be dorking up the Constructor Lombok is building such that Jackson can no longer deal with it. "Delombok" is useful here. I _did_ indeed have no public constructors. Why did it work in the IDE? Maybe no matter....I know Jackson needs stuff public – Bob Kuhar Oct 06 '21 at 00:07

2 Answers2

0

Please,

Check the following config on section plugins at you pom.xml project, as following here:

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>getting-started</artifactId>
    <!-- ... -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

For more information about, check this link: https://docs.spring.io/spring-boot/docs/2.5.4/maven-plugin/reference/htmlsingle/

marcosnasp
  • 43
  • 6
  • Interesting. My configuration of that plugin was excluding lombok. I took it out, though, but the problem persists. I added my current config to the question. – Bob Kuhar Oct 05 '21 at 19:53
  • @BobKuhar First of all, Are you having the same issue, when you execute java -jar command on the jar generated package after maven package phase, on that located on target folder: target/foo-0.0.1.jar? Secondly, According with docs of lombok, :https://projectlombok.org/api/lombok/Builder.html the use of this annotation (@Builder) at class level of TemplateLineItemInput, generates a private constructor in the lombok processing. – marcosnasp Oct 05 '21 at 21:17
  • I do get the same bad result with the `java -jar target/bom-authoring-service-exec.jar`. I need that @Builder, and I expected the @Data's invariants to just "come along" in the generated bean. It still strikes me a very odd that this works in the IDE and not anywhere else. I don't know how I can introspect the produced JAR to see what the Java actually looks like on the generated Bean. – Bob Kuhar Oct 05 '21 at 22:01
  • Both my lombok dependency and the spring-boot-maven-plugin configuration came from the Spring Initializr thing. Stranger yet. WHo is wrong? Should I start rolling back Spring Boot versions? – Bob Kuhar Oct 05 '21 at 22:33
0

Ultimately this comes down to Lombok misconfiguration of my beans. The fix is to add the @AllArgsConstructor to the bean definition

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

import java.math.BigDecimal;

@Data
@Builder(toBuilder = true)
@AllArgsConstructor
public class TemplateLineItemInput {
  private final String partMasterId;
  private final String partMasterIdPath;
  private final String actionType;
  private final BigDecimal actionQuantity;
}

How we figured this out was to "Delombok" the Bean and look at the resulting code. This observation matched the error message; there was no public constructor.

  ...
  TemplateLineItemInput(String partMasterId, String partMasterIdPath, String actionType, BigDecimal actionQuantity) {
    this.partMasterId = partMasterId;
    this.partMasterIdPath = partMasterIdPath;
    this.actionType = actionType;
    this.actionQuantity = actionQuantity;
  }

Somehow (I still don't fully get why), the @Builder(toBuilder=true) annotation had Lombok producing a package private constructor. Jackson needed something public.

Adding the @AllArgsConstructor annotation made that constructor public and all is well.

  public TemplateLineItemInput(String partMasterId, String partMasterIdPath, String actionType, BigDecimal actionQuantity) {
    this.partMasterId = partMasterId;
    this.partMasterIdPath = partMasterIdPath;
    this.actionType = actionType;
    this.actionQuantity = actionQuantity;
  }

Delombok was the key.

Bob Kuhar
  • 10,838
  • 11
  • 62
  • 115