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

Destroy method not found in Native image for ExecutorService Bean type #32006

Closed
RomanCht opened this issue Jan 10, 2024 · 4 comments
Closed
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@RomanCht
Copy link

Describe the issue
I have a Spring Boot 3 (version 3.2.1) app with nothing on the classpath except spring-boot-starter.

if I use following code:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    ExecutorService executorService() {
        return Executors.newFixedThreadPool(10);
    }
}

...and then compile as Native image:

mvn clean native:compile -Pnative

...and run:

./target/demo

...then I get the following error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'executorService': Invalid destruction signature
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:643) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[demo:6.1.2]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:960) ~[demo:6.1.2]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[demo:6.1.2]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) ~[demo:3.2.1]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:464) ~[demo:3.2.1]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[demo:3.2.1]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1358) ~[demo:3.2.1]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1347) ~[demo:3.2.1]
        at com.example.demo.DemoApplication.main(DemoApplication.java:15) ~[demo:na]
Caused by: org.springframework.beans.factory.support.BeanDefinitionValidationException: Could not find a destroy method named 'shutdown' on bean with name 'executorService'
        at org.springframework.beans.factory.support.DisposableBeanAdapter.<init>(DisposableBeanAdapter.java:134) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.registerDisposableBeanIfNecessary(AbstractBeanFactory.java:1868) ~[demo:6.1.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:639) ~[demo:6.1.2]

And if I'll switch bean type to a precise one: (ThreadPoolExecutor) instead of (ExecutorService), then it works:

    // This works
    @Bean
    ThreadPoolExecutor executorService() {
        return (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
    }

    // This doesn't work
    @Bean
    ExecutorService executorService() {
        return Executors.newFixedThreadPool(10);
    }

This issue is almost exactly the same like this one: #29545 except that now it only reproducing for ExecutorService.

Steps to reproduce the issue
Please see reproducible Demo app: spring-native-executor-demo.zip

Describe GraalVM and your environment:

  • GraalVM version: 17.0.9-graal
  • JDK major version: 17
  • OS: macOS Sonoma 14.1.1 (23B81)
  • Architecture: ARM64
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 10, 2024
@sdeleuze sdeleuze self-assigned this Jan 10, 2024
@sdeleuze sdeleuze added in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing labels Jan 10, 2024
@sdeleuze
Copy link
Contributor

Looks indeed very similar to #29545, especially given the fact that ExecutorService has the void shutdown() method defined at interface level.

Surprisingly, I can't reproduce with using SDKman with sdk use java 23.1.1.r21-nik or sdk use java 21.0.1-graalce, but I can reproduce using sdk use java 17.0.9-graalce. On Spring AOT side, the metadata for ExecutorService#shutdown are generated as expected.

That likely indicates that this is a GraalVM limitation or bug only fixed in recent versions.

As consequence, and since we don't have any actionable item on Spring side, I close this ticket.

I advise you to use a more recent GraalVM distribution or add invoke reflection hints on ThreadPoolExecutor#shutdown.

@sdeleuze sdeleuze closed this as not planned Won't fix, can't repro, duplicate, stale Jan 10, 2024
@sdeleuze sdeleuze added for: external-project Needs a fix in external project and removed status: waiting-for-triage An issue we've not yet triaged or decided on theme: aot An issue related to Ahead-of-time processing labels Jan 10, 2024
@RomanCht
Copy link
Author

@sdeleuze thank you for quick investigation!
I see that for different java versions the metadata for ExecutorService's Destroy method generated differently.

For the Java 17 (sdk use java 17.0.9-graalce) metadata looks like this:

  {
    "name": "java.util.concurrent.ExecutorService",
    "queryAllPublicMethods": true,
    "queryAllDeclaredMethods": true,
    "methods": [
      {
        "name": "shutdown",    <<== one method, which is not default in ExecutorService interface
        "parameterTypes": [ ]
      }
    ]
  },

...but for the Java 21 (sdk use java 21.0.1-graalce) metadata looks like this:

  {
    "name": "java.util.concurrent.ExecutorService",
    "queryAllPublicMethods": true,
    "queryAllDeclaredMethods": true,
    "methods": [
      {
        "name": "close",      <<== new default method
        "parameterTypes": [ ]
      }
    ]
  },

...because, since the Java 19 ExecutorService interface extends AutoClosable, and has default void close() method.

So, probably this is the issue?

@sdeleuze
Copy link
Contributor

Good catch, this is probably the reason for such difference of behavior. But that's does not explain why the repro for #29545 works and this one on Java 17 doesn't.

After a deeper look, I think I found the cause: #29545 does not attempt to find methods in superclass interfaces. As a consequence, I reopen this issue.

@sdeleuze sdeleuze reopened this Jan 12, 2024
@sdeleuze sdeleuze added type: bug A general bug and removed for: external-project Needs a fix in external project labels Jan 12, 2024
@sdeleuze sdeleuze added this to the 6.1.4 milestone Jan 12, 2024
@sdeleuze sdeleuze changed the title Destroy method not found in Native image for ExecutorService Bean type Destroy method not found in Native image for ExecutorService Bean type Jan 12, 2024
@jhoeller
Copy link
Contributor

@sdeleuze I suppose this should be backported to 6.0.x as well?

@sdeleuze sdeleuze added the for: backport-to-6.0.x Marks an issue as a candidate for backport to 6.0.x label Jan 12, 2024
@github-actions github-actions bot added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-6.0.x Marks an issue as a candidate for backport to 6.0.x labels Jan 12, 2024
sdeleuze added a commit to sdeleuze/spring-aot-smoke-tests that referenced this issue Jan 12, 2024
mhalbritter pushed a commit to spring-projects/spring-aot-smoke-tests that referenced this issue Jan 30, 2024
mhalbritter added a commit to spring-projects/spring-aot-smoke-tests that referenced this issue Jan 30, 2024
mhalbritter added a commit to spring-projects/spring-aot-smoke-tests that referenced this issue Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants