Implement new GraalVM reachability metadata format
As of GraalVM 23, a new and simplified reachability metadata format is available. Metadata now consists of a single "reachability-metadata.json" file that contains all the information previously spread in multiple files. The new format does not include some introspection flags, as they're now automatically included when a hint is registered against a type. Also, "typeReachable" has been renamed as "typeReached" to highlight the fact that the event considered is the static initialization of the type, not when the static analysis performed during native compilation is reaching the type. This new format ships with a JSON schema, which this commit is tested against. See gh-33847
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,11 +18,7 @@ package org.springframework.aot.nativex;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aot.hint.ProxyHints;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.ResourceHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.SerializationHints;
|
||||
|
||||
/**
|
||||
* Write {@link RuntimeHints} as GraalVM native configuration.
|
||||
@@ -30,8 +26,9 @@ import org.springframework.aot.hint.SerializationHints;
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @author Janne Valkealahti
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.1/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
* @see <a href="https://www.graalvm.org/jdk23/reference-manual/native-image/overview/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
public abstract class NativeConfigurationWriter {
|
||||
|
||||
@@ -40,24 +37,21 @@ public abstract class NativeConfigurationWriter {
|
||||
* @param hints the hints to handle
|
||||
*/
|
||||
public void write(RuntimeHints hints) {
|
||||
if (hints.serialization().javaSerializationHints().findAny().isPresent()) {
|
||||
writeSerializationHints(hints.serialization());
|
||||
}
|
||||
if (hints.proxies().jdkProxyHints().findAny().isPresent()) {
|
||||
writeProxyHints(hints.proxies());
|
||||
}
|
||||
if (hints.reflection().typeHints().findAny().isPresent()) {
|
||||
writeReflectionHints(hints.reflection());
|
||||
}
|
||||
if (hints.resources().resourcePatternHints().findAny().isPresent() ||
|
||||
hints.resources().resourceBundleHints().findAny().isPresent()) {
|
||||
writeResourceHints(hints.resources());
|
||||
}
|
||||
if (hints.jni().typeHints().findAny().isPresent()) {
|
||||
writeJniHints(hints.jni());
|
||||
if (hasAnyHint(hints)) {
|
||||
writeTo("reachability-metadata.json",
|
||||
writer -> new RuntimeHintsWriter().write(writer, hints));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAnyHint(RuntimeHints hints) {
|
||||
return (hints.serialization().javaSerializationHints().findAny().isPresent()
|
||||
|| hints.proxies().jdkProxyHints().findAny().isPresent()
|
||||
|| hints.reflection().typeHints().findAny().isPresent()
|
||||
|| hints.resources().resourcePatternHints().findAny().isPresent()
|
||||
|| hints.resources().resourceBundleHints().findAny().isPresent()
|
||||
|| hints.jni().typeHints().findAny().isPresent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the specified GraalVM native configuration file, using the
|
||||
* provided {@link BasicJsonWriter}.
|
||||
@@ -66,29 +60,4 @@ public abstract class NativeConfigurationWriter {
|
||||
*/
|
||||
protected abstract void writeTo(String fileName, Consumer<BasicJsonWriter> writer);
|
||||
|
||||
private void writeSerializationHints(SerializationHints hints) {
|
||||
writeTo("serialization-config.json", writer ->
|
||||
SerializationHintsWriter.INSTANCE.write(writer, hints));
|
||||
}
|
||||
|
||||
private void writeProxyHints(ProxyHints hints) {
|
||||
writeTo("proxy-config.json", writer ->
|
||||
ProxyHintsWriter.INSTANCE.write(writer, hints));
|
||||
}
|
||||
|
||||
private void writeReflectionHints(ReflectionHints hints) {
|
||||
writeTo("reflect-config.json", writer ->
|
||||
ReflectionHintsWriter.INSTANCE.write(writer, hints));
|
||||
}
|
||||
|
||||
private void writeResourceHints(ResourceHints hints) {
|
||||
writeTo("resource-config.json", writer ->
|
||||
ResourceHintsWriter.INSTANCE.write(writer, hints));
|
||||
}
|
||||
|
||||
private void writeJniHints(ReflectionHints hints) {
|
||||
writeTo("jni-config.json", writer ->
|
||||
ReflectionHintsWriter.INSTANCE.write(writer, hints));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or 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 org.springframework.aot.nativex;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.aot.hint.JdkProxyHint;
|
||||
import org.springframework.aot.hint.ProxyHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* Write {@link JdkProxyHint}s contained in a {@link ProxyHints} to the JSON
|
||||
* output expected by the GraalVM {@code native-image} compiler, typically named
|
||||
* {@code proxy-config.json}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.1/reference-manual/native-image/DynamicProxy/">Dynamic Proxy in Native Image</a>
|
||||
* @see <a href="https://www.graalvm.org/22.1/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
class ProxyHintsWriter {
|
||||
|
||||
public static final ProxyHintsWriter INSTANCE = new ProxyHintsWriter();
|
||||
|
||||
private static final Comparator<JdkProxyHint> JDK_PROXY_HINT_COMPARATOR =
|
||||
(left, right) -> {
|
||||
String leftSignature = left.getProxiedInterfaces().stream()
|
||||
.map(TypeReference::getCanonicalName).collect(Collectors.joining(","));
|
||||
String rightSignature = right.getProxiedInterfaces().stream()
|
||||
.map(TypeReference::getCanonicalName).collect(Collectors.joining(","));
|
||||
return leftSignature.compareTo(rightSignature);
|
||||
};
|
||||
|
||||
public void write(BasicJsonWriter writer, ProxyHints hints) {
|
||||
writer.writeArray(hints.jdkProxyHints().sorted(JDK_PROXY_HINT_COMPARATOR)
|
||||
.map(this::toAttributes).toList());
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(JdkProxyHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
handleCondition(attributes, hint);
|
||||
attributes.put("interfaces", hint.getProxiedInterfaces());
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private void handleCondition(Map<String, Object> attributes, JdkProxyHint hint) {
|
||||
if (hint.getReachableType() != null) {
|
||||
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
|
||||
conditionAttributes.put("typeReachable", hint.getReachableType());
|
||||
attributes.put("condition", conditionAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,48 +16,72 @@
|
||||
|
||||
package org.springframework.aot.nativex;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.aot.hint.ConditionalHint;
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.FieldHint;
|
||||
import org.springframework.aot.hint.JdkProxyHint;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Write {@link ReflectionHints} to the JSON output expected by the GraalVM
|
||||
* {@code native-image} compiler, typically named {@code reflect-config.json}
|
||||
* or {@code jni-config.json}.
|
||||
* Collect {@link ReflectionHints} as map attributes ready for JSON serialization for the GraalVM
|
||||
* {@code native-image} compiler.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @author Janne Valkealahti
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.0/reference-manual/native-image/Reflection/">Reflection Use in Native Images</a>
|
||||
* @see <a href="https://www.graalvm.org/22.0/reference-manual/native-image/JNI/">Java Native Interface (JNI) in Native Image</a>
|
||||
* @see <a href="https://www.graalvm.org/22.0/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
* @see <a href="https://www.graalvm.org/jdk23/reference-manual/native-image/metadata/#reflection">Reflection Use in Native Images</a>
|
||||
* @see <a href="https://www.graalvm.org/jdk23/reference-manual/native-image/dynamic-features/JNI/">Java Native Interface (JNI) in Native Image</a>
|
||||
* @see <a href="https://www.graalvm.org/jdk23/reference-manual/native-image/overview/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
class ReflectionHintsWriter {
|
||||
class ReflectionHintsAttributes {
|
||||
|
||||
public static final ReflectionHintsWriter INSTANCE = new ReflectionHintsWriter();
|
||||
private static final Comparator<JdkProxyHint> JDK_PROXY_HINT_COMPARATOR =
|
||||
(left, right) -> {
|
||||
String leftSignature = left.getProxiedInterfaces().stream()
|
||||
.map(TypeReference::getCanonicalName).collect(Collectors.joining(","));
|
||||
String rightSignature = right.getProxiedInterfaces().stream()
|
||||
.map(TypeReference::getCanonicalName).collect(Collectors.joining(","));
|
||||
return leftSignature.compareTo(rightSignature);
|
||||
};
|
||||
|
||||
public void write(BasicJsonWriter writer, ReflectionHints hints) {
|
||||
writer.writeArray(hints.typeHints()
|
||||
public List<Map<String, Object>> reflection(RuntimeHints hints) {
|
||||
List<Map<String, Object>> reflectionHints = new ArrayList<>();
|
||||
reflectionHints.addAll(hints.reflection().typeHints()
|
||||
.sorted(Comparator.comparing(TypeHint::getType))
|
||||
.map(this::toAttributes).toList());
|
||||
reflectionHints.addAll(hints.proxies().jdkProxyHints()
|
||||
.sorted(JDK_PROXY_HINT_COMPARATOR)
|
||||
.map(this::toAttributes).toList());
|
||||
return reflectionHints;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> jni(RuntimeHints hints) {
|
||||
List<Map<String, Object>> jniHints = new ArrayList<>();
|
||||
jniHints.addAll(hints.jni().typeHints()
|
||||
.sorted(Comparator.comparing(TypeHint::getType))
|
||||
.map(this::toAttributes).toList());
|
||||
return jniHints;
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(TypeHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
attributes.put("name", hint.getType());
|
||||
attributes.put("type", hint.getType());
|
||||
handleCondition(attributes, hint);
|
||||
handleCategories(attributes, hint.getMemberCategories());
|
||||
handleFields(attributes, hint.fields());
|
||||
@@ -66,33 +90,23 @@ class ReflectionHintsWriter {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private void handleCondition(Map<String, Object> attributes, TypeHint hint) {
|
||||
private void handleCondition(Map<String, Object> attributes, ConditionalHint hint) {
|
||||
if (hint.getReachableType() != null) {
|
||||
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
|
||||
conditionAttributes.put("typeReachable", hint.getReachableType());
|
||||
attributes.put("condition", conditionAttributes);
|
||||
attributes.put("condition", Map.of("typeReached", hint.getReachableType()));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFields(Map<String, Object> attributes, Stream<FieldHint> fields) {
|
||||
addIfNotEmpty(attributes, "fields", fields
|
||||
.sorted(Comparator.comparing(FieldHint::getName, String::compareToIgnoreCase))
|
||||
.map(this::toAttributes).toList());
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(FieldHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
attributes.put("name", hint.getName());
|
||||
return attributes;
|
||||
.map(fieldHint -> Map.of("name", fieldHint.getName()))
|
||||
.toList());
|
||||
}
|
||||
|
||||
private void handleExecutables(Map<String, Object> attributes, List<ExecutableHint> hints) {
|
||||
addIfNotEmpty(attributes, "methods", hints.stream()
|
||||
.filter(h -> h.getMode().equals(ExecutableMode.INVOKE))
|
||||
.map(this::toAttributes).toList());
|
||||
addIfNotEmpty(attributes, "queriedMethods", hints.stream()
|
||||
.filter(h -> h.getMode().equals(ExecutableMode.INTROSPECT))
|
||||
.map(this::toAttributes).toList());
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(ExecutableHint hint) {
|
||||
@@ -102,28 +116,19 @@ class ReflectionHintsWriter {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private void handleCategories(Map<String, Object> attributes, Set<MemberCategory> categories) {
|
||||
categories.stream().sorted().forEach(category -> {
|
||||
switch (category) {
|
||||
case PUBLIC_FIELDS -> attributes.put("allPublicFields", true);
|
||||
case DECLARED_FIELDS -> attributes.put("allDeclaredFields", true);
|
||||
case INTROSPECT_PUBLIC_CONSTRUCTORS ->
|
||||
attributes.put("queryAllPublicConstructors", true);
|
||||
case INTROSPECT_DECLARED_CONSTRUCTORS ->
|
||||
attributes.put("queryAllDeclaredConstructors", true);
|
||||
case INVOKE_PUBLIC_FIELDS, PUBLIC_FIELDS -> attributes.put("allPublicFields", true);
|
||||
case INVOKE_DECLARED_FIELDS, DECLARED_FIELDS -> attributes.put("allDeclaredFields", true);
|
||||
case INVOKE_PUBLIC_CONSTRUCTORS ->
|
||||
attributes.put("allPublicConstructors", true);
|
||||
case INVOKE_DECLARED_CONSTRUCTORS ->
|
||||
attributes.put("allDeclaredConstructors", true);
|
||||
case INTROSPECT_PUBLIC_METHODS ->
|
||||
attributes.put("queryAllPublicMethods", true);
|
||||
case INTROSPECT_DECLARED_METHODS ->
|
||||
attributes.put("queryAllDeclaredMethods", true);
|
||||
case INVOKE_PUBLIC_METHODS -> attributes.put("allPublicMethods", true);
|
||||
case INVOKE_DECLARED_METHODS ->
|
||||
attributes.put("allDeclaredMethods", true);
|
||||
case PUBLIC_CLASSES -> attributes.put("allPublicClasses", true);
|
||||
case DECLARED_CLASSES -> attributes.put("allDeclaredClasses", true);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -135,4 +140,11 @@ class ReflectionHintsWriter {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(JdkProxyHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
handleCondition(attributes, hint);
|
||||
attributes.put("type", Map.of("proxy", hint.getProxiedInterfaces()));
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.aot.hint.ConditionalHint;
|
||||
import org.springframework.aot.hint.ResourceBundleHint;
|
||||
@@ -31,8 +30,8 @@ import org.springframework.aot.hint.ResourcePatternHints;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Write a {@link ResourceHints} to the JSON output expected by the GraalVM
|
||||
* {@code native-image} compiler, typically named {@code resource-config.json}.
|
||||
* Collect {@link ResourceHints} as map attributes ready for JSON serialization for the GraalVM
|
||||
* {@code native-image} compiler.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
@@ -41,9 +40,7 @@ import org.springframework.lang.Nullable;
|
||||
* @see <a href="https://www.graalvm.org/22.1/reference-manual/native-image/Resources/">Accessing Resources in Native Images</a>
|
||||
* @see <a href="https://www.graalvm.org/22.1/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
class ResourceHintsWriter {
|
||||
|
||||
public static final ResourceHintsWriter INSTANCE = new ResourceHintsWriter();
|
||||
class ResourceHintsAttributes {
|
||||
|
||||
private static final Comparator<ResourcePatternHint> RESOURCE_PATTERN_HINT_COMPARATOR =
|
||||
Comparator.comparing(ResourcePatternHint::getPattern);
|
||||
@@ -52,30 +49,17 @@ class ResourceHintsWriter {
|
||||
Comparator.comparing(ResourceBundleHint::getBaseName);
|
||||
|
||||
|
||||
public void write(BasicJsonWriter writer, ResourceHints hints) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
addIfNotEmpty(attributes, "resources", toAttributes(hints));
|
||||
handleResourceBundles(attributes, hints.resourceBundleHints());
|
||||
writer.writeObject(attributes);
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(ResourceHints hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
addIfNotEmpty(attributes, "includes", hint.resourcePatternHints()
|
||||
public List<Map<String, Object>> resources(ResourceHints hint) {
|
||||
return hint.resourcePatternHints()
|
||||
.map(ResourcePatternHints::getIncludes).flatMap(List::stream).distinct()
|
||||
.sorted(RESOURCE_PATTERN_HINT_COMPARATOR)
|
||||
.map(this::toAttributes).toList());
|
||||
addIfNotEmpty(attributes, "excludes", hint.resourcePatternHints()
|
||||
.map(ResourcePatternHints::getExcludes).flatMap(List::stream).distinct()
|
||||
.sorted(RESOURCE_PATTERN_HINT_COMPARATOR)
|
||||
.map(this::toAttributes).toList());
|
||||
return attributes;
|
||||
.map(this::toAttributes).toList();
|
||||
}
|
||||
|
||||
private void handleResourceBundles(Map<String, Object> attributes, Stream<ResourceBundleHint> resourceBundles) {
|
||||
addIfNotEmpty(attributes, "bundles", resourceBundles
|
||||
public List<Map<String, Object>> resourceBundles(ResourceHints hint) {
|
||||
return hint.resourceBundleHints()
|
||||
.sorted(RESOURCE_BUNDLE_HINT_COMPARATOR)
|
||||
.map(this::toAttributes).toList());
|
||||
.map(this::toAttributes).toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(ResourceBundleHint hint) {
|
||||
@@ -88,7 +72,7 @@ class ResourceHintsWriter {
|
||||
private Map<String, Object> toAttributes(ResourcePatternHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
handleCondition(attributes, hint);
|
||||
attributes.put("pattern", hint.toRegex().toString());
|
||||
attributes.put("glob", hint.getPattern());
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@@ -111,7 +95,7 @@ class ResourceHintsWriter {
|
||||
private void handleCondition(Map<String, Object> attributes, ConditionalHint hint) {
|
||||
if (hint.getReachableType() != null) {
|
||||
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
|
||||
conditionAttributes.put("typeReachable", hint.getReachableType());
|
||||
conditionAttributes.put("typeReached", hint.getReachableType());
|
||||
attributes.put("condition", conditionAttributes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or 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 org.springframework.aot.nativex;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.core.SpringVersion;
|
||||
|
||||
/**
|
||||
* Write a {@link RuntimeHints} instance to the JSON output expected by the
|
||||
* GraalVM {@code native-image} compiler, typically named {@code reachability-metadata.json}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 7.0
|
||||
* @see <a href="https://www.graalvm.org/jdk23/reference-manual/native-image/metadata/#specifying-metadata-with-json">GraalVM Reachability Metadata</a>
|
||||
*/
|
||||
class RuntimeHintsWriter {
|
||||
|
||||
public void write(BasicJsonWriter writer, RuntimeHints hints) {
|
||||
Map<String, Object> document = new LinkedHashMap<>();
|
||||
String springVersion = SpringVersion.getVersion();
|
||||
if (springVersion != null) {
|
||||
document.put("comment", "Spring Framework " + springVersion);
|
||||
}
|
||||
List<Map<String, Object>> reflection = new ReflectionHintsAttributes().reflection(hints);
|
||||
if (!reflection.isEmpty()) {
|
||||
document.put("reflection", reflection);
|
||||
}
|
||||
List<Map<String, Object>> jni = new ReflectionHintsAttributes().jni(hints);
|
||||
if (!jni.isEmpty()) {
|
||||
document.put("jni", jni);
|
||||
}
|
||||
List<Map<String, Object>> resourceHints = new ResourceHintsAttributes().resources(hints.resources());
|
||||
if (!resourceHints.isEmpty()) {
|
||||
document.put("resources", resourceHints);
|
||||
}
|
||||
List<Map<String, Object>> resourceBundles = new ResourceHintsAttributes().resourceBundles(hints.resources());
|
||||
if (!resourceBundles.isEmpty()) {
|
||||
document.put("bundles", resourceBundles);
|
||||
}
|
||||
List<Map<String, Object>> serialization = new SerializationHintsAttributes().toAttributes(hints.serialization());
|
||||
if (!serialization.isEmpty()) {
|
||||
document.put("serialization", serialization);
|
||||
}
|
||||
|
||||
writer.writeObject(document);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.aot.nativex;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.aot.hint.ConditionalHint;
|
||||
@@ -25,40 +26,36 @@ import org.springframework.aot.hint.JavaSerializationHint;
|
||||
import org.springframework.aot.hint.SerializationHints;
|
||||
|
||||
/**
|
||||
* Write a {@link SerializationHints} to the JSON output expected by the
|
||||
* GraalVM {@code native-image} compiler, typically named
|
||||
* {@code serialization-config.json}.
|
||||
* Collect {@link SerializationHints} as map attributes ready for JSON serialization for the GraalVM
|
||||
* {@code native-image} compiler.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.1/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
* @see <a href="https://www.graalvm.org/jdk23/reference-manual/native-image/overview/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
class SerializationHintsWriter {
|
||||
|
||||
public static final SerializationHintsWriter INSTANCE = new SerializationHintsWriter();
|
||||
class SerializationHintsAttributes {
|
||||
|
||||
private static final Comparator<JavaSerializationHint> JAVA_SERIALIZATION_HINT_COMPARATOR =
|
||||
Comparator.comparing(JavaSerializationHint::getType);
|
||||
|
||||
public void write(BasicJsonWriter writer, SerializationHints hints) {
|
||||
writer.writeArray(hints.javaSerializationHints()
|
||||
public List<Map<String, Object>> toAttributes(SerializationHints hints) {
|
||||
return hints.javaSerializationHints()
|
||||
.sorted(JAVA_SERIALIZATION_HINT_COMPARATOR)
|
||||
.map(this::toAttributes).toList());
|
||||
.map(this::toAttributes).toList();
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(JavaSerializationHint serializationHint) {
|
||||
LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
|
||||
handleCondition(attributes, serializationHint);
|
||||
attributes.put("name", serializationHint.getType());
|
||||
attributes.put("type", serializationHint.getType());
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private void handleCondition(Map<String, Object> attributes, ConditionalHint hint) {
|
||||
if (hint.getReachableType() != null) {
|
||||
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
|
||||
conditionAttributes.put("typeReachable", hint.getReachableType());
|
||||
conditionAttributes.put("typeReached", hint.getReachableType());
|
||||
attributes.put("condition", conditionAttributes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user