diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java index fb3c32dbe9bce..ed5870318db36 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java @@ -48,6 +48,8 @@ */ public class ApplicationLifecycleManager { + // used by ShutdownEvent to propagate the information about shutdown reason + public static volatile ShutdownEvent.ShutdownReason shutdownReason = ShutdownEvent.ShutdownReason.STANDARD; private static volatile BiConsumer defaultExitCodeHandler = new BiConsumer() { @Override public void accept(Integer integer, Throwable cause) { @@ -408,6 +410,8 @@ public void run() { //we let the application main thread take care of actually exiting //TODO: if the main thread is not actively waiting to exit should we interrupt it? shutdownRequested = true; + // so long as this thread is invoked, the app shutdown is considered non-standard + shutdownReason = ShutdownEvent.ShutdownReason.NON_STANDARD; try { stateCond.signalAll(); } finally { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ShutdownEvent.java b/core/runtime/src/main/java/io/quarkus/runtime/ShutdownEvent.java index 4583447e96430..8033a11bb7ba9 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ShutdownEvent.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ShutdownEvent.java @@ -1,7 +1,8 @@ package io.quarkus.runtime; /** - * Event that is fired before shutdown. + * Event that is fired before shutdown and can be inspected for shutdown cause. + * See {@link ShutdownEvent#isStandardShutdown()} * * This event is observed as follows: * @@ -14,4 +15,43 @@ * The annotated method can access other injected beans. */ public class ShutdownEvent { + + private ShutdownReason shutdownReason; + + public ShutdownEvent() { + this.shutdownReason = ShutdownReason.STANDARD; + } + + public ShutdownEvent(ShutdownReason shutdownReason) { + this.shutdownReason = shutdownReason; + } + + /** + * Returns {@code true} if the application shutdown is considered standard; i.e. by exiting {@code main()} method or + * executing either {@link Quarkus#asyncExit()} or {@link Quarkus#blockingExit()}. + *

+ * All other cases are non-standard - {@code SIGINT}, {@code SIGTERM}, {@code System.exit(n} and so on. + * Sending {@code CTRL + C} to running app in terminal is also non-standard shutdown. + * + * @return true if the app shutdown was standard, false otherwise + */ + public boolean isStandardShutdown() { + return shutdownReason.equals(ShutdownReason.STANDARD); + } + + /** + * An enum with values reflecting the reason for application shutdown. + */ + enum ShutdownReason { + /** + * When {@code main()} method exits or when either {@link Quarkus#asyncExit()} or + * {@link Quarkus#blockingExit()} was executed + */ + STANDARD, + /** + * All other cases - {@code SIGINT}, {@code SIGTERM}, {@code System.exit(n} and so on. + * This includes sending {@code CTRL + C} to running app in terminal. + */ + NON_STANDARD; + } } diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ArcRecorder.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ArcRecorder.java index 2f48f6ec8c526..b7859ac82f432 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ArcRecorder.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ArcRecorder.java @@ -19,6 +19,7 @@ import io.quarkus.arc.InjectableBean.Kind; import io.quarkus.arc.impl.ArcContainerImpl; import io.quarkus.arc.runtime.test.PreloadedTestApplicationClassPredicate; +import io.quarkus.runtime.ApplicationLifecycleManager; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; @@ -101,7 +102,7 @@ public void handleLifecycleEvents(ShutdownContext context, LaunchMode launchMode context.addShutdownTask(new Runnable() { @Override public void run() { - fireLifecycleEvent(container, new ShutdownEvent(), mockBeanClasses); + fireLifecycleEvent(container, new ShutdownEvent(ApplicationLifecycleManager.shutdownReason), mockBeanClasses); } }); }