Multirelease include JDK21 to support Thread.isVirtual()

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/core-common/pom.xml b/core-common/pom.xml
index 7a187e0..357b2e9 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -693,6 +693,157 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>build.jdk20-</id>
+            <activation>
+                <jdk>[1.8,21)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>add.java20-</id>
+                                <phase>generate-sources</phase>
+                                <goals>
+                                    <goal>add-source</goal>
+                                </goals>
+                                <configuration>
+                                    <sources>
+                                        <source>src/main/java20-</source>
+                                    </sources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>build.jdk21</id>
+            <activation>
+                <jdk>[21,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>add.java20-</id>
+                                <phase>generate-sources</phase>
+                                <goals>
+                                    <goal>add-source</goal>
+                                </goals>
+                                <configuration>
+                                    <sources>
+                                        <source>src/main/java21</source>
+                                    </sources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>default-compile</id>
+                                <configuration>
+                                    <!-- compile everything to ensure module-info contains right entries -->
+                                    <release>21</release>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-antrun-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <phase>validate</phase>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                                <configuration>
+                                    <target>
+                                        <echo>Building for JDK 21+</echo>
+                                    </target>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>compile-java21</id>
+                                <phase>process-resources</phase>
+                                <configuration>
+                                    <target>
+                                        <mkdir dir="${java21.build.outputDirectory}" />
+                                        <javac srcdir="${java21.sourceDirectory}" destdir="${java21.build.outputDirectory}"
+                                               classpath="${project.build.outputDirectory}" includeantruntime="false" release="21"/>
+                                    </target>
+                                </configuration>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>copyJDK21FilesToMultiReleaseJar</id>
+            <activation>
+                <file>
+                    <!-- ${java21.build.outputDirectory} does not work here -->
+                    <exists>target/classes-java21/org/glassfish/jersey/innate/VirtualThreadSupport.class</exists>
+                </file>
+                <jdk>[1.8,21)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-resources-plugin</artifactId>
+                        <inherited>true</inherited>
+                        <executions>
+                            <execution>
+                                <id>copy-jdk21-sources</id>
+                                <phase>prepare-package</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/21/org/glassfish/jersey/innate</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${java21.sourceDirectory}/org/glassfish/jersey/innate</directory>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>copy-jdk21-classes-to-meta-inf</id>
+                                <phase>prepare-package</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${project.build.outputDirectory}/META-INF/versions/21</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${java21.build.outputDirectory}</directory>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
 
         <profile>
             <id>securityOff</id>
@@ -739,6 +890,8 @@
         <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory>
         <java11.build.outputDirectory>${project.build.directory}/classes-java11</java11.build.outputDirectory>
         <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+        <java21.build.outputDirectory>${project.build.directory}/classes-java21</java21.build.outputDirectory>
+        <java21.sourceDirectory>${project.basedir}/src/main/java21</java21.sourceDirectory>
     </properties>
 
 </project>
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
index 7c1cf4a..05e77cb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
@@ -23,9 +23,9 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.glassfish.jersey.innate.VirtualThreadSupport;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.guava.Preconditions;
-import org.glassfish.jersey.internal.util.JdkVersion;
 
 /**
  * A committing output stream with optional serialized entity buffering functionality
@@ -59,7 +59,7 @@
 public final class CommittingOutputStream extends OutputStream {
 
     private static final Logger LOGGER = Logger.getLogger(CommittingOutputStream.class.getName());
-    private static final boolean JDK_21 = JdkVersion.getJdkVersion().getMajor() > 20;
+    private final boolean isVirtualThread = VirtualThreadSupport.isVirtualThread();
 
     /**
      * Null stream provider.
@@ -278,7 +278,7 @@
 
             commitStream(currentSize);
             if (buffer != null) {
-                if (adaptedOutput != null && JDK_21) {
+                if (isVirtualThread && adaptedOutput != null) {
                     adaptedOutput.write(buffer.toByteArray());
                 } else {
                     // Virtual thread in JDK 21 are blocked by synchronized writeTo
diff --git a/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
new file mode 100644
index 0000000..90cafba
--- /dev/null
+++ b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.innate;
+
+/**
+ * Utility class for the virtual thread support.
+ */
+public final class VirtualThreadSupport {
+
+    /**
+     * Do not instantiate.
+     */
+    private VirtualThreadSupport() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Informs whether the given {@link Thread} is virtual.
+     * @return true when the current thread is virtual.
+     */
+    public static boolean isVirtualThread() {
+        return false;
+    }
+}
diff --git a/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
new file mode 100644
index 0000000..74f58ba
--- /dev/null
+++ b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.innate;
+
+/**
+ * Utility class for the virtual thread support.
+ */
+public final class VirtualThreadSupport {
+
+    /**
+     * Do not instantiate.
+     */
+    private VirtualThreadSupport() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Informs whether the given {@link Thread} is virtual.
+     * @return true when the current thread is virtual.
+     */
+    public static boolean isVirtualThread() {
+        return Thread.currentThread().isVirtual();
+    }
+}