Expand hints conditions support in RuntimeHints
Prior to this commit, the hints conditions were only supported for the `TypeHint` case. GraalVM generally expanded this concept to all hints and we should do the same. Right now, only the `typeReachable` condition is available but we should design for possible future additions. This commit introduces a new `ConditionalHint` contract implemented by all hints compatible with this approach. The condition information is also used in all configuration writers as a result. Closes gh-28126
This commit is contained in:
@@ -32,24 +32,25 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link ClassProxyHint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class ClassProxyHintTests {
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameInstanceIsTrue() {
|
||||
void equalsWithSameInstanceIsTrue() {
|
||||
ClassProxyHint hint = ClassProxyHint.of(Properties.class).build();
|
||||
assertThat(hint).isEqualTo(hint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameTargetClassIsTrue() {
|
||||
void equalsWithSameTargetClassIsTrue() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(TypeReference.of(Properties.class)).build();
|
||||
assertThat(first).isEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesIsTrue() {
|
||||
void equalsWithSameProxiedInterfacesIsTrue() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(Serializable.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class)
|
||||
@@ -58,14 +59,14 @@ class ClassProxyHintTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithDifferentTargetClassIsFalse() {
|
||||
void equalsWithDifferentTargetClassIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Hashtable.class).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesDifferentOrderIsFalse() {
|
||||
void equalsWithSameProxiedInterfacesDifferentOrderIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(Serializable.class, Closeable.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class)
|
||||
@@ -75,7 +76,7 @@ class ClassProxyHintTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithDifferentProxiedInterfacesIsFalse() {
|
||||
void equalsWithDifferentProxiedInterfacesIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class)
|
||||
.proxiedInterfaces(Serializable.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class)
|
||||
@@ -90,4 +91,11 @@ class ClassProxyHintTests {
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithDifferentConditionIsFalse() {
|
||||
ClassProxyHint first = ClassProxyHint.of(Properties.class).build();
|
||||
ClassProxyHint second = ClassProxyHint.of(Properties.class).onReachableType(TypeReference.of("org.example.test")).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class JdkProxyHintTests {
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameInstanceIsTrue() {
|
||||
void equalsWithSameInstanceIsTrue() {
|
||||
JdkProxyHint hint = new Builder().proxiedInterfaces(Function.class, Consumer.class).build();
|
||||
assertThat(hint).isEqualTo(hint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesIsTrue() {
|
||||
void equalsWithSameProxiedInterfacesIsTrue() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class, Consumer.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Function.class.getName()),
|
||||
TypeReference.of(Consumer.class)).build();
|
||||
@@ -47,7 +47,16 @@ class JdkProxyHintTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithSameProxiedInterfacesDifferentOrderIsFalse() {
|
||||
void equalsWithSameProxiedInterfacesAndDifferentConditionIsFalse() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class, Consumer.class)
|
||||
.onReachableType(TypeReference.of(String.class)).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Function.class.getName()),
|
||||
TypeReference.of(Consumer.class)).onReachableType(TypeReference.of(Function.class)).build();
|
||||
assertThat(first).isNotEqualTo(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithSameProxiedInterfacesDifferentOrderIsFalse() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class, Consumer.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Consumer.class),
|
||||
TypeReference.of(Function.class.getName())).build();
|
||||
@@ -55,7 +64,7 @@ class JdkProxyHintTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWithWithDifferentProxiedInterfacesIsFalse() {
|
||||
void equalsWithDifferentProxiedInterfacesIsFalse() {
|
||||
JdkProxyHint first = new Builder().proxiedInterfaces(Function.class).build();
|
||||
JdkProxyHint second = new Builder().proxiedInterfaces(TypeReference.of(Function.class.getName()),
|
||||
TypeReference.of(Consumer.class)).build();
|
||||
|
||||
@@ -84,8 +84,8 @@ class ResourceHintsTests {
|
||||
|
||||
@Test
|
||||
void registerPatternWithIncludesAndExcludes() {
|
||||
this.resourceHints.registerPattern("com/example/*.properties",
|
||||
resourceHint -> resourceHint.excludes("com/example/to-ignore.properties"));
|
||||
this.resourceHints.registerPattern(resourceHint ->
|
||||
resourceHint.includes("com/example/*.properties").excludes("com/example/to-ignore.properties"));
|
||||
assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(patternOf(
|
||||
List.of("com/example/*.properties"),
|
||||
List.of("com/example/to-ignore.properties")));
|
||||
@@ -107,7 +107,7 @@ class ResourceHintsTests {
|
||||
}
|
||||
|
||||
|
||||
private Consumer<ResourcePatternHint> patternOf(String... includes) {
|
||||
private Consumer<ResourcePatternHints> patternOf(String... includes) {
|
||||
return patternOf(Arrays.asList(includes), Collections.emptyList());
|
||||
}
|
||||
|
||||
@@ -115,10 +115,10 @@ class ResourceHintsTests {
|
||||
return resourceBundleHint -> assertThat(resourceBundleHint.getBaseName()).isEqualTo(baseName);
|
||||
}
|
||||
|
||||
private Consumer<ResourcePatternHint> patternOf(List<String> includes, List<String> excludes) {
|
||||
private Consumer<ResourcePatternHints> patternOf(List<String> includes, List<String> excludes) {
|
||||
return pattern -> {
|
||||
assertThat(pattern.getIncludes()).containsExactlyElementsOf(includes);
|
||||
assertThat(pattern.getExcludes()).containsExactlyElementsOf(excludes);
|
||||
assertThat(pattern.getIncludes()).map(ResourcePatternHint::getPattern).containsExactlyElementsOf(includes);
|
||||
assertThat(pattern.getExcludes()).map(ResourcePatternHint::getPattern).containsExactlyElementsOf(excludes);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,15 +48,16 @@ class RuntimeHintsTests {
|
||||
void resourceHintWithClass() {
|
||||
this.hints.resources().registerType(String.class);
|
||||
assertThat(this.hints.resources().resourcePatterns()).singleElement().satisfies(resourceHint -> {
|
||||
assertThat(resourceHint.getIncludes()).containsExactly("java/lang/String.class");
|
||||
assertThat(resourceHint.getIncludes()).map(ResourcePatternHint::getPattern).containsExactly("java/lang/String.class");
|
||||
assertThat(resourceHint.getExcludes()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void javaSerializationHintWithClass() {
|
||||
this.hints.javaSerialization().registerType(String.class);
|
||||
assertThat(this.hints.javaSerialization().types()).containsExactly(TypeReference.of(String.class));
|
||||
this.hints.serialization().registerType(String.class);
|
||||
assertThat(this.hints.serialization().javaSerialization().map(JavaSerializationHint::getType))
|
||||
.containsExactly(TypeReference.of(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -23,20 +23,20 @@ import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JavaSerializationHints}.
|
||||
* Tests for {@link SerializationHints}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class JavaSerializationHintsTests {
|
||||
class SerializationHintsTests {
|
||||
|
||||
private final JavaSerializationHints javaSerializationHints = new JavaSerializationHints();
|
||||
private final SerializationHints serializationHints = new SerializationHints();
|
||||
|
||||
@Test
|
||||
void registerTypeTwiceExposesOneHint() {
|
||||
this.javaSerializationHints.registerType(URL.class);
|
||||
this.javaSerializationHints.registerType(TypeReference.of(URL.class.getName()));
|
||||
assertThat(this.javaSerializationHints.types()).singleElement()
|
||||
.isEqualTo(TypeReference.of(URL.class));
|
||||
this.serializationHints.registerType(URL.class);
|
||||
this.serializationHints.registerType(TypeReference.of(URL.class.getName()));
|
||||
assertThat(this.serializationHints.javaSerialization()).singleElement()
|
||||
.extracting(JavaSerializationHint::getType).isEqualTo(TypeReference.of(URL.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,12 +32,12 @@ import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.JavaSerializationHints;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
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;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.util.MimeType;
|
||||
@@ -66,7 +66,7 @@ public class FileNativeConfigurationWriterTests {
|
||||
void serializationConfig() throws IOException, JSONException {
|
||||
FileNativeConfigurationWriter generator = new FileNativeConfigurationWriter(tempDir);
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
JavaSerializationHints serializationHints = hints.javaSerialization();
|
||||
SerializationHints serializationHints = hints.serialization();
|
||||
serializationHints.registerType(Integer.class);
|
||||
serializationHints.registerType(Long.class);
|
||||
generator.write(hints);
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
|
||||
import org.springframework.aot.hint.ProxyHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* Tests for {@link ProxyHintsWriter}.
|
||||
@@ -41,7 +42,7 @@ public class ProxyHintsWriterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void one() throws JSONException {
|
||||
void shouldWriteOneEntry() throws JSONException {
|
||||
ProxyHints hints = new ProxyHints();
|
||||
hints.registerJdkProxy(Function.class);
|
||||
assertEquals("""
|
||||
@@ -51,7 +52,7 @@ public class ProxyHintsWriterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void two() throws JSONException {
|
||||
void shouldWriteMultipleEntries() throws JSONException {
|
||||
ProxyHints hints = new ProxyHints();
|
||||
hints.registerJdkProxy(Function.class);
|
||||
hints.registerJdkProxy(Function.class, Consumer.class);
|
||||
@@ -62,6 +63,17 @@ public class ProxyHintsWriterTests {
|
||||
]""", hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWriteCondition() throws JSONException {
|
||||
ProxyHints hints = new ProxyHints();
|
||||
hints.registerJdkProxy(builder -> builder.proxiedInterfaces(Function.class)
|
||||
.onReachableType(TypeReference.of("org.example.Test")));
|
||||
assertEquals("""
|
||||
[
|
||||
{ "condition": { "typeReachable": "org.example.Test"}, "interfaces": [ "java.util.function.Function" ] }
|
||||
]""", hints);
|
||||
}
|
||||
|
||||
private void assertEquals(String expectedString, ProxyHints hints) throws JSONException {
|
||||
StringWriter out = new StringWriter();
|
||||
BasicJsonWriter writer = new BasicJsonWriter(out, "\t");
|
||||
|
||||
@@ -24,11 +24,13 @@ import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
|
||||
import org.springframework.aot.hint.ResourceHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceHintsWriter}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ResourceHintsWriterTests {
|
||||
|
||||
@@ -71,18 +73,32 @@ public class ResourceHintsWriterTests {
|
||||
@Test
|
||||
void registerPatternWithIncludesAndExcludes() throws JSONException {
|
||||
ResourceHints hints = new ResourceHints();
|
||||
hints.registerPattern("com/example/*.properties", hint -> hint.excludes("com/example/to-ignore.properties"));
|
||||
hints.registerPattern("org/example/*.properties", hint -> hint.excludes("org/example/to-ignore.properties"));
|
||||
hints.registerPattern(hint -> hint.includes("com/example/*.properties").excludes("com/example/to-ignore.properties"));
|
||||
hints.registerPattern(hint -> hint.includes("org/other/*.properties").excludes("org/other/to-ignore.properties"));
|
||||
assertEquals("""
|
||||
{
|
||||
"resources": {
|
||||
"includes": [
|
||||
{ "pattern": "\\\\Qcom/example/\\\\E.*\\\\Q.properties\\\\E"},
|
||||
{ "pattern": "\\\\Qorg/example/\\\\E.*\\\\Q.properties\\\\E"}
|
||||
{ "pattern": "\\\\Qorg/other/\\\\E.*\\\\Q.properties\\\\E"}
|
||||
],
|
||||
"excludes": [
|
||||
{ "pattern": "\\\\Qcom/example/to-ignore.properties\\\\E"},
|
||||
{ "pattern": "\\\\Qorg/example/to-ignore.properties\\\\E"}
|
||||
{ "pattern": "\\\\Qorg/other/to-ignore.properties\\\\E"}
|
||||
]
|
||||
}
|
||||
}""", hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithReachableTypeCondition() throws JSONException {
|
||||
ResourceHints hints = new ResourceHints();
|
||||
hints.registerPattern(builder -> builder.includes(TypeReference.of("com.example.Test"), "com/example/test.properties"));
|
||||
assertEquals("""
|
||||
{
|
||||
"resources": {
|
||||
"includes": [
|
||||
{ "condition": { "typeReachable": "com.example.Test"}, "pattern": "\\\\Qcom/example/test.properties\\\\E"}
|
||||
]
|
||||
}
|
||||
}""", hints);
|
||||
|
||||
@@ -23,26 +23,26 @@ import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
|
||||
import org.springframework.aot.hint.JavaSerializationHints;
|
||||
import org.springframework.aot.hint.SerializationHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Tests for {@link JavaSerializationHintsWriter}.
|
||||
* Tests for {@link SerializationHintsWriter}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class JavaSerializationHintsWriterTests {
|
||||
public class SerializationHintsWriterTests {
|
||||
|
||||
@Test
|
||||
void empty() throws JSONException {
|
||||
JavaSerializationHints hints = new JavaSerializationHints();
|
||||
void shouldWriteEmptyHint() throws JSONException {
|
||||
SerializationHints hints = new SerializationHints();
|
||||
assertEquals("[]", hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void one() throws JSONException {
|
||||
JavaSerializationHints hints = new JavaSerializationHints().registerType(TypeReference.of(String.class));
|
||||
void shouldWriteSingleHint() throws JSONException {
|
||||
SerializationHints hints = new SerializationHints().registerType(TypeReference.of(String.class));
|
||||
assertEquals("""
|
||||
[
|
||||
{ "name": "java.lang.String" }
|
||||
@@ -50,8 +50,8 @@ public class JavaSerializationHintsWriterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void two() throws JSONException {
|
||||
JavaSerializationHints hints = new JavaSerializationHints()
|
||||
void shouldWriteMultipleHints() throws JSONException {
|
||||
SerializationHints hints = new SerializationHints()
|
||||
.registerType(TypeReference.of(String.class))
|
||||
.registerType(TypeReference.of(Environment.class));
|
||||
assertEquals("""
|
||||
@@ -61,10 +61,20 @@ public class JavaSerializationHintsWriterTests {
|
||||
]""", hints);
|
||||
}
|
||||
|
||||
private void assertEquals(String expectedString, JavaSerializationHints hints) throws JSONException {
|
||||
@Test
|
||||
void shouldWriteSingleHintWithCondition() throws JSONException {
|
||||
SerializationHints hints = new SerializationHints().registerType(TypeReference.of(String.class),
|
||||
builder -> builder.onReachableType(TypeReference.of("org.example.Test")));
|
||||
assertEquals("""
|
||||
[
|
||||
{ "condition": { "typeReachable": "org.example.Test" }, "name": "java.lang.String" }
|
||||
]""", hints);
|
||||
}
|
||||
|
||||
private void assertEquals(String expectedString, SerializationHints hints) throws JSONException {
|
||||
StringWriter out = new StringWriter();
|
||||
BasicJsonWriter writer = new BasicJsonWriter(out, "\t");
|
||||
JavaSerializationHintsWriter.INSTANCE.write(writer, hints);
|
||||
SerializationHintsWriter.INSTANCE.write(writer, hints);
|
||||
JSONAssert.assertEquals(expectedString, out.toString(), JSONCompareMode.NON_EXTENSIBLE);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ResourcePatternHint;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
@@ -48,7 +49,7 @@ class SpringFactoriesLoaderRuntimeHintsRegistrarTests {
|
||||
@Test
|
||||
void resourceLocationHasHints() {
|
||||
assertThat(this.hints.resources().resourcePatterns())
|
||||
.anySatisfy(hint -> assertThat(hint.getIncludes())
|
||||
.anySatisfy(hint -> assertThat(hint.getIncludes()).map(ResourcePatternHint::getPattern)
|
||||
.contains(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user