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

Custom Init or Destroy method from an interface cannot be invoked in a native image #31819

Closed
czp3009 opened this issue Dec 12, 2023 · 5 comments
Assignees
Labels
type: regression A bug that is also a regression
Milestone

Comments

@czp3009
Copy link

czp3009 commented Dec 12, 2023

Affected spring version: 6.1.1(native image)

r2dbc connectionFactory will clean up the connection pool when the application exits, but at this time it will report an error:

2023-12-12T17:15:53.545+08:00  WARN 48253 --- [ionShutdownHook] o.s.b.f.support.DisposableBeanAdapter    : Failed to invoke custom destroy method 'dispose' on bean with name 'connectionFactory'

org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public abstract void reactor.core.Disposable.dispose() 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.
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.forQueriedOnlyExecutable(MissingReflectionRegistrationUtils.java:97) ~[na:na]
        at java.base@17.0.8/java.lang.reflect.Method.acquireMethodAccessor(Method.java:77) ~[demo:na]
        at java.base@17.0.8/java.lang.reflect.Method.invoke(Method.java:566) ~[demo:na]
        at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:316) ~[na:na]
        at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:249) ~[na:na]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:587) ~[demo:6.1.1]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:559) ~[demo:6.1.1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1200) ~[demo:6.1.1]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:520) ~[demo:6.1.1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1193) ~[demo:6.1.1]
        at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1125) ~[demo:6.1.1]
        at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1086) ~[demo:6.1.1]
        at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.doClose(ReactiveWebServerApplicationContext.java:149) ~[demo:3.2.0]
        at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1037) ~[demo:6.1.1]
        at org.springframework.boot.SpringApplicationShutdownHook.closeAndWait(SpringApplicationShutdownHook.java:145) ~[na:na]
        at java.base@17.0.8/java.lang.Iterable.forEach(Iterable.java:75) ~[demo:na]
        at org.springframework.boot.SpringApplicationShutdownHook.run(SpringApplicationShutdownHook.java:114) ~[na:na]
        at java.base@17.0.8/java.lang.Thread.run(Thread.java:833) ~[demo:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:807) ~[demo:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:210) ~[na:na]

I have written some code to reproduce this problem: https://github.com/czp3009/spring-native-dispose-issue-reproduction

Native compile and execute the application and then terminate it to see the exception

I guess it's related to this issue: #31278

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 12, 2023
@snicoll
Copy link
Member

snicoll commented Dec 12, 2023

@czp3009 thanks for the report and the sample but I believe that the repo is either private or its URL is misspelled. Can you please check?

@czp3009
Copy link
Author

czp3009 commented Dec 12, 2023

@snicoll oh, sorry, i forgot to change repository visibility. please try again.

@snicoll snicoll self-assigned this Dec 12, 2023
@snicoll
Copy link
Member

snicoll commented Dec 12, 2023

It's a bit strange. I can see a hint being generated for the proper target class:

{
    "name": "io.r2dbc.pool.ConnectionPool",
    "queryAllPublicMethods": true,
    "queryAllDeclaredMethods": true,
    "methods": [
      {
        "name": "dispose",
        "parameterTypes": [ ]
      }
    ]
  }

I don't really understand yet why it insists on having a hint for the interface.

@snicoll snicoll added type: regression A bug that is also a regression and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 12, 2023
@snicoll snicoll added this to the 6.1.2 milestone Dec 12, 2023
@snicoll
Copy link
Member

snicoll commented Dec 12, 2023

This sample worked with Spring Boot 3.1.x (and Spring Framework 6.0.x) so I've flagged it as a regression.

The only thing that was different was "queryAllPublicMethods": true missing in the 6.0.x version. I've added it manually in the sample and it works still.

@snicoll snicoll changed the title Missing reflection hint on reactor.core.Disposable.dispose() Custom Init or Destroy method from an interface cannot be invoked in a native image Dec 12, 2023
@snicoll
Copy link
Member

snicoll commented Dec 12, 2023

Looking at the code it was always meant to be invoked from the interface. It looks like in 6.0.x the interface was hidden and things were working as a side effect of that. I've made sure the interface method has the appropriate hint. With a snapshot, that sample now works. Thanks again for the report @czp3009.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

3 participants