diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java index 8cb7ba3acbfcf9..8538f02fabf5d1 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java @@ -17,6 +17,7 @@ import org.wildfly.common.cpu.ProcessorInfo; import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.runtime.util.ManagedScheduledExecutorService; /** * @@ -57,8 +58,13 @@ public void run() { if (threadPoolConfig.prefill) { underlying.prestartAllCoreThreads(); } - current = underlying; - return underlying; + ScheduledExecutorService managed = underlying; + // In prod mode we don't want to allow the executor to be shut down by the application or other extensions + if (launchMode == LaunchMode.NORMAL) { + managed = new ManagedScheduledExecutorService(underlying); + } + current = managed; + return managed; } private static Runnable createShutdownTask(ThreadPoolConfig threadPoolConfig, EnhancedQueueExecutor executor) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/util/ForwardingScheduledExecutorService.java b/core/runtime/src/main/java/io/quarkus/runtime/util/ForwardingScheduledExecutorService.java new file mode 100644 index 00000000000000..eae3e5cf98a321 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/util/ForwardingScheduledExecutorService.java @@ -0,0 +1,37 @@ +package io.quarkus.runtime.util; + +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * Forwards all method calls to the scheduled executor service returned from the {@link #delegate()} method. Only non-default + * methods + * declared on the {@link ScheduledExecutorService} interface are forwarded. + */ +public abstract class ForwardingScheduledExecutorService extends ForwardingExecutorService implements ScheduledExecutorService { + + protected abstract ScheduledExecutorService delegate(); + + @Override + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return delegate().schedule(command, delay, unit); + } + + @Override + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + return delegate().schedule(callable, delay, unit); + } + + @Override + public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + return delegate().scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + return delegate().scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/util/ManagedScheduledExecutorService.java b/core/runtime/src/main/java/io/quarkus/runtime/util/ManagedScheduledExecutorService.java new file mode 100644 index 00000000000000..7c0b63ec9a4a0d --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/util/ManagedScheduledExecutorService.java @@ -0,0 +1,50 @@ +package io.quarkus.runtime.util; + +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; + +import org.jboss.logging.Logger; + +/** + * Forwards all method calls to the scheduled executor service returned from the {@link #delegate()} method. + * Does not allow shutdown + */ +public class ManagedScheduledExecutorService extends ForwardingScheduledExecutorService { + + private static final Logger LOG = Logger.getLogger(ManagedScheduledExecutorService.class); + + private final ScheduledExecutorService delegate; + + public ManagedScheduledExecutorService(final ScheduledExecutorService delegate) { + this.delegate = delegate; + } + + @Override + protected ScheduledExecutorService delegate() { + return delegate; + } + + @Override + public boolean isShutdown() { + // managed executors are never shut down from the application's perspective + return false; + } + + @Override + public boolean isTerminated() { + // managed executors are never shut down from the application's perspective + return false; + } + + @Override + public void shutdown() { + LOG.debug("shutdown() not allowed on managed executor service"); + } + + @Override + public List shutdownNow() { + LOG.debug("shutdownNow() not allowed on managed executor service"); + return List.of(); + } + +}