From 097cf156dc36e1a74e0e440d6cdcfba279210f52 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 2 Dec 2020 14:33:35 +0300 Subject: [PATCH] fix: use provided executor for frontend tools (#9553) fixes vaadin/spring#691 --- .../vaadin/flow/server/DevModeHandler.java | 53 +++++++++------ .../server/startup/DevModeInitializer.java | 66 ++++++++++++------- 2 files changed, 76 insertions(+), 43 deletions(-) diff --git a/flow-server/src/main/java/com/vaadin/flow/server/DevModeHandler.java b/flow-server/src/main/java/com/vaadin/flow/server/DevModeHandler.java index 0237eddd3ee..469d055bb91 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/DevModeHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/DevModeHandler.java @@ -15,18 +15,9 @@ */ package com.vaadin.flow.server; -import static com.vaadin.flow.server.Constants.VAADIN_MAPPING; -import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_ERROR_PATTERN; -import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_OPTIONS; -import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_SUCCESS_PATTERN; -import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_TIMEOUT; -import static com.vaadin.flow.server.frontend.FrontendUtils.GREEN; -import static com.vaadin.flow.server.frontend.FrontendUtils.RED; -import static com.vaadin.flow.server.frontend.FrontendUtils.commandToString; -import static com.vaadin.flow.server.frontend.FrontendUtils.console; -import static java.lang.String.format; -import static java.net.HttpURLConnection.HTTP_NOT_FOUND; -import static java.net.HttpURLConnection.HTTP_OK; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; @@ -44,14 +35,12 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import java.util.regex.Pattern; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; @@ -64,6 +53,19 @@ import com.vaadin.flow.server.frontend.FrontendTools; import com.vaadin.flow.server.frontend.FrontendUtils; +import static com.vaadin.flow.server.Constants.VAADIN_MAPPING; +import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_ERROR_PATTERN; +import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_OPTIONS; +import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_SUCCESS_PATTERN; +import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_WEBPACK_TIMEOUT; +import static com.vaadin.flow.server.frontend.FrontendUtils.GREEN; +import static com.vaadin.flow.server.frontend.FrontendUtils.RED; +import static com.vaadin.flow.server.frontend.FrontendUtils.commandToString; +import static com.vaadin.flow.server.frontend.FrontendUtils.console; +import static java.lang.String.format; +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import static java.net.HttpURLConnection.HTTP_OK; + /** * Handles getting resources from webpack-dev-server. *

@@ -142,12 +144,23 @@ private DevModeHandler(DeploymentConfiguration config, int runningPort, reuseDevServer = config.reuseDevServer(); devServerPortFile = getDevServerPortFile(npmFolder); - devServerStartFuture = waitFor.whenCompleteAsync((value, exception) -> { + // Check whether executor is provided by the caller (framework) + Object service = config.getInitParameters().get(Executor.class); + + BiConsumer action = (value, exception) -> { // this will throw an exception if an exception has been thrown by // the waitFor task waitFor.getNow(null); runOnFutureComplete(config); - }); + }; + + if (service instanceof Executor) { + // if there is an executor use it to run the task + devServerStartFuture = waitFor.whenCompleteAsync(action, + (Executor) service); + } else { + devServerStartFuture = waitFor.whenCompleteAsync(action); + } } @@ -665,8 +678,10 @@ private boolean doStartWebpack(DeploymentConfiguration config, long ms = (System.nanoTime() - start) / 1000000; getLogger().info(LOG_END, ms); return true; - } catch (IOException | InterruptedException e) { + } catch (IOException e) { getLogger().error("Failed to start the webpack process", e); + } catch (InterruptedException e) { + getLogger().debug("Webpack process start has been interrupted", e); } return false; } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/startup/DevModeInitializer.java b/flow-server/src/main/java/com/vaadin/flow/server/startup/DevModeInitializer.java index a2c8096e8dd..108c62aa392 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/startup/DevModeInitializer.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/startup/DevModeInitializer.java @@ -45,6 +45,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -94,11 +95,11 @@ import static com.vaadin.flow.server.Constants.CONNECT_OPEN_API_FILE_TOKEN; import static com.vaadin.flow.server.Constants.PACKAGE_JSON; import static com.vaadin.flow.server.InitParameters.SERVLET_PARAMETER_DEVMODE_OPTIMIZE_BUNDLE; -import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_FLOW_RESOURCES_FOLDER; import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_CONNECT_APPLICATION_PROPERTIES; import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_CONNECT_GENERATED_TS_DIR; import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_CONNECT_JAVA_SOURCE_FOLDER; import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_CONNECT_OPENAPI_JSON_FILE; +import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_FLOW_RESOURCES_FOLDER; import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_FRONTEND_DIR; import static com.vaadin.flow.server.frontend.FrontendUtils.DEFAULT_GENERATED_DIR; import static com.vaadin.flow.server.frontend.FrontendUtils.PARAM_FRONTEND_DIR; @@ -117,7 +118,7 @@ NpmPackage.Container.class, JsModule.class, JsModule.Container.class, CssImport.class, CssImport.Container.class, JavaScript.class, JavaScript.Container.class, Theme.class, NoTheme.class, - AppShellConfigurator.class,HasErrorParameter.class }) + AppShellConfigurator.class, HasErrorParameter.class }) @WebListener public class DevModeInitializer implements ClassLoaderAwareServletContainerInitializer, Serializable, @@ -268,7 +269,8 @@ public static void initDevModeHandler(Set> classes, return; } - String baseDir = config.getStringProperty(FrontendUtils.PROJECT_BASEDIR, null); + String baseDir = config.getStringProperty(FrontendUtils.PROJECT_BASEDIR, + null); if (baseDir == null) { baseDir = getBaseDirectoryFallback(); } @@ -373,25 +375,23 @@ public static void initDevModeHandler(Set> classes, .withEmbeddableWebComponents(true).enablePnpm(enablePnpm) .withHomeNodeExecRequired(useHomeNodeExec).build(); - CompletableFuture runNodeTasks = CompletableFuture - .runAsync(() -> { - try { - tasks.execute(); - - FallbackChunk chunk = FrontendUtils - .readFallbackChunk(tokenFileData); - if (chunk != null) { - vaadinContext.setAttribute(chunk); - } - } catch (ExecutionFailedException exception) { - log().debug( - "Could not initialize dev mode handler. One of the node tasks failed", - exception); - throw new CompletionException(exception); - } - }); - - DevModeHandler.start(config, builder.npmFolder, runNodeTasks); + // Check whether executor is provided by the caller (framework) + Object service = config.getInitParameters().get(Executor.class); + + Runnable runnable = () -> runNodeTasks(vaadinContext, tokenFileData, + tasks); + + CompletableFuture nodeTasksFuture; + if (service instanceof Executor) { + // if there is an executor use it to run the task + nodeTasksFuture = CompletableFuture.runAsync(runnable, + (Executor) service); + } else { + nodeTasksFuture = CompletableFuture.runAsync(runnable); + + } + + DevModeHandler.start(config, builder.npmFolder, nodeTasksFuture); } /** @@ -444,12 +444,12 @@ private static String getBaseDirectoryFallback() { + "Directory '%s' does not look like a Maven or " + "Gradle project. Ensure that you have run the " + "prepare-frontend Maven goal, which generates " - +"'flow-build-info.json', prior to deploying your " + + "'flow-build-info.json', prior to deploying your " + "application", path.toString())); } } - + /* * This method returns all folders of jar files having files in the * META-INF/resources/frontend folder. We don't use URLClassLoader because @@ -465,6 +465,24 @@ static Set getFrontendLocationsFromClassloader( return frontendFiles; } + private static void runNodeTasks(VaadinContext vaadinContext, + JsonObject tokenFileData, NodeTasks tasks) { + try { + tasks.execute(); + + FallbackChunk chunk = FrontendUtils + .readFallbackChunk(tokenFileData); + if (chunk != null) { + vaadinContext.setAttribute(chunk); + } + } catch (ExecutionFailedException exception) { + log().debug( + "Could not initialize dev mode handler. One of the node tasks failed", + exception); + throw new CompletionException(exception); + } + } + private static Set getFrontendLocationsFromClassloader( ClassLoader classLoader, String resourcesFolder) throws ServletException {