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

Document the need for compilation with -parameters when targeting a native image #33182

Closed
michalkrajcovic opened this issue Nov 15, 2022 · 8 comments
Assignees
Labels
type: documentation A documentation update
Milestone

Comments

@michalkrajcovic
Copy link

Describe the bug

Spring Boot 3 Native - "Could not bind properties" error when properties class located in another module uses @ConstructorBinding in multi-module gradle project

To Reproduce
Environment

  • Spring Boot: 3.0.0-RC2
  • Native Buildtools: 0.9.17
  • GraalVM version : graalvm-ce-java17-22.3.0
  • JDK version: openjdk 17.0.5
  • Architecture: AMD64

Sample project
https://github.com/michalkrajcovic/spring-native-multi-modules-bind-properties

Compile

./gradlew nativeCompile

Run

./greetings-app/build/native/nativeCompile/greetings-app

Fails with

o.s.c.support.GenericApplicationContext  : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'application': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'greetings-com.example.greetings.configuration.GreetingsProperties': Could not bind properties to 'GreetingsProperties' : prefix=greetings, ignoreInvalidFields=false, ignoreUnknownFields=true
Description:
Failed to bind properties under 'greetings' to com.example.greetings.configuration.GreetingsProperties:
    Reason: java.lang.IllegalStateException: Failed to extract parameter names for public com.example.greetings.configuration.GreetingsProperties(java.lang.String)

Runs without issues on JVM

./gradlew bootRun

Expected behavior
Application starts without issues using JVM and native image.

Observation
Does not fail when properties class uses setters see brach setter. Also ConstructorBinding doesn't fail in single module project see branch single-module

Originally reported for Spring Boot 2.7.5 in spring-native

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 15, 2022
@snicoll
Copy link
Member

snicoll commented Nov 15, 2022

@michalkrajcovic thanks for the report. GreetingsProperties must be annotated with @NestedConfigurationProperties to indicate to the inference algorithm that it isn't a scalar value but a "sub-namespace".

See #31708.

@snicoll snicoll closed this as not planned Won't fix, can't repro, duplicate, stale Nov 15, 2022
@snicoll snicoll added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 15, 2022
@michalkrajcovic
Copy link
Author

@snicoll actually, this is not nested property class, as described in the docs here. Please see the properties class here it has only one field, type java.lang.String.

@snicoll snicoll reopened this Nov 15, 2022
@snicoll
Copy link
Member

snicoll commented Nov 15, 2022

Apologizes for this, I must have misread something but I can't tell what now.

@snicoll snicoll added status: waiting-for-triage An issue we've not yet triaged and removed status: invalid An issue that we don't feel is valid labels Nov 15, 2022
@snicoll
Copy link
Member

snicoll commented Nov 15, 2022

The error is

Caused by: java.lang.IllegalStateException: Failed to extract parameter names for public com.example.greetings.configuration.GreetingsProperties(java.lang.String)
	at org.springframework.util.Assert.state(Assert.java:97) ~[na:na]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder$DefaultValueObject.parseConstructorParameters(ValueObjectBinder.java:270) ~[na:na]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder$DefaultValueObject.<init>(ValueObjectBinder.java:264) ~[na:na]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder$DefaultValueObject.get(ValueObjectBinder.java:291) ~[na:na]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder$ValueObject.get(ValueObjectBinder.java:198) ~[greetings-app:3.0.0-RC2]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder.bind(ValueObjectBinder.java:67) ~[greetings-app:3.0.0-RC2]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:476) ~[greetings-app:3.0.0-RC2]
	at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:590) ~[na:na]
	at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:576) ~[na:na]
	at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:474) ~[greetings-app:3.0.0-RC2]
	at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:414) ~[greetings-app:3.0.0-RC2]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343) ~[greetings-app:3.0.0-RC2]
	... 39 common frames omitted

There is a reflection hint for GreetingProperties:

 {
    "name": "com.example.greetings.configuration.GreetingsProperties",
    "methods": [
      {
        "name": "<init>",
        "parameterTypes": [
          "java.lang.String"
        ]
      }
    ]
  }

It feels like --parameter wasn't set or something.

@wilkinsona
Copy link
Member

It feels like --parameter wasn't set or something.

I think that's exactly it. Boot's Gradle plugin sets -parameters but it hasn't been applied to the greetings-configuration project. I suspect that it works on the JVM as we're able to fall back to the LocalVariableTableParameterNameDiscoverer. That fallback would only work in a native image if the class files were made available as resources.

@wilkinsona
Copy link
Member

The sample works with this addition to the greetings-configuration project:

tasks.named('compileJava').configure {
    options.compilerArgs = ['-parameters']
}

There's no way for us to do this automatically as Boot's plugin hasn't been applied (and nor should it have been in this case). I wonder if there's something else that we could do to make this work automatically. If including the class files as resources works, perhaps we could generate hints that do that if we detect that the standard reflection parameter information is missing?

@wilkinsona wilkinsona changed the title Spring Boot 3 Native - "Could not bind properties" error when properties class located in another module uses @ConstructorBinding in multi-module gradle project "Could not bind properties" error in a native image when a @ConstructorBinding type has not been compiled with -parameters Nov 15, 2022
@wilkinsona wilkinsona added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 15, 2022
@wilkinsona wilkinsona self-assigned this Nov 15, 2022
@wilkinsona wilkinsona added this to the 3.0.0 milestone Nov 15, 2022
@wilkinsona
Copy link
Member

If including the class files as resources works, perhaps we could generate hints that do that if we detect that the standard reflection parameter information is missing?

I've prototyped this and it works. I'd like to discuss this with team to see if it's something that we want to do or if we'd prefer to document the need to compile with -parameters when targeting a native image.

@wilkinsona
Copy link
Member

We discussed this today and concluded that relying on .class resources and ASM-based processing isn't the right choice for a native image. We're going to document the need for -parameters and also fail fast at build time when we detect that it hasn't been used.

@wilkinsona wilkinsona added type: documentation A documentation update and removed type: bug A general bug labels Nov 16, 2022
@wilkinsona wilkinsona changed the title "Could not bind properties" error in a native image when a @ConstructorBinding type has not been compiled with -parameters Document the need for compilation with -parameters when targeting a native image Nov 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation A documentation update
Projects
None yet
Development

No branches or pull requests

4 participants