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

@Validated in @ConfigurationProperties doesn't work when running in native image #6976

Closed
KimSeongIl opened this issue Jul 12, 2023 · 17 comments
Assignees
Labels

Comments

@KimSeongIl
Copy link

Describe the issue

It worked fine on the jvm, but when I run it on the native image I get an error.
But if I remove the Pattern annotation, it works fine.

Why is this issue occurring?
How can I solve this problem?

Steps to reproduce the issue

  1. git clone https://github.com/KimSeongIl/demo.git
  2. nativeCompile
  3. nativeRun

DemoContext.kt

@ConfigurationProperties(prefix = "demo.context")
@Validated
class DemoContext {

    var exclude: List<Exclude> = mutableListOf()

    data class Exclude(
        var url: List<String>? = null,

        var httpMethod: List<HttpMethod>? = null,

        var httpStatus: List<@Pattern(regexp="^([1-5][x|X]{2}|[1-5][0-9]{2})\$") String>? = null,

        var elapsedTime: Long? = null
    )

}

application.yml

demo:
  context:
    exclude:
      - httpStatus: 2xx

nativeRun result

Binding to target [Bindable@517a5ddb type = java.util.List<DemoContext$Exclude>, value = 'provided', annotations = array<Annotation>[[empty]]] failed:

    Property: demo.context.exclude[0].httpstatus
    Value: "2xx"
    Origin: class path resource [application.yml] - 31:21
    Reason: The elements [demo.context.exclude[0].httpstatus] were left unbound.

Action:

Update your application's configuration

Describe GraalVM and your environment

spring boot version: 3.1.0
graalvm plugin version: id("org.graalvm.buildtools.native") version "0.9.20"
graalvm version: 22.3.r17

More details

spring-projects/spring-boot#37101

discussed in the above issue
@sdeleuze said it looks like a graalvm bug.

@vjovanov
Copy link
Member

vjovanov commented Jul 16, 2023

I tried running this app with -H:ThrowMissingRegistrationErrors= -H:MissingRegistrationReportingMode=Warn and I get the following list of missing metadata:

org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively access jakarta.validation.constraints.Pattern.getDeclaredConstructors() without it being registered for runtime reflection. Add it to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.
  [email protected]/java.lang.Class.getDeclaredConstructors(DynamicHub.java:1121)
  kotlin.reflect.jvm.internal.impl.descriptors.runtime.structure.ReflectJavaClass.getConstructors(ReflectJavaClass.kt:96)
  kotlin.reflect.jvm.internal.impl.descriptors.runtime.structure.ReflectJavaClass.getConstructors(ReflectJavaClass.kt:29)
  kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope$constructors$1.invoke(LazyJavaClassMemberScope.kt:84)

Why would these reflective accesses be registered by GraalVM? Have you tried running with the agent to collect reflection metadata?

@KimSeongIl
Copy link
Author

KimSeongIl commented Jul 17, 2023

@RegisterReflectionForBinding(
Pattern::class,
Pattern.Flag::class)

@vjovanov
I added the reflect setting with the annotation as above.

If I add the annotation, the application works but
The pattern rule doesn't work.

@vjovanov
Copy link
Member

Can you please run the application with the native image agent so we can see all the produced JSON?

@KimSeongIl
Copy link
Author

KimSeongIl commented Jul 19, 2023

@vjovanov
What is a native image agent?

I ran it with nativeCompile and nativeRun in gradle environment.

https://github.com/KimSeongIl/demo/blob/main/build/resources/aot/META-INF/native-image/com.example/demoo/reflect-config.json

Does the produced json say this?

@vjovanov
Copy link
Member

@KimSeongIl
Copy link
Author

@vjovanov

https://github.com/KimSeongIl/demo/blob/main/build/resources/aot/META-INF/native-image/com.example/demoo/reflect-config.json

Isn't the produced json the link above?

./gradlew -Pagent run

If I run

java.lang.IllegalArgumentException: Could not find class [com.demo.search.middle.demo.DemooApplicationKt__ApplicationContextInitializer]

The above error occurs.

@vjovanov
Copy link
Member

Not sure it is the same file. Does the ./gradlew run work? If no, is it possible to make it work?

@KimSeongIl
Copy link
Author

@vjovanov
The ./gradlew run command also gives an error for the same reason.

[native-image-plugin] Instrumenting task with the native-image-agent: processAot

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2023-07-20T10:14:10.724+09:00 ERROR 82427 --- [           main] o.s.boot.SpringApplication               : Application run failed
java.lang.IllegalArgumentException: Could not find class [com.demo.search.middle.demo.DemooApplicationKt__ApplicationContextInitializer]

