From 7b5b6ab9537d970c559f7d6c676f49e8a2a8a203 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 8 Jul 2020 17:15:47 +1000 Subject: [PATCH] Remote dev mode improvements --- .../deployment/devmode/HttpRemoteDevClient.java | 4 ++-- .../vertx/http/runtime/VertxHttpRecorder.java | 16 ++++++++++++++-- .../http/runtime/devmode/RemoteSyncHandler.java | 11 +++++++++++ .../bootstrap/runner/DevModeMediator.java | 2 +- .../bootstrap/runner/QuarkusEntryPoint.java | 3 ++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java index dfe33351dcd56..a3992d7a07323 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/HttpRemoteDevClient.java @@ -71,9 +71,9 @@ private String doConnect(RemoteDevState initialState, Function, Map< //this file needs to be sent last //if it is modified it will trigger a reload //and we need the rest of the app to be present - byte[] lastFile = data.remove(QuarkusEntryPoint.QUARKUS_APPLICATION_DAT); + byte[] lastFile = data.remove(QuarkusEntryPoint.LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT); if (lastFile != null) { - data.put(QuarkusEntryPoint.QUARKUS_APPLICATION_DAT, lastFile); + data.put(QuarkusEntryPoint.LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT, lastFile); } for (Map.Entry entry : data.entrySet()) { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 93a4f51264745..6adaf0372da2a 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -109,6 +109,7 @@ public class VertxHttpRecorder { private static volatile Handler hotReplacementHandler; private static volatile HotReplacementContext hotReplacementContext; + private static volatile RemoteSyncHandler remoteSyncHandler; private static volatile Runnable closeTask; @@ -123,7 +124,14 @@ public void handle(HttpServerRequest httpServerRequest) { //as the underlying handler has not had a chance to install a read handler yet //and data that arrives while the blocking task is being processed will be lost httpServerRequest.pause(); - rootHandler.handle(httpServerRequest); + Handler rh = VertxHttpRecorder.rootHandler; + if (rh != null) { + rh.handle(httpServerRequest); + } else { + //very rare race condition, that can happen when dev mode is shutting down + httpServerRequest.resume(); + httpServerRequest.response().setStatusCode(503).end(); + } } }; @@ -374,7 +382,7 @@ public void handle(RoutingContext event) { }); } if (launchMode == LaunchMode.DEVELOPMENT && liveReloadConfig.password.isPresent()) { - root = new RemoteSyncHandler(liveReloadConfig.password.get(), root, hotReplacementContext); + root = remoteSyncHandler = new RemoteSyncHandler(liveReloadConfig.password.get(), root, hotReplacementContext); } rootHandler = root; } @@ -457,6 +465,10 @@ public void handle(AsyncResult event) { } } closeTask = null; + if (remoteSyncHandler != null) { + remoteSyncHandler.close(); + remoteSyncHandler = null; + } } } }; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RemoteSyncHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RemoteSyncHandler.java index 6714b1664bbba..46b0c0cf78c9e 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RemoteSyncHandler.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/RemoteSyncHandler.java @@ -6,6 +6,7 @@ import java.util.Base64; import java.util.Objects; import java.util.Set; +import java.util.concurrent.RejectedExecutionException; import org.jboss.logging.Logger; @@ -142,6 +143,10 @@ public void run() { } event.response().end(); } + } catch (RejectedExecutionException e) { + //everything is shut down + //likely in the middle of a restart + event.connection().close(); } catch (Exception e) { log.error("Connect failed", e); event.response().setStatusCode(500).end(); @@ -234,4 +239,10 @@ private boolean checkSession(HttpServerRequest event) { } return false; } + + public void close() { + synchronized (RemoteSyncHandler.class) { + RemoteSyncHandler.class.notifyAll(); + } + } } diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java index befdaae4883b0..a0876079b7e36 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java @@ -25,7 +25,7 @@ public class DevModeMediator { static void doDevMode(Path appRoot) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - Path deploymentClassPath = appRoot.resolve("lib/deployment/deployment-class-path.dat"); + Path deploymentClassPath = appRoot.resolve(QuarkusEntryPoint.LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT); Closeable closeable = doStart(appRoot, deploymentClassPath); Timer timer = new Timer("Classpath Change Timer", false); timer.schedule(new ChangeDetector(appRoot, deploymentClassPath, closeable), 1000, 1000); diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java index 792c63942b577..5c886b3a0f1ce 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/QuarkusEntryPoint.java @@ -16,6 +16,7 @@ public class QuarkusEntryPoint { public static final String QUARKUS_APPLICATION_DAT = "quarkus/quarkus-application.dat"; + public static final String LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT = "lib/deployment/deployment-class-path.dat"; public static void main(String... args) throws Throwable { System.setProperty("java.util.logging.manager", org.jboss.logmanager.LogManager.class.getName()); @@ -52,7 +53,7 @@ private static void doRun(Object args) throws IOException, ClassNotFoundExceptio private static void doReaugment(Path appRoot) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { try (ObjectInputStream in = new ObjectInputStream( - Files.newInputStream(appRoot.resolve("lib/deployment/deployment-class-path.dat")))) { + Files.newInputStream(appRoot.resolve(LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT)))) { List paths = (List) in.readObject(); //yuck, should use runner class loader URLClassLoader loader = new URLClassLoader(paths.stream().map((s) -> {