Hi,

I started activating VirtualThreads in application.properties, compiled my application using OpenJDK 21 (Amazon Corretto), but get the following error:

2024-04-15T10:30:02.913Z  INFO 15927 --- [           main] com.unicorn.store.StoreApplication       : Starting StoreApplication v1.0.0 using Java 21.0.2 with PID 15927 (/home/ec2-user/environment/unicorn-store-spring/store-spring.jar started by ec2-user in /home/ec2-user/environment/unicorn-store-spring)
2024-04-15T10:30:02.917Z  INFO 15927 --- [           main] com.unicorn.store.StoreApplication       : No active profile set, falling back to 1 default profile: "default"
2024-04-15T10:30:03.075Z  INFO 15927 --- [           main] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2024-04-15T10:30:05.165Z  INFO 15927 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-04-15T10:30:05.340Z  INFO 15927 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 163 ms. Found 1 JPA repository interface.
2024-04-15T10:30:06.758Z  INFO 15927 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-04-15T10:30:06.777Z  INFO 15927 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-04-15T10:30:06.778Z  INFO 15927 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.19]
2024-04-15T10:30:06.834Z  INFO 15927 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-04-15T10:30:06.836Z  INFO 15927 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3742 ms
2024-04-15T10:30:07.351Z  INFO 15927 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-15T10:30:07.930Z  INFO 15927 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@439f2d87
2024-04-15T10:30:07.932Z  INFO 15927 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-15T10:30:08.176Z  INFO 15927 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-04-15T10:30:08.276Z  INFO 15927 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 1.0.0
2024-04-15T10:30:08.328Z  INFO 15927 --- [           main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
2024-04-15T10:30:08.624Z  INFO 15927 --- [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-04-15T10:30:08.712Z  WARN 15927 --- [           main] org.hibernate.orm.deprecation            : HHH90000025: PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
2024-04-15T10:30:09.846Z  INFO 15927 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2024-04-15T10:30:09.852Z  INFO 15927 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-04-15T10:30:10.000Z  INFO 15927 --- [           main] c.unicorn.store.data.UnicornPublisher    : Creating EventBridgeAsyncClient
2024-04-15T10:30:11.189Z  WARN 15927 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'applicationTaskExecutor' defined in class path resource [org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations$TaskExecutorConfiguration.class]: Failed to instantiate [org.springframework.core.task.SimpleAsyncTaskExecutor]: Factory method 'applicationTaskExecutorVirtualThreads' threw exception with message: Virtual threads not supported on JDK <21
2024-04-15T10:30:11.192Z  INFO 15927 --- [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2024-04-15T10:30:11.196Z  INFO 15927 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-04-15T10:30:11.201Z  INFO 15927 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2024-04-15T10:30:11.203Z  INFO 15927 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2024-04-15T10:30:11.238Z  INFO 15927 --- [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-04-15T10:30:11.266Z ERROR 15927 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'applicationTaskExecutor' defined in class path resource [org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations$TaskExecutorConfiguration.class]: Failed to instantiate [org.springframework.core.task.SimpleAsyncTaskExecutor]: Factory method 'applicationTaskExecutorVirtualThreads' threw exception with message: Virtual threads not supported on JDK <21
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[!/:1.0.0]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[!/:1.0.0]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[!/:1.0.0]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[!/:1.0.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[!/:1.0.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[!/:1.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[!/:1.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[!/:1.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[!/:1.0.0]
        at com.unicorn.store.StoreApplication.main(StoreApplication.java:13) ~[!/:1.0.0]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
        at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[store-spring.jar:1.0.0]
        at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[store-spring.jar:1.0.0]
        at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) ~[store-spring.jar:1.0.0]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.core.task.SimpleAsyncTaskExecutor]: Factory method 'applicationTaskExecutorVirtualThreads' threw exception with message: Virtual threads not supported on JDK <21
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~[!/:1.0.0]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[!/:1.0.0]
        ... 24 common frames omitted
Caused by: java.lang.UnsupportedOperationException: Virtual threads not supported on JDK <21
        at org.springframework.core.task.VirtualThreadDelegate.<init>(VirtualThreadDelegate.java:32) ~[!/:1.0.0]
        at org.springframework.core.task.SimpleAsyncTaskExecutor.setVirtualThreads(SimpleAsyncTaskExecutor.java:125) ~[!/:1.0.0]
        at org.springframework.boot.context.properties.PropertyMapper$Source.to(PropertyMapper.java:294) ~[!/:1.0.0]
        at org.springframework.boot.task.SimpleAsyncTaskExecutorBuilder.configure(SimpleAsyncTaskExecutorBuilder.java:218) ~[!/:1.0.0]
        at org.springframework.boot.task.SimpleAsyncTaskExecutorBuilder.build(SimpleAsyncTaskExecutorBuilder.java:192) ~[!/:1.0.0]
        at org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorConfiguration.applicationTaskExecutorVirtualThreads(TaskExecutorConfigurations.java:58) ~[!/:1.0.0]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[!/:1.0.0]
        ... 25 common frames omitted

My Java-version:

java -version
openjdk version "21.0.2" 2024-01-16 LTS
OpenJDK Runtime Environment Corretto-21.0.2.14.1 (build 21.0.2+14-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.2.14.1 (build 21.0.2+14-LTS, mixed mode, sharing)

Maven-output:

mvn -version
Apache Maven 3.9.4 (dfbb324ad4a7c8fb0bf182e6d91b0ae20e3d2dd9)
Maven home: /usr/lib/maven
Java version: 21.0.2, vendor: Amazon.com Inc., runtime: /usr/lib/jvm/java-21-amazon-corretto.x86_64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "6.1.82-99.168.amzn2023.x86_64", arch: "amd64", family: "unix"

Comment From: wilkinsona

This failure indicates that https://github.com/spring-projects/spring-framework/blob/5012843b1254aefa7d81132619c8fa6e3f79b495/spring-core/src/main/java/org/springframework/core/task/VirtualThreadDelegate.java has been loaded instead of https://github.com/spring-projects/spring-framework/blob/5012843b1254aefa7d81132619c8fa6e3f79b495/spring-core/src/main/java21/org/springframework/core/task/VirtualThreadDelegate.java. The latter should be used automatically on Java 21+ due to spring-core being a multi-release jar file. I cannot tell why the correct version is not being loaded automatically without knowing the version of Spring Boot that you are using and exactly how you have built and run your application.

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: smoell

unicorn-store-spring.tar.gz

Thank you! I've attached a demo project, you can reproduce the issue with:

  1. Unzip the project
  2. Build the project using docker buildx build --load -t unicorn-store-spring:latest .
  3. cd dockerfiles && docker compose up

Comment From: wilkinsona

The store-spring.jar that you're building is rather unusual as it contains classes from your application's dependencies in BOOT-INF/classes, including VirtualThreadDelegate:

BOOT-INF/classes/org/springframework/core/task/VirtualThreadTaskExecutor.class

I suspect that this is due to your configuration of the maven-shade-plugin. It's breaking the multi-release nature of spring-core when it shades it, causing the wrong version of VirtualThreadTaskExecutor.class to be loaded.

Comment From: smoell

Thank you! We need to Maven Shade plugin to generate an artifact for AWS Lambda as well. We'll stick with standard threads until we find a way to fix this. Thank you!.