ee.jakarta.tck.concurrent.spec.Platform.virtual.VirtualThreadServlet Maven / Gradle / Ivy
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* 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 ee.jakarta.tck.concurrent.spec.Platform.virtual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.naming.InitialContext;
import ee.jakarta.tck.concurrent.framework.TestConstants;
import ee.jakarta.tck.concurrent.framework.TestLogger;
import ee.jakarta.tck.concurrent.framework.TestServlet;
import ee.jakarta.tck.concurrent.framework.junit.extensions.Wait;
import jakarta.enterprise.concurrent.ManagedExecutorDefinition;
import jakarta.enterprise.concurrent.ManagedExecutorService;
import jakarta.enterprise.concurrent.ManagedScheduledExecutorDefinition;
import jakarta.enterprise.concurrent.ManagedScheduledExecutorService;
import jakarta.enterprise.concurrent.ManagedThreadFactory;
import jakarta.enterprise.concurrent.ManagedThreadFactoryDefinition;
import jakarta.servlet.annotation.WebServlet;
@ManagedExecutorDefinition(name = "java:app/concurrent/ManagedExecutorAnnoPlatform", virtual = false)
@ManagedExecutorDefinition(name = "java:app/concurrent/ManagedExecutorAnnoVirtual", virtual = true)
@ManagedScheduledExecutorDefinition(name = "java:app/concurrent/ManagedScheduledExecutorAnnoPlatform", virtual = false)
@ManagedScheduledExecutorDefinition(name = "java:app/concurrent/ManagedScheduledExecutorAnnoVirtual", virtual = true)
@ManagedThreadFactoryDefinition(name = "java:app/concurrent/ThreadFactoryAnnoPlatform", virtual = false)
@ManagedThreadFactoryDefinition(name = "java:app/concurrent/ThreadFactoryAnnoVirtual", virtual = true)
@WebServlet("/VirtualThreadServlet")
public class VirtualThreadServlet extends TestServlet {
private static final long serialVersionUID = 1L;
private static final TestLogger log = TestLogger.get(VirtualThreadServlet.class);
private static final Runnable NOOP_RUNNABLE = () -> {
};
private static final int VERSION = Runtime.version().feature();
public void testPlatformExecutor() throws Exception {
ManagedExecutorService platformManagedExecutorAnno = InitialContext
.doLookup("java:app/concurrent/ManagedExecutorAnnoPlatform");
ManagedExecutorService platformManagedExecutorDD = InitialContext
.doLookup("java:app/concurrent/ManagedExecutorDDPlatform");
assertNotNull(platformManagedExecutorAnno);
assertNotNull(platformManagedExecutorDD);
Future future;
future = platformManagedExecutorAnno.submit(Thread::currentThread);
assertFalse(isVirtual(future.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS)));
future = platformManagedExecutorDD.submit(Thread::currentThread);
assertFalse(isVirtual(future.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS)));
}
public void testVirtualExecutor() throws Exception {
ManagedExecutorService virtualManagedExecutorAnno = InitialContext
.doLookup("java:app/concurrent/ManagedExecutorAnnoVirtual");
ManagedExecutorService virtualManagedExecutorDD = InitialContext
.doLookup("java:app/concurrent/ManagedExecutorDDVirtual");
assertNotNull(virtualManagedExecutorAnno);
assertNotNull(virtualManagedExecutorDD);
// Force executor to run task on a new thread, if the platform supports virutal=true we should get a virtual thread
Thread annoThread = virtualManagedExecutorAnno.supplyAsync(Thread::currentThread)
.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS);
Thread ddThread = virtualManagedExecutorDD.supplyAsync(Thread::currentThread)
.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS);
if (VERSION == 17) { //TODO remove when Concurrency API supports only 21+
assertThrows(NoSuchMethodException.class, () -> isVirtual(annoThread), "Should be impossible to get a virtual thread on Java 17");
assertThrows(NoSuchMethodException.class, () -> isVirtual(ddThread), "Should be impossible to get a virtual thread on Java 17");
return;
}
// Java 21+
assumingThat(isVirtual(annoThread), () -> {
// Test invokeAll on potential virtual threads
List> results = virtualManagedExecutorAnno.invokeAll(
List.of(new LookupActionCaptureThread(null, "java:app/concurrent/ManagedExecutorAnnoVirtual"),
new LookupActionCaptureThread(null, "java:app/concurrent/ManagedExecutorAnnoVirtual"),
new LookupActionCaptureThread(null, "java:app/concurrent/ManagedExecutorAnnoVirtual")),
TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS);
assertEquals(3, results.size());
Object result0 = results.get(0).get(1, TimeUnit.MILLISECONDS);
Object result1 = results.get(1).get(1, TimeUnit.MILLISECONDS);
Object result2 = results.get(2).get(1, TimeUnit.MILLISECONDS);
Set virtualThreads = new HashSet();
assertNotNull(result0);
if (result0 instanceof Throwable)
throw new AssertionError("An error occured on thread.", (Throwable) result0);
if (isVirtual((Thread) result0))
virtualThreads.add((Thread) result0);
assertNotNull(result1);
if (result1 instanceof Throwable)
throw new AssertionError("An error occured on thread.", (Throwable) result1);
if (isVirtual((Thread) result1))
virtualThreads.add((Thread) result1);
assertNotNull(result2);
if (result2 instanceof Throwable)
throw new AssertionError("An error occured on thread.", (Throwable) result2);
if (isVirtual((Thread) result2))
virtualThreads.add((Thread) result2);
// Avoid assertions of how many tasks were executed on virtual threads since there is no guarantee
log.info("ManagedExecutorService.invokeAll() resulted in " + virtualThreads.size()
+ " out of 3 tasks were run on virtual threads.");
});
assumingThat(isVirtual(ddThread), () -> {
// Test invokeAny on virtual threads
Object result = virtualManagedExecutorDD
.invokeAny(List.of(
new LookupActionCaptureThread(null, "java:app/concurrent/ManagedExecutorDDVirtual"),
new LookupActionCaptureThread(null, "java:app/concurrent/ManagedExecutorDDVirtual")));
assertNotNull(result);
if (result instanceof Throwable)
throw new AssertionError("An error occured on thread.", (Throwable) result);
// Avoid assertion that a task was executed on a virtual thread since there is no guarantee
if (isVirtual((Thread) result))
log.info("ManagedExecutorService.invokeAny() resulted in task being run on a virtual thread.");
});
}
public void testPlatformScheduledExecutor() throws Exception {
ManagedScheduledExecutorService platformManagedScheduledExecutorAnno = InitialContext
.doLookup("java:app/concurrent/ManagedScheduledExecutorAnnoPlatform");
ManagedScheduledExecutorService platformManagedScheduledExecutorDD = InitialContext
.doLookup("java:app/concurrent/ManagedScheduledExecutorDDPlatform");
assertNotNull(platformManagedScheduledExecutorAnno);
assertNotNull(platformManagedScheduledExecutorDD);
Future future;
future = platformManagedScheduledExecutorAnno.submit(Thread::currentThread);
assertFalse(isVirtual(future.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS)));
future = platformManagedScheduledExecutorDD.submit(Thread::currentThread);
assertFalse(isVirtual(future.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS)));
}
public void testVirtualScheduledExecutor() throws Exception {
ManagedScheduledExecutorService virtualManagedScheduledExecutorAnno = InitialContext
.doLookup("java:app/concurrent/ManagedScheduledExecutorAnnoVirtual");
ManagedScheduledExecutorService virtualManagedScheduledExecutorDD = InitialContext
.doLookup("java:app/concurrent/ManagedScheduledExecutorDDVirtual");
assertNotNull(virtualManagedScheduledExecutorAnno);
assertNotNull(virtualManagedScheduledExecutorDD);
// Force executor to run task on a new thread, if the platform supports virtual=true we should get a virtual thread
Thread annoThread = virtualManagedScheduledExecutorAnno.supplyAsync(Thread::currentThread)
.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS);
Thread ddThread = virtualManagedScheduledExecutorDD.supplyAsync(Thread::currentThread)
.get(TestConstants.waitTimeout.toMillis(), TimeUnit.MILLISECONDS);
if (VERSION == 17) { //TODO remove when Concurrency API supports only 21+
assertThrows(NoSuchMethodException.class, () -> isVirtual(annoThread), "Should be impossible to get a virtual thread on Java 17");
assertThrows(NoSuchMethodException.class, () -> isVirtual(ddThread), "Should be impossible to get a virtual thread on Java 17");
return;
}
// Java 21+
assumingThat(isVirtual(ddThread), () -> {
// Test schedule on virtual threads
final LinkedBlockingQueue © 2015 - 2025 Weber Informatics LLC | Privacy Policy