diff --git a/.github/workflows/graalvm.yml b/.github/workflows/graalvm.yml index d03c43c702d..c06db472f01 100644 --- a/.github/workflows/graalvm.yml +++ b/.github/workflows/graalvm.yml @@ -19,14 +19,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - graalvm: [ 'latest', 'dev' ] + graalvm: [ 'latest'] java: [ '17' ] - include: - - graalvm: 'dev' - java: '19' - exclude: - - graalvm: 'dev' - java: '17' steps: # https://github.com/actions/virtual-environments/issues/709 - name: Free disk space diff --git a/buffer-netty/src/main/java/io/micronaut/buffer/netty/NettyFeature.java b/buffer-netty/src/main/java/io/micronaut/buffer/netty/NettyFeature.java deleted file mode 100644 index dc320f7c73f..00000000000 --- a/buffer-netty/src/main/java/io/micronaut/buffer/netty/NettyFeature.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017-2020 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.micronaut.buffer.netty; - -import com.oracle.svm.core.jdk.SystemPropertiesSupport; -import io.micronaut.core.annotation.Internal; -import io.micronaut.core.graal.AutomaticFeatureUtils; -import io.netty.util.internal.logging.InternalLoggerFactory; -import io.netty.util.internal.logging.Slf4JLoggerFactory; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; -import org.graalvm.nativeimage.hosted.RuntimeReflection; - -import java.lang.reflect.Method; -import java.util.Arrays; - -/** - * A native image feature that configures the netty library. - * - * @author Jonas Konrad - * @since 3.3.0 - */ -@Internal -final class NettyFeature implements Feature { - @Override - public void beforeAnalysis(BeforeAnalysisAccess access) { - RuntimeClassInitialization.initializeAtRunTime("io.netty"); - RuntimeClassInitialization.initializeAtBuildTime( - "io.netty.util.internal.shaded.org.jctools", - "io.netty.util.internal.logging.InternalLoggerFactory", - "io.netty.util.internal.logging.Slf4JLoggerFactory", - "io.netty.util.internal.logging.LocationAwareSlf4JLogger" - ); - - // force netty to use slf4j logging - try { - InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE); - } catch (Throwable e) { - // can fail if no-op logger is on the classpath - } - - registerClasses(access, - "io.netty.channel.kqueue.KQueueChannelOption", "io.netty.channel.epoll.EpollChannelOption"); - - registerMethods(access, "io.netty.buffer.AbstractByteBufAllocator", "toLeakAwareBuffer"); - registerMethods(access, "io.netty.buffer.AdvancedLeakAwareByteBuf", "touch", "recordLeakNonRefCountingOperation"); - registerMethods(access, "io.netty.util.ReferenceCountUtil", "touch"); - - System.setProperty("io.netty.tryReflectionSetAccessible", "true"); - ImageSingletons.lookup(SystemPropertiesSupport.class).initializeProperty("io.netty.tryReflectionSetAccessible", "true"); - try { - RuntimeReflection.register(access.findClassByName("java.nio.DirectByteBuffer").getDeclaredConstructor(long.class, int.class)); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - Class unsafeOld = access.findClassByName("sun.misc.Unsafe"); - if (unsafeOld != null) { - try { - RuntimeReflection.register(unsafeOld.getDeclaredMethod("allocateUninitializedArray", Class.class, int.class)); - } catch (NoSuchMethodException ignored) { - } - } - Class unsafeNew = access.findClassByName("jdk.internal.misc.Unsafe"); - if (unsafeNew != null) { - try { - RuntimeReflection.register(unsafeNew.getDeclaredMethod("allocateUninitializedArray", Class.class, int.class)); - } catch (NoSuchMethodException ignored) { - } - } - } - - private void registerClasses(BeforeAnalysisAccess access, String... classes) { - for (String clazz : classes) { - AutomaticFeatureUtils.registerClassForRuntimeReflection(access, clazz); - AutomaticFeatureUtils.registerFieldsForRuntimeReflection(access, clazz); - } - } - - private void registerMethods(BeforeAnalysisAccess access, String clz, String... methods) { - for (Method declaredMethod : access.findClassByName(clz).getDeclaredMethods()) { - if (Arrays.asList(methods).contains(declaredMethod.getName())) { - RuntimeReflection.register(declaredMethod); - } - } - } -} diff --git a/buffer-netty/src/main/resources/META-INF/native-image/io.micronaut/micronaut-buffer-netty/native-image.properties b/buffer-netty/src/main/resources/META-INF/native-image/io.micronaut/micronaut-buffer-netty/native-image.properties deleted file mode 100644 index 7552ba4d263..00000000000 --- a/buffer-netty/src/main/resources/META-INF/native-image/io.micronaut/micronaut-buffer-netty/native-image.properties +++ /dev/null @@ -1 +0,0 @@ -Args = --features=io.micronaut.buffer.netty.NettyFeature diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index b0e94ba8091..d75793b323a 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -13,5 +13,5 @@ dependencies { implementation "org.tomlj:tomlj:1.1.0" implementation "me.champeau.gradle:japicmp-gradle-plugin:0.4.1" - implementation "org.graalvm.buildtools.native:org.graalvm.buildtools.native.gradle.plugin:0.9.14" + implementation "org.graalvm.buildtools.native:org.graalvm.buildtools.native.gradle.plugin:0.9.20" } diff --git a/core/src/main/java/io/micronaut/core/graal/AutomaticFeatureUtils.java b/core/src/main/java/io/micronaut/core/graal/AutomaticFeatureUtils.java index b46c68d4a64..5cf1d3311f6 100644 --- a/core/src/main/java/io/micronaut/core/graal/AutomaticFeatureUtils.java +++ b/core/src/main/java/io/micronaut/core/graal/AutomaticFeatureUtils.java @@ -15,13 +15,12 @@ */ package io.micronaut.core.graal; -import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import io.micronaut.core.util.ArrayUtils; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature.BeforeAnalysisAccess; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; +import org.graalvm.nativeimage.hosted.RuntimeProxyCreation; import org.graalvm.nativeimage.hosted.RuntimeReflection; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -36,7 +35,9 @@ * @author Álvaro Sánchez-Mariscal * @author graemerocher * @since 2.0.0 + * @deprecated Use GraalVM's own public API under {@code org.graalvm} or conditional metadata in JSON format instead */ +@Deprecated public final class AutomaticFeatureUtils { /** @@ -176,7 +177,7 @@ public static void addProxyClass(BeforeAnalysisAccess access, String... interfac } } if (classList.size() == interfaces.length) { - ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(classList.toArray(new Class[interfaces.length])); + RuntimeProxyCreation.register(classList.toArray(new Class[interfaces.length])); } } @@ -187,12 +188,12 @@ public static void addProxyClass(BeforeAnalysisAccess access, String... interfac */ public static void addResourcePatterns(String... patterns) { if (ArrayUtils.isNotEmpty(patterns)) { - ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - if (resourcesRegistry != null) { for (String resource : patterns) { - resourcesRegistry.addResources(resource); + RuntimeResourceAccess.addResource( + AutomaticFeatureUtils.class.getClassLoader().getUnnamedModule(), + resource + ); } - } } } @@ -203,11 +204,11 @@ public static void addResourcePatterns(String... patterns) { */ public static void addResourceBundles(String... bundles) { if (ArrayUtils.isNotEmpty(bundles)) { - ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - if (resourcesRegistry != null) { - for (String resource : bundles) { - resourcesRegistry.addResourceBundles(resource); - } + for (String resource : bundles) { + RuntimeResourceAccess.addResourceBundle( + AutomaticFeatureUtils.class.getClassLoader().getUnnamedModule(), + resource + ); } } } diff --git a/core/src/main/java/io/micronaut/core/graal/LoggingFeature.java b/core/src/main/java/io/micronaut/core/graal/LoggingFeature.java deleted file mode 100644 index 3d89677e5bf..00000000000 --- a/core/src/main/java/io/micronaut/core/graal/LoggingFeature.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017-2022 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.micronaut.core.graal; - -import io.micronaut.core.annotation.Internal; -import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.hosted.RuntimeReflection; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.util.Objects; -import java.util.stream.Stream; - -/** - * Configures logback for runtime reflection if required. - * - * @since 4.0.0 - * @author graemerocher - */ -@Internal -public final class LoggingFeature implements Feature { - - @Override - public void beforeAnalysis(BeforeAnalysisAccess access) { - Stream.of("ch.qos.logback.classic.encoder.PatternLayoutEncoder", - "ch.qos.logback.classic.pattern.DateConverter", - "ch.qos.logback.classic.pattern.LevelConverter", - "ch.qos.logback.classic.pattern.LineSeparatorConverter", - "ch.qos.logback.classic.pattern.LoggerConverter", - "ch.qos.logback.classic.pattern.MessageConverter", - "ch.qos.logback.classic.pattern.ThreadConverter", - "ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter", - "ch.qos.logback.core.ConsoleAppender", - "ch.qos.logback.core.OutputStreamAppender", - "ch.qos.logback.core.encoder.LayoutWrappingEncoder", - "ch.qos.logback.core.pattern.PatternLayoutEncoderBase", - "ch.qos.logback.core.pattern.color.CyanCompositeConverter", - "ch.qos.logback.core.pattern.color.GrayCompositeConverter", - "ch.qos.logback.core.pattern.color.MagentaCompositeConverter") - .map(access::findClassByName) - .filter(Objects::nonNull) - .forEach(t -> { - RuntimeReflection.registerForReflectiveInstantiation(t); - RuntimeReflection.register(t); - Constructor[] declaredConstructors = t.getConstructors(); - for (Constructor c : declaredConstructors) { - RuntimeReflection.register(c); - } - Method[] methods = t.getMethods(); - for (Method method : methods) { - RuntimeReflection.register(method); - } - }); - } -} diff --git a/core/src/main/java/io/micronaut/core/graal/ServiceLoaderInitialization.java b/core/src/main/java/io/micronaut/core/io/service/ServiceLoaderInitialization.java similarity index 89% rename from core/src/main/java/io/micronaut/core/graal/ServiceLoaderInitialization.java rename to core/src/main/java/io/micronaut/core/io/service/ServiceLoaderInitialization.java index 9580646436f..dfe17ac7ca1 100644 --- a/core/src/main/java/io/micronaut/core/graal/ServiceLoaderInitialization.java +++ b/core/src/main/java/io/micronaut/core/io/service/ServiceLoaderInitialization.java @@ -13,16 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micronaut.core.graal; +package io.micronaut.core.io.service; -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; import io.micronaut.core.annotation.AnnotationValue; -import io.micronaut.core.annotation.Internal; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.beans.BeanInfo; +import io.micronaut.core.graal.GraalReflectionConfigurer; import io.micronaut.core.io.IOUtils; -import io.micronaut.core.io.service.SoftServiceLoader; +import io.micronaut.core.io.service.ServiceScanner.StaticServiceDefinitions; import io.micronaut.core.reflect.exception.InstantiationException; import io.micronaut.core.util.ArrayUtils; import org.graalvm.nativeimage.ImageSingletons; @@ -43,13 +41,10 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -67,7 +62,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { configureForReflection(access); StaticServiceDefinitions staticServiceDefinitions = buildStaticServiceDefinitions(access); - final Collection> allTypeNames = staticServiceDefinitions.serviceTypeMap.values(); + final Collection> allTypeNames = staticServiceDefinitions.serviceTypeMap().values(); for (Set typeNameSet : allTypeNames) { Iterator i = typeNameSet.iterator(); serviceLoop: while (i.hasNext()) { @@ -119,7 +114,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { @NonNull private StaticServiceDefinitions buildStaticServiceDefinitions(BeforeAnalysisAccess access) { - StaticServiceDefinitions staticServiceDefinitions = new StaticServiceDefinitions(); + StaticServiceDefinitions staticServiceDefinitions = new StaticServiceDefinitions(null); final String path = "META-INF/micronaut/"; try { final Enumeration micronautResources = access.getApplicationClassLoader().getResources(path); @@ -160,7 +155,7 @@ private StaticServiceDefinitions buildStaticServiceDefinitions(BeforeAnalysisAcc servicePath, serviceTypePath -> { if (Files.isRegularFile(serviceTypePath)) { - final Set serviceTypeNames = staticServiceDefinitions.serviceTypeMap + final Set serviceTypeNames = staticServiceDefinitions.serviceTypeMap() .computeIfAbsent(servicePath, key -> new HashSet<>()); final String serviceTypeName = serviceTypePath.getFileName().toString(); @@ -215,24 +210,4 @@ public void register(Constructor... constructors) { } } -@Internal -final class StaticServiceDefinitions { - final Map> serviceTypeMap = new HashMap<>(); -} - -@SuppressWarnings("unused") -@TargetClass(className = "io.micronaut.core.io.service.ServiceScanner") -@Internal -final class ServiceLoaderInitialization { - private ServiceLoaderInitialization() { - } - @Substitute - private static Set computeMicronautServiceTypeNames(URI uri, String path) { - final StaticServiceDefinitions ssd = ImageSingletons.lookup(StaticServiceDefinitions.class); - return ssd.serviceTypeMap.getOrDefault( - path, - Collections.emptySet() - ); - } -} diff --git a/core/src/main/java/io/micronaut/core/io/service/ServiceScanner.java b/core/src/main/java/io/micronaut/core/io/service/ServiceScanner.java index 2bde13fd5f5..8ca26ebfc54 100644 --- a/core/src/main/java/io/micronaut/core/io/service/ServiceScanner.java +++ b/core/src/main/java/io/micronaut/core/io/service/ServiceScanner.java @@ -16,7 +16,9 @@ package io.micronaut.core.io.service; import io.micronaut.core.annotation.Internal; +import io.micronaut.core.annotation.Nullable; import io.micronaut.core.io.IOUtils; +import org.graalvm.nativeimage.ImageSingletons; import java.io.BufferedReader; import java.io.IOException; @@ -30,10 +32,13 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.ServiceConfigurationError; import java.util.Set; import java.util.concurrent.ForkJoinPool; @@ -79,20 +84,48 @@ private static URI normalizeFilePath(String path, URI uri) { */ @SuppressWarnings("java:S3398") private static Set computeMicronautServiceTypeNames(URI uri, String path) { - Set typeNames = new HashSet<>(); - // Keep the anonymous class instead of Lambda to reduce the Lambda invocation overhead during the startup - Consumer consumer = new Consumer<>() { - - @Override - public void accept(Path currentPath) { - if (Files.isRegularFile(currentPath)) { - final String typeName = currentPath.getFileName().toString(); - typeNames.add(typeName); + final StaticServiceDefinitions ssd = findStaticServiceDefinitions(); + if (ssd != null) { + return ssd.serviceTypeMap.getOrDefault( + path, + Collections.emptySet() + ); + } else { + Set typeNames = new HashSet<>(); + // Keep the anonymous class instead of Lambda to reduce the Lambda invocation overhead during the startup + @SuppressWarnings({"Convert2Lambda", "java:S1604"}) Consumer consumer = new Consumer<>() { + + @Override + public void accept(Path currentPath) { + if (Files.isRegularFile(currentPath)) { + final String typeName = currentPath.getFileName().toString(); + typeNames.add(typeName); + } } - } - }; - IOUtils.eachFile(uri, path, consumer); - return typeNames; + }; + IOUtils.eachFile(uri, path, consumer); + return typeNames; + } + } + + @Nullable + private static StaticServiceDefinitions findStaticServiceDefinitions() { + if (hasImageSingletons()) { + return ImageSingletons.contains(StaticServiceDefinitions.class) ? ImageSingletons.lookup(StaticServiceDefinitions.class) : null; + } else { + return null; + } + } + + @SuppressWarnings("java:S1181") + private static boolean hasImageSingletons() { + try { + //noinspection ConstantValue + return ImageSingletons.class != null; + } catch (Throwable e) { + // not present or not a GraalVM JDK + return false; + } } @SuppressWarnings("java:S3398") @@ -333,4 +366,14 @@ private abstract static class RecursiveActionValuesCollector extends Recursiv public abstract void collect(Collection values); } + + @Internal + record StaticServiceDefinitions(Map> serviceTypeMap) { + StaticServiceDefinitions { + if (serviceTypeMap == null) { + serviceTypeMap = new HashMap<>(); + } + } + } + } diff --git a/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/native-image.properties b/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/native-image.properties index 3e9d3606c01..1384544a982 100644 --- a/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/native-image.properties +++ b/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/native-image.properties @@ -21,5 +21,4 @@ Args = --initialize-at-run-time=io.micronaut.core.io.socket.SocketUtils \ --initialize-at-build-time=io.micronaut.core.convert \ --initialize-at-build-time=io.micronaut.core.type \ --initialize-at-build-time=io.micronaut.core.annotation \ - --features=io.micronaut.core.graal.LoggingFeature \ - --features=io.micronaut.core.graal.ServiceLoaderFeature + --features=io.micronaut.core.io.service.ServiceLoaderFeature diff --git a/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/reflect-config.json b/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/reflect-config.json new file mode 100644 index 00000000000..32ac2eac8e9 --- /dev/null +++ b/core/src/main/resources/META-INF/native-image/io.micronaut/micronaut-core/reflect-config.json @@ -0,0 +1,30 @@ +[ + { + "name": "ch.qos.logback.core.ConsoleAppender", + "condition": { + "typeReachable": "ch.qos.logback.core.ConsoleAppender" + }, + "methods": [ + { + "name": "setWithJansi", + "parameterTypes": [ + "boolean" + ] + } + ] + }, + { + "name": "ch.qos.logback.core.encoder.LayoutWrappingEncoder", + "condition": { + "typeReachable": "ch.qos.logback.core.ConsoleAppender" + }, + "methods": [ + { + "name": "setParent", + "parameterTypes": [ + "ch.qos.logback.core.spi.ContextAware" + ] + } + ] + } +] diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 481f7f443fb..b89b6381952 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,8 +9,7 @@ compile-testing = "0.19" geb = "7.0" gorm = "7.3.2" # be sure to update graal version in gradle.properties as well -# Intentionally pin to 22.0.0.2 see https://github.com/micronaut-projects/micronaut-kafka/pull/564 and https://github.com/micronaut-projects/micronaut-core/pull/7663 -graal-svm = "22.0.0.2" +graal-svm = "22.3.1" h2 = "2.1.210" hibernate = "5.5.9.Final" hibernate-validator = "6.1.6.Final" diff --git a/http-netty/src/main/java/io/micronaut/http/netty/graal/MicronautSubstitutions.java b/http-netty/src/main/java/io/micronaut/http/netty/graal/MicronautSubstitutions.java deleted file mode 100644 index 61a569608b7..00000000000 --- a/http-netty/src/main/java/io/micronaut/http/netty/graal/MicronautSubstitutions.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017-2020 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.micronaut.http.netty.graal; - -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; -import io.micronaut.core.annotation.Experimental; -import io.micronaut.core.annotation.Internal; -import io.netty.util.internal.logging.InternalLoggerFactory; -import io.netty.util.internal.logging.JdkLoggerFactory; - -/** - * Micronaut substitutions for GraalVM. - * - * @since 1.1 - * @author graemerocher - */ -@Experimental -@Internal -public class MicronautSubstitutions { -} - -//CHECKSTYLE:OFF -/** - * Substitutions for Netty logging. - */ -@TargetClass(io.netty.util.internal.logging.InternalLoggerFactory.class) -final class Target_io_netty_util_internal_logging_InternalLoggerFactory { - @Substitute - private static InternalLoggerFactory newDefaultFactory(String name) { - return JdkLoggerFactory.INSTANCE; - } -} -//CHECKSTYLE:ON diff --git a/test-suite-graal/build.gradle b/test-suite-graal/build.gradle index 1e614d8c828..2a8cfa4d740 100644 --- a/test-suite-graal/build.gradle +++ b/test-suite-graal/build.gradle @@ -65,6 +65,9 @@ def openGraalModules = [ graalvmNative { toolchainDetection = false + metadataRepository { + enabled = true + } binaries { all { openGraalModules.each { module ->