From 3900a2ee1a2544261445ee8fbd6fab8c19aa739c Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Tue, 20 Dec 2022 17:30:03 -0600 Subject: [PATCH] Introduce `ImageMode` API This class can be used by applications and components to know whether they are currently executing in JVM mode or within a native image build or run, in a way which is agnostic to what image generator (or version thereof) is currently being used. --- .../runtime/ApplicationLifecycleManager.java | 3 +- .../java/io/quarkus/runtime/ImageMode.java | 48 +++++++++++++++++++ .../NativeImageRuntimePropertiesRecorder.java | 4 +- .../configuration/ConfigDiagnostic.java | 6 +-- .../runtime/logging/LoggingSetupRecorder.java | 4 +- .../runtime/MicrometerRecorder.java | 4 +- .../runtime/QuarkusRestClientBuilder.java | 4 +- .../runtime/SmallRyeMetricsRecorder.java | 16 +++---- .../rest-client/QuarkusRestClientBuilder.java | 4 +- 9 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/ImageMode.java 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 b629a4d4fa99c..aa824fd8ad3f6 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java @@ -18,7 +18,6 @@ import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logging.Logger; import org.jboss.logmanager.handlers.AsyncHandler; import org.wildfly.common.lock.Locks; @@ -269,7 +268,7 @@ private static void longLivedPostBootCleanup() { } private static void registerHooks(final BiConsumer exitCodeHandler) { - if (ImageInfo.inImageRuntimeCode() && System.getenv(DISABLE_SIGNAL_HANDLERS) == null) { + if (ImageMode.current() == ImageMode.NATIVE_RUN && System.getenv(DISABLE_SIGNAL_HANDLERS) == null) { registerSignalHandlers(exitCodeHandler); } shutdownHookThread = new ShutdownHookThread(); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ImageMode.java b/core/runtime/src/main/java/io/quarkus/runtime/ImageMode.java new file mode 100644 index 0000000000000..e66e6e3cbb966 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/ImageMode.java @@ -0,0 +1,48 @@ +package io.quarkus.runtime; + +import org.graalvm.nativeimage.ImageInfo; + +/** + * The image execution mode of the application. + */ +public enum ImageMode { + /** + * The image mode which indicates that the application is running in a standard JVM. + */ + JVM, + /** + * The image mode which indicates that the application is currently executing the build phase of a native static image. + */ + NATIVE_BUILD, + /** + * The image mode which indicates that the application is a native static image which is currently running on a target + * system. + */ + NATIVE_RUN, + ; + + /** + * Determine whether the application image is a native static image. + * + * @return {@code true} if the application image is a native static image, or {@code false} otherwise + */ + public boolean isNativeImage() { + return current() != JVM; + } + + /** + * Get the current image mode. Note that it is possible for the image mode to change during the lifetime of + * an application. + * + * @return the image mode (not {@code null}) + */ + public static ImageMode current() { + if (ImageInfo.inImageBuildtimeCode()) { + return NATIVE_BUILD; + } else if (ImageInfo.inImageRuntimeCode()) { + return NATIVE_RUN; + } else { + return JVM; + } + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/NativeImageRuntimePropertiesRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/NativeImageRuntimePropertiesRecorder.java index 1a261d7585294..23b5c36c4bff4 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/NativeImageRuntimePropertiesRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/NativeImageRuntimePropertiesRecorder.java @@ -3,8 +3,6 @@ import java.util.HashMap; import java.util.Map; -import org.graalvm.nativeimage.ImageInfo; - import io.quarkus.runtime.annotations.Recorder; /** @@ -16,7 +14,7 @@ public class NativeImageRuntimePropertiesRecorder { private static final Map MAP = new HashMap<>(); public void setInStaticInit(String name, String value) { - if (ImageInfo.inImageBuildtimeCode()) { + if (ImageMode.current() == ImageMode.NATIVE_BUILD) { MAP.put(name, value); } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java index ac4fc458b9573..cc113466940e6 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java @@ -10,9 +10,9 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logging.Logger; +import io.quarkus.runtime.ImageMode; import io.smallrye.config.common.utils.StringUtil; /** @@ -107,7 +107,7 @@ public static void unknownProperties(List properties) { } public static void unknownRunTime(String name) { - if (ImageInfo.inImageRuntimeCode()) { + if (ImageMode.current() == ImageMode.NATIVE_RUN) { // only warn at run time for native images, otherwise the user will get warned twice for every property unknown(name); } @@ -118,7 +118,7 @@ public static void unknownRunTime(NameIterator name) { } public static void unknownPropertiesRuntime(List properties) { - if (ImageInfo.inImageRuntimeCode()) { + if (ImageMode.current() == ImageMode.NATIVE_RUN) { unknownProperties(properties); } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java index 8c872044359bd..4a956e57bba69 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java @@ -26,7 +26,6 @@ import java.util.logging.LogManager; import java.util.logging.LogRecord; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logmanager.EmbeddedConfigurator; import org.jboss.logmanager.LogContext; import org.jboss.logmanager.Logger; @@ -44,6 +43,7 @@ import io.quarkus.bootstrap.logging.InitialConfigurator; import io.quarkus.dev.console.CurrentAppExceptionHighlighter; import io.quarkus.dev.testing.ExceptionReporting; +import io.quarkus.runtime.ImageMode; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; @@ -492,7 +492,7 @@ private static void addNamedHandlersToRootHandlers(Optional> handle } public void initializeLoggingForImageBuild() { - if (ImageInfo.inImageBuildtimeCode()) { + if (ImageMode.current() == ImageMode.NATIVE_BUILD) { final ConsoleHandler handler = new ConsoleHandler(new PatternFormatter( "%d{HH:mm:ss,SSS} %-5p [%c{1.}] %s%e%n")); handler.setLevel(Level.INFO); diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/MicrometerRecorder.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/MicrometerRecorder.java index 4ed9d324b4dc1..aaab2f94a3283 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/MicrometerRecorder.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/MicrometerRecorder.java @@ -14,7 +14,6 @@ import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logging.Logger; import io.micrometer.core.instrument.Meter; @@ -37,6 +36,7 @@ import io.quarkus.micrometer.runtime.config.runtime.HttpClientConfig; import io.quarkus.micrometer.runtime.config.runtime.HttpServerConfig; import io.quarkus.micrometer.runtime.config.runtime.VertxConfig; +import io.quarkus.runtime.ImageMode; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; @@ -113,7 +113,7 @@ public void configureRegistries(MicrometerConfig config, new JvmMemoryMetrics().bindTo(Metrics.globalRegistry); new JvmThreadMetrics().bindTo(Metrics.globalRegistry); new JVMInfoBinder().bindTo(Metrics.globalRegistry); - if (!ImageInfo.inImageCode()) { + if (ImageMode.current() == ImageMode.JVM) { new JvmGcMetrics().bindTo(Metrics.globalRegistry); } } diff --git a/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/QuarkusRestClientBuilder.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/QuarkusRestClientBuilder.java index 5a8136fd83e28..d96ca2680894c 100644 --- a/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/QuarkusRestClientBuilder.java +++ b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/QuarkusRestClientBuilder.java @@ -57,7 +57,6 @@ import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logging.Logger; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; @@ -84,6 +83,7 @@ import io.quarkus.restclient.NoopHostnameVerifier; import io.quarkus.resteasy.common.runtime.QuarkusInjectorFactory; +import io.quarkus.runtime.ImageMode; import io.quarkus.runtime.graal.DisabledSSLContext; import io.quarkus.runtime.ssl.SslContextConfiguration; @@ -324,7 +324,7 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi configureTrustAll(resteasyClientBuilder); // we need to push a disabled SSL context when SSL has been disabled // because otherwise Apache HTTP Client will try to initialize one and will fail - if (ImageInfo.inImageRuntimeCode() && !SslContextConfiguration.isSslNativeEnabled()) { + if (ImageMode.current() == ImageMode.NATIVE_RUN && !SslContextConfiguration.isSslNativeEnabled()) { resteasyClientBuilder.sslContext(new DisabledSSLContext()); } diff --git a/extensions/smallrye-metrics/runtime/src/main/java/io/quarkus/smallrye/metrics/runtime/SmallRyeMetricsRecorder.java b/extensions/smallrye-metrics/runtime/src/main/java/io/quarkus/smallrye/metrics/runtime/SmallRyeMetricsRecorder.java index 08966f4712840..840be46047a59 100644 --- a/extensions/smallrye-metrics/runtime/src/main/java/io/quarkus/smallrye/metrics/runtime/SmallRyeMetricsRecorder.java +++ b/extensions/smallrye-metrics/runtime/src/main/java/io/quarkus/smallrye/metrics/runtime/SmallRyeMetricsRecorder.java @@ -28,10 +28,10 @@ import org.eclipse.microprofile.metrics.MetricUnits; import org.eclipse.microprofile.metrics.Tag; import org.eclipse.microprofile.metrics.Timer; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logging.Logger; import io.quarkus.arc.runtime.BeanContainer; +import io.quarkus.runtime.ImageMode; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.metrics.MetricsFactory; @@ -328,7 +328,7 @@ public Number getValue() { // some metrics are only available in jdk internal class 'com.sun.management.OperatingSystemMXBean': cast to it. // com.sun.management.OperatingSystemMXBean is not available in SubstratVM // the cast will fail for some JVM not derived from HotSpot (J9 for example) so we check if it is assignable to it - if (!ImageInfo.inImageCode() + if (ImageMode.current() == ImageMode.JVM && com.sun.management.OperatingSystemMXBean.class.isAssignableFrom(operatingSystemMXBean.getClass())) { try { com.sun.management.OperatingSystemMXBean internalOperatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) operatingSystemMXBean; @@ -367,7 +367,7 @@ private void vendorOperatingSystemMetrics(MetricRegistry registry) { // some metrics are only available in jdk internal class 'com.sun.management.OperatingSystemMXBean': cast to it. // com.sun.management.OperatingSystemMXBean is not available in SubstratVM // the cast will fail for some JVM not derived from HotSpot (J9 for example) so we check if it is assignable to it - if (!ImageInfo.inImageCode() + if (ImageMode.current() == ImageMode.JVM && com.sun.management.OperatingSystemMXBean.class.isAssignableFrom(operatingSystemMXBean.getClass())) { try { com.sun.management.OperatingSystemMXBean internalOperatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) operatingSystemMXBean; @@ -608,7 +608,7 @@ public Number getValue() { private void memoryPoolMetrics(MetricRegistry registry) { // MemoryPoolMXBean doesn't work in native mode - if (!ImageInfo.inImageCode()) { + if (ImageMode.current() == ImageMode.JVM) { List mps = ManagementFactory.getMemoryPoolMXBeans(); Metadata usageMetadata = Metadata.builder() .withName("memoryPool.usage") @@ -665,7 +665,7 @@ public Number getValue() { } private void micrometerJvmGcMetrics(MetricRegistry registry, ShutdownContext shutdownContext) { - if (!ImageInfo.inImageCode()) { + if (ImageMode.current() == ImageMode.JVM) { MicrometerGCMetrics gcMetrics = new MicrometerGCMetrics(); registry.register(new ExtendedMetadataBuilder() @@ -815,7 +815,7 @@ public Number getValue() { } }); - if (!ImageInfo.inImageCode()) { + if (ImageMode.current() == ImageMode.JVM) { ExtendedMetadata threadStatesMetadata = new ExtendedMetadataBuilder() .withName("jvm.threads.states") .withType(MetricType.GAUGE) @@ -837,7 +837,7 @@ public Number getValue() { } private void micrometerJvmMemoryMetrics(MetricRegistry registry) { - if (!ImageInfo.inImageCode()) { + if (ImageMode.current() == ImageMode.JVM) { for (MemoryPoolMXBean memoryPoolMXBean : ManagementFactory.getMemoryPoolMXBeans()) { String area = MemoryType.HEAP.equals(memoryPoolMXBean.getType()) ? "heap" : "nonheap"; Tag[] tags = new Tag[] { new Tag("id", memoryPoolMXBean.getName()), @@ -951,7 +951,7 @@ public Number getValue() { private void micrometerJvmClassLoaderMetrics(MetricRegistry registry) { // The ClassLoadingMXBean can be used in native mode, but it only returns zeroes, so there's no point in including such metrics. - if (!ImageInfo.inImageCode()) { + if (ImageMode.current() == ImageMode.JVM) { ClassLoadingMXBean classLoadingBean = ManagementFactory.getClassLoadingMXBean(); registry.register( diff --git a/jakarta/overrides/rest-client/QuarkusRestClientBuilder.java b/jakarta/overrides/rest-client/QuarkusRestClientBuilder.java index 501e18f059b09..07dd31a1dcaea 100644 --- a/jakarta/overrides/rest-client/QuarkusRestClientBuilder.java +++ b/jakarta/overrides/rest-client/QuarkusRestClientBuilder.java @@ -50,6 +50,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.ext.ParamConverterProvider; +import io.quarkus.runtime.ImageMode; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.rest.client.RestClientBuilder; @@ -59,7 +60,6 @@ import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.graalvm.nativeimage.ImageInfo; import org.jboss.logging.Logger; import org.jboss.resteasy.client.jaxrs.ClientHttpEngine; import org.jboss.resteasy.client.jaxrs.ResteasyClient; @@ -338,7 +338,7 @@ public T build(Class aClass, ClientHttpEngine httpEngine) configureTrustAll(resteasyClientBuilder); // we need to push a disabled SSL context when SSL has been disabled // because otherwise Apache HTTP Client will try to initialize one and will fail - if (ImageInfo.inImageRuntimeCode() && !SslContextConfiguration.isSslNativeEnabled()) { + if (ImageMode.current() == ImageMode.NATIVE_RUN && !SslContextConfiguration.isSslNativeEnabled()) { resteasyClientBuilder.sslContext(new DisabledSSLContext()); }