am trying to solve it.

@sdeleuze
Is this error caused by Kotlin environment?

@KimSeongIl
Copy link
Author

@vjovanov

./gradlew run works fine.
Why did you fail before

graalvmNative {

agent {
          enabled.set(true)
   }
}

Because of the above settings, it was run as an agent .

As a result
./gradlew run works, but
./gradlew -Pagent run doesn't work.

[native-image-plugin] Instrumenting task with the native-image-agent: processAot

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2023-07-20T10:14:10.724+09:00 ERROR 82427 --- [           main] o.s.boot.SpringApplication               : Application run failed
java.lang.IllegalArgumentException: Could not find class [com.demo.search.middle.demo.DemooApplicationKt__ApplicationContextInitializer]

How to solve above error when there is agent parameter?

@sdeleuze
Copy link
Collaborator

sdeleuze commented Aug 7, 2023

@KimSeongIl You can use Spring Boot 3.1.2 to fix this error (was a Spring bug I fixed).

Why would these reflective accesses be registered by GraalVM? Have you tried running with the agent to collect reflection metadata?

@vjovanov Let see what the agent says, if the usage of Pattern.Flag is due to Pattern instantiation, I agree GraalVM can't guess it automatically and maybe we need to refine Spring Hibernate Validator algorithm. But let's fully analyze this from a pure GraalVM perspective to be sure.

@KimSeongIl
Copy link
Author

@sdeleuze

Thank you for answer. 😁

https://github.com/KimSeongIl/demo

However, upgrading the spring boot version to 3.1.2 does not change anything.
Are additional code modifications required?

https://github.com/spring-projects/spring-boot/releases/tag/v3.1.2

Where in the release notes are the bugfixes related?

@sdeleuze
Copy link
Collaborator

@KimSeongIl I meant to fix the java.lang.IllegalArgumentException: Could not find class [com.demo.search.middle.demo.DemooApplicationKt__ApplicationContextInitializer] error with the agent. With Spring Boot 3.1.2, you can now run ./gradlew -Pagent run (I just tried) and provide a feedback to @vjovanov on the generated metadata.

@KimSeongIl
Copy link
Author

KimSeongIl commented Aug 11, 2023

@sdeleuze
I understand. Thanks. 😁

https://github.com/KimSeongIl/demo/tree/main/src/main/resources/META-INF/native-image

@vjovanov

./gradlew -Pagent run

./gradlew metadataCopy --task run --dir src/main/resources/META-INF/native-image

./gradlew nativeCompile

./gradlew nativeRun

After running these commands, it works as expected.

Why do I have to go through that process?

@sdeleuze

Is it normal to have to go through that process?

https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.developing-your-first-application.native-build-tools.gradle

I couldn't find it in spring documentation.

@sdeleuze
Copy link
Collaborator

@KimSeongIl When the bug will be fixed either on GraalVM or Spring side, this "dance" won't be needed anymore, but for now yes that's what I would expect when you need to use the agent. Looks reasonable to me this is documented on GraalVM side as there is no Spring specific here (we may add a note later).

@vjovanov Could you please check the agent output and let me know if you see something that GraalVM could fix or if you think I should take care of that on Spring side (I will have a deeper look after your feedback)?

@KimSeongIl
Copy link
Author

@vjovanov @loicottet

Can you please answer this issue?

@vjovanov
Copy link
Member

The relevant metadata is:

{
  "name":"jakarta.validation.constraints.Pattern",
  "allDeclaredClasses":true,
  "queryAllDeclaredMethods":true,
  "queryAllDeclaredConstructors":true,
  "methods":[
    {"name":"flags","parameterTypes":[] }, 
    {"name":"groups","parameterTypes":[] }, 
    {"name":"message","parameterTypes":[] }, 
    {"name":"payload","parameterTypes":[] }, 
    {"name":"regexp","parameterTypes":[] }
  ]
},
{
  "name":"jakarta.validation.constraints.Pattern$Flag"
},

I do not see how GraalVM can prove this automatically. The proper fix would be for the framework that uses these flags to add the metadata into the jar.

Until that happens, the workaround is to keep the config in your project.

@sdeleuze
Copy link
Collaborator

Ok let's go back on Spring side for this one -> spring-projects/spring-boot#37101.

@sdeleuze sdeleuze closed this as not planned Won't fix, can't repro, duplicate, stale Aug 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants