diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index 3d63057412663..63dad57889b28 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -88,6 +88,7 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable private final DevModeType devModeType; volatile Throwable compileProblem; volatile Throwable testCompileProblem; + volatile Throwable hotReloadProblem; private volatile Predicate disableInstrumentationForClassPredicate = new AlwaysFalsePredicate<>(); private volatile Predicate disableInstrumentationForIndexPredicate = new AlwaysFalsePredicate<>(); @@ -388,7 +389,8 @@ public List getResourcesDir() { public Throwable getDeploymentProblem() { //we differentiate between these internally, however for the error reporting they are the same return compileProblem != null ? compileProblem - : IsolatedDevModeMain.deploymentProblem; + : IsolatedDevModeMain.deploymentProblem != null ? IsolatedDevModeMain.deploymentProblem + : hotReloadProblem; } @Override @@ -551,13 +553,17 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) { return true; } else if (!filesChanged.isEmpty()) { - for (Consumer> consumer : noRestartChangesConsumers) { - try { + try { + for (Consumer> consumer : noRestartChangesConsumers) { consumer.accept(filesChanged); - } catch (Throwable t) { - log.error("Changed files consumer failed", t); } + hotReloadProblem = null; + getCompileOutput().setMessage(null); + } catch (Throwable t) { + hotReloadProblem = t; + getCompileOutput().setMessage(t.getMessage()); } + log.infof("Files changed but restart not needed - notified extensions in: %ss ", Timing.convertToBigDecimalSeconds(System.nanoTime() - startNanoseconds)); } else if (instrumentationChange) { diff --git a/integration-tests/test-extension/extension/deployment/src/main/java/io/quarkus/extest/deployment/HotReplacementProcessor.java b/integration-tests/test-extension/extension/deployment/src/main/java/io/quarkus/extest/deployment/HotReplacementProcessor.java new file mode 100644 index 0000000000000..121f2fb7d9242 --- /dev/null +++ b/integration-tests/test-extension/extension/deployment/src/main/java/io/quarkus/extest/deployment/HotReplacementProcessor.java @@ -0,0 +1,12 @@ +package io.quarkus.extest.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; +import io.quarkus.extest.runtime.TestHotReplacementSetup; + +public class HotReplacementProcessor { + @BuildStep + public HotDeploymentWatchedFileBuildItem registerHotReplacementFile() { + return new HotDeploymentWatchedFileBuildItem(TestHotReplacementSetup.HOT_REPLACEMENT_FILE, false); + } +} diff --git a/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/HotReplacementException.java b/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/HotReplacementException.java new file mode 100644 index 0000000000000..04b1b97b22765 --- /dev/null +++ b/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/HotReplacementException.java @@ -0,0 +1,5 @@ +package io.quarkus.extest.runtime; + +public class HotReplacementException extends Exception { + +} diff --git a/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/TestHotReplacementSetup.java b/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/TestHotReplacementSetup.java new file mode 100644 index 0000000000000..e332fa6d44cbc --- /dev/null +++ b/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/TestHotReplacementSetup.java @@ -0,0 +1,49 @@ +package io.quarkus.extest.runtime; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +import io.quarkus.dev.ErrorPageGenerators; +import io.quarkus.dev.spi.HotReplacementContext; +import io.quarkus.dev.spi.HotReplacementSetup; + +public class TestHotReplacementSetup implements HotReplacementSetup { + + public final static String HOT_REPLACEMENT_FILE = "hot.replacement"; + + private static final String HOT_REPLACEMENT_EXCEPTION = HotReplacementException.class.getName(); + + private HotReplacementContext context; + + @Override + public void setupHotDeployment(HotReplacementContext context) { + context.consumeNoRestartChanges(this::noRestartChanges); + this.context = context; + ErrorPageGenerators.register(HOT_REPLACEMENT_EXCEPTION, this::generatePage); + } + + public void noRestartChanges(Set changedFiles) { + if (changedFiles.contains(HOT_REPLACEMENT_FILE)) { + for (Path resourcePath : context.getResourcesDir()) { + Path myFile = resourcePath.resolve(HOT_REPLACEMENT_FILE); + if (Files.exists(myFile)) { + try { + String contents = Files.readString(myFile); + if ("throw".equals(contents)) + throw new RuntimeException(new HotReplacementException()); + return; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + } + } + + private String generatePage(Throwable x) { + return "Generated page for exception " + x; + } +} diff --git a/integration-tests/test-extension/extension/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup b/integration-tests/test-extension/extension/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup new file mode 100644 index 0000000000000..fa941d5fb246c --- /dev/null +++ b/integration-tests/test-extension/extension/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup @@ -0,0 +1 @@ +io.quarkus.extest.runtime.TestHotReplacementSetup diff --git a/integration-tests/test-extension/tests/pom.xml b/integration-tests/test-extension/tests/pom.xml index 37f0c9eb05582..006dc4c8e25be 100644 --- a/integration-tests/test-extension/tests/pom.xml +++ b/integration-tests/test-extension/tests/pom.xml @@ -29,6 +29,11 @@ quarkus-junit5 test + + io.quarkus + quarkus-junit5-internal + test + io.rest-assured rest-assured @@ -96,6 +101,30 @@ maven-surefire-plugin + + + default-test + + test + + + + io/quarkus/it/extension/HotReplacementSetupDevModeTest.java + + + + + quarkus-test + + test + + + + io/quarkus/it/extension/HotReplacementSetupDevModeTest.java + + + + diff --git a/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/HotReplacementSetupDevModeTest.java b/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/HotReplacementSetupDevModeTest.java new file mode 100644 index 0000000000000..4d05a761e911e --- /dev/null +++ b/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/HotReplacementSetupDevModeTest.java @@ -0,0 +1,35 @@ +package io.quarkus.it.extension; + +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.extest.runtime.TestHotReplacementSetup; +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +public class HotReplacementSetupDevModeTest { + + @RegisterExtension + static final QuarkusDevModeTest TEST = new QuarkusDevModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(SystemPropertyTestEndpoint.class) + .addAsResource(new StringAsset("nothing"), TestHotReplacementSetup.HOT_REPLACEMENT_FILE)); + + @Test + public void watched() { + RestAssured.get("/core/sysprop") + .then() + .statusCode(200); + TEST.modifyResourceFile(TestHotReplacementSetup.HOT_REPLACEMENT_FILE, text -> "throw"); + RestAssured.get("/core/sysprop") + .then() + .statusCode(500) + .body(Matchers.containsString("Generated page for exception")); + TEST.modifyResourceFile(TestHotReplacementSetup.HOT_REPLACEMENT_FILE, text -> "nothing"); + RestAssured.get("/core/sysprop") + .then() + .statusCode(200); + } +}