diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/CompiledJavaVersionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/CompiledJavaVersionBuildItem.java index c7d10dd152feb..672d287652d84 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/CompiledJavaVersionBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/CompiledJavaVersionBuildItem.java @@ -30,6 +30,8 @@ public interface JavaVersion { Status isJava17OrHigher(); + Status isJava19OrHigher(); + enum Status { TRUE, FALSE, @@ -57,12 +59,18 @@ public Status isJava11OrHigher() { public Status isJava17OrHigher() { return Status.UNKNOWN; } + + @Override + public Status isJava19OrHigher() { + return Status.UNKNOWN; + } } final class Known implements JavaVersion { private static final int JAVA_11_MAJOR = 55; private static final int JAVA_17_MAJOR = 61; + private static final int JAVA_19_MAJOR = 63; private final int determinedMajor; @@ -85,6 +93,11 @@ public Status isJava17OrHigher() { return higherOrEqualStatus(JAVA_17_MAJOR); } + @Override + public Status isJava19OrHigher() { + return higherOrEqualStatus(JAVA_19_MAJOR); + } + private Status higherOrEqualStatus(int javaMajor) { return determinedMajor >= javaMajor ? Status.TRUE : Status.FALSE; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/CompiledJavaVersionBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/CompiledJavaVersionBuildStep.java index eb2e7246e12bf..f5dd073251b92 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/CompiledJavaVersionBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/CompiledJavaVersionBuildStep.java @@ -23,6 +23,10 @@ public class CompiledJavaVersionBuildStep { */ @BuildStep public CompiledJavaVersionBuildItem compiledJavaVersion(BuildSystemTargetBuildItem buildSystemTarget) { + if ((buildSystemTarget.getOutputDirectory() == null) || (!Files.exists(buildSystemTarget.getOutputDirectory()))) { + // needed for Arquillian TCK tests + return CompiledJavaVersionBuildItem.unknown(); + } AtomicReference majorVersion = new AtomicReference<>(null); try { Files.walkFileTree(buildSystemTarget.getOutputDirectory(), new SimpleFileVisitor<>() { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index d690a15792e12..be9da31998cac 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -58,6 +58,7 @@ import org.jboss.resteasy.reactive.common.processor.DefaultProducesHandler; import org.jboss.resteasy.reactive.common.processor.EndpointIndexer; import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.TargetJavaVersion; import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationsTransformer; @@ -117,6 +118,7 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; import io.quarkus.deployment.configuration.ConfigurationError; +import io.quarkus.deployment.pkg.builditem.CompiledJavaVersionBuildItem; import io.quarkus.deployment.recording.RecorderContext; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.MethodCreator; @@ -359,6 +361,7 @@ public void setupEndpoints(ApplicationIndexBuildItem applicationIndexBuildItem, List methodScanners, List annotationTransformerBuildItems, List contextTypeBuildItems, + CompiledJavaVersionBuildItem compiledJavaVersionBuildItem, Capabilities capabilities) throws NoSuchMethodException { @@ -514,7 +517,28 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an } }) .setResteasyReactiveRecorder(recorder) - .setApplicationClassPredicate(applicationClassPredicate); + .setApplicationClassPredicate(applicationClassPredicate) + .setTargetJavaVersion(new TargetJavaVersion() { + + private final Status result; + + { + CompiledJavaVersionBuildItem.JavaVersion.Status status = compiledJavaVersionBuildItem + .getJavaVersion().isJava19OrHigher(); + if (status == CompiledJavaVersionBuildItem.JavaVersion.Status.FALSE) { + result = Status.FALSE; + } else if (status == CompiledJavaVersionBuildItem.JavaVersion.Status.TRUE) { + result = Status.TRUE; + } else { + result = Status.UNKNOWN; + } + } + + @Override + public Status isJava19OrHigher() { + return result; + } + }); if (!serverDefaultProducesHandlers.isEmpty()) { List handlers = new ArrayList<>(serverDefaultProducesHandlers.size()); diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index dfa580ff8449d..caa4e997d08ad 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -118,6 +118,7 @@ import org.jboss.resteasy.reactive.common.model.ParameterType; import org.jboss.resteasy.reactive.common.model.ResourceClass; import org.jboss.resteasy.reactive.common.model.ResourceMethod; +import org.jboss.resteasy.reactive.common.processor.TargetJavaVersion.Status; import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationsTransformer; @@ -159,6 +160,8 @@ public abstract class EndpointIndexer prims = new HashMap<>(); prims.put(byte.class.getName(), Byte.class.getName()); @@ -195,6 +198,14 @@ public abstract class EndpointIndexer contextTypes; private final MultipartReturnTypeIndexerExtension multipartReturnTypeIndexerExtension; private final MultipartParameterIndexerExtension multipartParameterIndexerExtension; + private final TargetJavaVersion targetJavaVersion; protected EndpointIndexer(Builder builder) { this.index = builder.index; @@ -238,6 +250,7 @@ protected EndpointIndexer(Builder builder) { this.contextTypes = builder.contextTypes; this.multipartReturnTypeIndexerExtension = builder.multipartReturnTypeIndexerExtension; this.multipartParameterIndexerExtension = builder.multipartParameterIndexerExtension; + this.targetJavaVersion = builder.targetJavaVersion; } public Optional createEndpoints(ClassInfo classInfo, boolean considerApplication) { @@ -700,20 +713,6 @@ private String getAnnotationValueAsString(AnnotationTarget target, DotName annot private boolean isRunOnVirtualThread(MethodInfo info, BlockingDefault defaultValue) { boolean isRunOnVirtualThread = false; - boolean isJDKCompatible = true; - try { - Class.forName("java.lang.ThreadBuilders"); - } catch (ClassNotFoundException e) { - isJDKCompatible = false; - } - - if (!isJDKCompatible) { - log.warn("Your version of the JDK is '" + Runtime.version() + - "' and doesn't support Loom's virtual threads" + - ", your runtime will have to use jdk-19-loom or superior to leverage virtual threads " + - "(else java platform threads will be used instead)."); - } - Map.Entry runOnVirtualThreadAnnotation = getInheritableAnnotation(info, RUN_ON_VIRTUAL_THREAD); @@ -725,6 +724,15 @@ private boolean isRunOnVirtualThread(MethodInfo info, BlockingDefault defaultVal } if (runOnVirtualThreadAnnotation != null) { + if (!JDK_SUPPORTS_VIRTUAL_THREADS) { + throw new DeploymentException("Method '" + info.name() + "' of class '" + info.declaringClass().name() + + "' uses @RunOnVirtualThread but the JDK version '" + Runtime.version() + + "' and doesn't support virtual threads"); + } + if (targetJavaVersion.isJava19OrHigher() == Status.FALSE) { + throw new DeploymentException("Method '" + info.name() + "' of class '" + info.declaringClass().name() + + "' uses @RunOnVirtualThread but the target JDK version doesn't support virtual threads. Please configure your build tool to target Java 19 or above"); + } isRunOnVirtualThread = true; } @@ -1403,6 +1411,7 @@ public boolean handleMultipartForReturnType(AdditionalWriters additionalWriters, public void handleMultipartParameter(ClassInfo multipartClassInfo, IndexView indexView) { } }; + private TargetJavaVersion targetJavaVersion = new TargetJavaVersion.Unknown(); public B setMultipartReturnTypeIndexerExtension(MultipartReturnTypeIndexerExtension multipartReturnTypeHandler) { this.multipartReturnTypeIndexerExtension = multipartReturnTypeHandler; @@ -1504,6 +1513,11 @@ public B setApplicationScanningResult(ApplicationScanningResult applicationScann return (B) this; } + public B setTargetJavaVersion(TargetJavaVersion targetJavaVersion) { + this.targetJavaVersion = targetJavaVersion; + return (B) this; + } + public abstract T build(); } diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/TargetJavaVersion.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/TargetJavaVersion.java new file mode 100644 index 0000000000000..0db7f01858b12 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/TargetJavaVersion.java @@ -0,0 +1,28 @@ +package org.jboss.resteasy.reactive.common.processor; + +/** + * Used to determine the java version of the compiled Java code + */ +public interface TargetJavaVersion { + + Status isJava19OrHigher(); + + enum Status { + TRUE, + FALSE, + UNKNOWN + } + + final class Unknown implements TargetJavaVersion { + + public static final Unknown INSTANCE = new Unknown(); + + Unknown() { + } + + @Override + public Status isJava19OrHigher() { + return Status.UNKNOWN; + } + } +}