diff --git a/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java b/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java index ec072787948..ad32d5a2a86 100644 --- a/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java +++ b/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java @@ -79,6 +79,7 @@ protected Stream getExcludedPatterns() { "com\\.vaadin\\.base\\.devserver\\.DebugWindowConnection", "com\\.vaadin\\.base\\.devserver\\.DebugWindowConnection\\$DevToolsInterfaceImpl", "com\\.vaadin\\.base\\.devserver\\.DevModeHandlerManagerImpl", + "com\\.vaadin\\.base\\.devserver\\.DevModeHandlerManagerImpl\\$InternalThreadFactory", "com\\.vaadin\\.base\\.devserver\\.DevServerWatchDog", "com\\.vaadin\\.base\\.devserver\\.DevServerWatchDog\\$WatchDogServer", "com\\.vaadin\\.base\\.devserver\\.DevToolsInterface", diff --git a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/DevModeHandlerManagerImpl.java b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/DevModeHandlerManagerImpl.java index cde5c77efcd..f14d02fe656 100644 --- a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/DevModeHandlerManagerImpl.java +++ b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/DevModeHandlerManagerImpl.java @@ -15,18 +15,20 @@ */ package com.vaadin.base.devserver; -import com.vaadin.flow.server.Command; import jakarta.servlet.annotation.HandlesTypes; import java.io.Closeable; import java.io.File; -import java.io.IOException; import java.io.Serializable; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +37,7 @@ import com.vaadin.base.devserver.startup.DevModeStartupListener; import com.vaadin.flow.internal.DevModeHandler; import com.vaadin.flow.internal.DevModeHandlerManager; +import com.vaadin.flow.server.Command; import com.vaadin.flow.server.Mode; import com.vaadin.flow.server.VaadinContext; import com.vaadin.flow.server.frontend.FrontendUtils; @@ -68,6 +71,7 @@ private static final class DevModeHandlerAlreadyStartedAttribute private DevModeHandler devModeHandler; private BrowserLauncher browserLauncher; private final Set shutdownCommands = new HashSet<>(); + private ExecutorService executorService; private String applicationUrl; private boolean fullyStarted = false; @@ -96,8 +100,11 @@ public DevModeHandler getDevModeHandler() { @Override public void initDevModeHandler(Set> classes, VaadinContext context) throws VaadinInitializerException { - setDevModeHandler( - DevModeInitializer.initDevModeHandler(classes, context)); + shutdownExecutorService(); + executorService = Executors.newFixedThreadPool(4, + new InternalThreadFactory()); + setDevModeHandler(DevModeInitializer.initDevModeHandler(classes, + context, executorService)); CompletableFuture.runAsync(() -> { DevModeHandler devModeHandler = getDevModeHandler(); if (devModeHandler instanceof AbstractDevServerRunner) { @@ -111,11 +118,18 @@ public void initDevModeHandler(Set> classes, VaadinContext context) startWatchingThemeFolder(context, config); watchExternalDependencies(context, config); setFullyStarted(true); - }); + }, executorService); setDevModeStarted(context); this.browserLauncher = new BrowserLauncher(context); } + private void shutdownExecutorService() { + if (executorService != null) { + executorService.shutdownNow(); + executorService = null; + } + } + private void watchExternalDependencies(VaadinContext context, ApplicationConfiguration config) { File frontendFolder = FrontendUtils.getProjectFrontendDir(config); @@ -159,6 +173,7 @@ public void stopDevModeHandler() { devModeHandler.stop(); devModeHandler = null; } + shutdownExecutorService(); for (Command shutdownCommand : shutdownCommands) { try { shutdownCommand.execute(); @@ -231,4 +246,18 @@ public static boolean isDevModeAlreadyStarted(VaadinContext context) { private static Logger getLogger() { return LoggerFactory.getLogger(DevModeHandlerManagerImpl.class); } + + private static class InternalThreadFactory implements ThreadFactory { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + @Override + public Thread newThread(Runnable runnable) { + String threadName = "vaadin-dev-server-" + + threadNumber.getAndIncrement(); + Thread thread = new Thread(runnable, threadName); + thread.setDaemon(true); + thread.setPriority(Thread.NORM_PRIORITY); + return thread; + } + } } diff --git a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java index 6b7da24ee24..e2cfd862a38 100644 --- a/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java +++ b/vaadin-dev-server/src/main/java/com/vaadin/base/devserver/startup/DevModeInitializer.java @@ -39,6 +39,8 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -74,9 +76,6 @@ import com.vaadin.flow.server.startup.VaadinInitializerException; import com.vaadin.pro.licensechecker.LicenseChecker; -import elemental.json.Json; -import elemental.json.JsonObject; - import static com.vaadin.flow.server.Constants.PACKAGE_JSON; import static com.vaadin.flow.server.Constants.PROJECT_FRONTEND_GENERATED_DIR_TOKEN; import static com.vaadin.flow.server.Constants.VAADIN_SERVLET_RESOURCES; @@ -169,6 +168,12 @@ private static Set calculateApplicableClassNames() { /** * Initialize the devmode server if not in production mode or compatibility * mode. + *

+ *

+ * Uses common ForkJoin pool to execute asynchronous tasks. It is + * recommended to use + * {@link #initDevModeHandler(Set, VaadinContext, Executor)} and provide a a + * custom executor if initialization starts long-running tasks. * * @param classes * classes to check for npm- and js modules @@ -179,9 +184,34 @@ private static Set calculateApplicableClassNames() { * * @throws VaadinInitializerException * if dev mode can't be initialized + * @deprecated use {@link #initDevModeHandler(Set, VaadinContext, Executor)} + * providing a custom executor. */ + @Deprecated(forRemoval = true) public static DevModeHandler initDevModeHandler(Set> classes, VaadinContext context) throws VaadinInitializerException { + return initDevModeHandler(classes, context, ForkJoinPool.commonPool()); + } + + /** + * Initialize the devmode server if not in production mode or compatibility + * mode. + * + * @param classes + * classes to check for npm- and js modules + * @param context + * VaadinContext we are running in + * @param taskExecutor + * the executor to use for asynchronous execution + * @return the initialized dev mode handler or {@code null} if none was + * created + * + * @throws VaadinInitializerException + * if dev mode can't be initialized + */ + public static DevModeHandler initDevModeHandler(Set> classes, + VaadinContext context, Executor taskExecutor) + throws VaadinInitializerException { ApplicationConfiguration config = ApplicationConfiguration.get(context); if (config.isProductionMode()) { @@ -317,7 +347,7 @@ public static DevModeHandler initDevModeHandler(Set> classes, }; CompletableFuture nodeTasksFuture = CompletableFuture - .runAsync(runnable); + .runAsync(runnable, taskExecutor); Lookup devServerLookup = Lookup.compose(lookup, Lookup.of(config, ApplicationConfiguration.class));