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:
@@ -31,6 +31,7 @@ import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.InMemoryGeneratedFiles;
|
||||
import org.springframework.aot.generate.MethodGenerator;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.aot.hint.ResourcePatternHint;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
@@ -90,6 +91,7 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
assertThat(generationContext.getRuntimeHints().resources().resourcePatterns())
|
||||
.singleElement()
|
||||
.satisfies(resourceHint -> assertThat(resourceHint.getIncludes())
|
||||
.map(ResourcePatternHint::getPattern)
|
||||
.containsOnly(
|
||||
"org/springframework/context/testfixture/context/generator/annotation/ImportConfiguration.class"));
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.springframework.aot.generate;
|
||||
|
||||
import org.springframework.aot.hint.JavaSerializationHints;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Central interface used for code generation.
|
||||
@@ -67,7 +67,7 @@ public interface GenerationContext {
|
||||
/**
|
||||
* Return the {@link RuntimeHints} being used by the context. Used to record
|
||||
* {@link ReflectionHints reflection}, {@link ResourceHints resource},
|
||||
* {@link JavaSerializationHints serialization} and {@link ProxyHints proxy}
|
||||
* {@link SerializationHints serialization} and {@link ProxyHints proxy}
|
||||
* hints so that the application can run as a native image.
|
||||
* @return the runtime hints
|
||||
*/
|
||||
|
||||
@@ -21,22 +21,29 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A hint that describes the need for a proxy against a concrete class.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ClassProxyHint {
|
||||
public final class ClassProxyHint implements ConditionalHint {
|
||||
|
||||
private final TypeReference targetClass;
|
||||
|
||||
private final List<TypeReference> proxiedInterfaces;
|
||||
|
||||
@Nullable
|
||||
private final TypeReference reachableType;
|
||||
|
||||
|
||||
private ClassProxyHint(Builder builder) {
|
||||
this.targetClass = builder.targetClass;
|
||||
this.proxiedInterfaces = builder.proxiedInterfaces.stream().distinct().toList();
|
||||
this.reachableType = builder.reachableType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +83,12 @@ public final class ClassProxyHint {
|
||||
return this.proxiedInterfaces;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TypeReference getReachableType() {
|
||||
return this.reachableType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
@@ -86,7 +99,8 @@ public final class ClassProxyHint {
|
||||
}
|
||||
ClassProxyHint that = (ClassProxyHint) o;
|
||||
return this.targetClass.equals(that.targetClass)
|
||||
&& this.proxiedInterfaces.equals(that.proxiedInterfaces);
|
||||
&& this.proxiedInterfaces.equals(that.proxiedInterfaces)
|
||||
&& Objects.equals(this.reachableType, that.reachableType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,6 +118,9 @@ public final class ClassProxyHint {
|
||||
|
||||
private final LinkedList<TypeReference> proxiedInterfaces = new LinkedList<>();
|
||||
|
||||
@Nullable
|
||||
private TypeReference reachableType;
|
||||
|
||||
|
||||
Builder(TypeReference targetClass) {
|
||||
this.targetClass = targetClass;
|
||||
@@ -130,6 +147,18 @@ public final class ClassProxyHint {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this hint conditional on the fact that the specified type
|
||||
* can be resolved.
|
||||
* @param reachableType the type that should be reachable for this
|
||||
* hint to apply
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder onReachableType(TypeReference reachableType) {
|
||||
this.reachableType = reachableType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link ClassProxyHint} based on the state of this builder.
|
||||
* @return a class proxy hint
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.hint;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Contract for {@link RuntimeHints runtime hints} that only apply
|
||||
* if the described condition is met.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public interface ConditionalHint {
|
||||
|
||||
/**
|
||||
* Return the type that should be reachable for this hint to apply, or
|
||||
* {@code null} if this hint should always been applied.
|
||||
* @return the reachable type, if any
|
||||
*/
|
||||
@Nullable
|
||||
TypeReference getReachableType();
|
||||
|
||||
/**
|
||||
* Whether the condition described for this hint is met. If it is not,
|
||||
* the hint does not apply.
|
||||
* <p>Instead of checking for actual reachability of a type in the
|
||||
* application, the classpath is checked for the presence of this
|
||||
* type as a simple heuristic.
|
||||
* @param classLoader the current classloader
|
||||
* @return whether the condition is met and the hint applies
|
||||
*/
|
||||
default boolean conditionMatches(ClassLoader classLoader) {
|
||||
TypeReference reachableType = getReachableType();
|
||||
if (reachableType != null) {
|
||||
return ClassUtils.isPresent(reachableType.getCanonicalName(), classLoader);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.hint;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A hint that describes the need for Java serialization at runtime.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public class JavaSerializationHint implements ConditionalHint {
|
||||
|
||||
private final TypeReference type;
|
||||
|
||||
@Nullable
|
||||
private final TypeReference reachableType;
|
||||
|
||||
JavaSerializationHint(Builder builder) {
|
||||
this.type = builder.type;
|
||||
this.reachableType = builder.reachableType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link TypeReference type} that needs to be serialized using
|
||||
* Java serialization at runtime.
|
||||
* @return a {@link Serializable} type
|
||||
*/
|
||||
public TypeReference getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TypeReference getReachableType() {
|
||||
return this.reachableType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
JavaSerializationHint that = (JavaSerializationHint) o;
|
||||
return this.type.equals(that.type)
|
||||
&& Objects.equals(this.reachableType, that.reachableType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.type, this.reachableType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link JavaSerializationHint}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final TypeReference type;
|
||||
|
||||
@Nullable
|
||||
private TypeReference reachableType;
|
||||
|
||||
|
||||
Builder(TypeReference type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this hint conditional on the fact that the specified type
|
||||
* can be resolved.
|
||||
* @param reachableType the type that should be reachable for this
|
||||
* hint to apply
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder onReachableType(TypeReference reachableType) {
|
||||
this.reachableType = reachableType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link JavaSerializationHint} based on the state of this builder.
|
||||
* @return a java serialization hint
|
||||
*/
|
||||
JavaSerializationHint build() {
|
||||
return new JavaSerializationHint(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.hint;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Gather the need for Java serialization at runtime.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @see Serializable
|
||||
*/
|
||||
public class JavaSerializationHints {
|
||||
|
||||
private final Set<TypeReference> types;
|
||||
|
||||
|
||||
public JavaSerializationHints() {
|
||||
this.types = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link TypeReference types} that need to be serialized using
|
||||
* Java serialization at runtime.
|
||||
* @return a stream of {@link Serializable} types
|
||||
*/
|
||||
public Stream<TypeReference> types() {
|
||||
return this.types.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the type defined by the specified {@link TypeReference}
|
||||
* need to be serialized using java serialization.
|
||||
* @param type the type to register
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public JavaSerializationHints registerType(TypeReference type) {
|
||||
this.types.add(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the specified type need to be serialized using java
|
||||
* serialization.
|
||||
* @param type the type to register
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public JavaSerializationHints registerType(Class<? extends Serializable> type) {
|
||||
return registerType(TypeReference.of(type));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,20 +22,27 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A hint that describes the need of a JDK {@link Proxy}, that is an
|
||||
* interfaces-based proxy.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class JdkProxyHint {
|
||||
public final class JdkProxyHint implements ConditionalHint {
|
||||
|
||||
private final List<TypeReference> proxiedInterfaces;
|
||||
|
||||
@Nullable
|
||||
private final TypeReference reachableType;
|
||||
|
||||
|
||||
private JdkProxyHint(Builder builder) {
|
||||
this.proxiedInterfaces = List.copyOf(builder.proxiedInterfaces);
|
||||
this.reachableType = builder.reachableType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,6 +71,12 @@ public final class JdkProxyHint {
|
||||
return this.proxiedInterfaces;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TypeReference getReachableType() {
|
||||
return this.reachableType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
@@ -73,7 +86,8 @@ public final class JdkProxyHint {
|
||||
return false;
|
||||
}
|
||||
JdkProxyHint that = (JdkProxyHint) o;
|
||||
return this.proxiedInterfaces.equals(that.proxiedInterfaces);
|
||||
return this.proxiedInterfaces.equals(that.proxiedInterfaces)
|
||||
&& Objects.equals(this.reachableType, that.reachableType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,6 +103,10 @@ public final class JdkProxyHint {
|
||||
|
||||
private final LinkedList<TypeReference> proxiedInterfaces;
|
||||
|
||||
@Nullable
|
||||
private TypeReference reachableType;
|
||||
|
||||
|
||||
Builder() {
|
||||
this.proxiedInterfaces = new LinkedList<>();
|
||||
}
|
||||
@@ -113,6 +131,18 @@ public final class JdkProxyHint {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this hint conditional on the fact that the specified type
|
||||
* can be resolved.
|
||||
* @param reachableType the type that should be reachable for this
|
||||
* hint to apply
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder onReachableType(TypeReference reachableType) {
|
||||
this.reachableType = reachableType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link JdkProxyHint} based on the state of this builder.
|
||||
* @return a jdk proxy hint
|
||||
|
||||
@@ -16,21 +16,29 @@
|
||||
|
||||
package org.springframework.aot.hint;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A hint that describes the need to access to a {@link ResourceBundle}.
|
||||
* A hint that describes the need to access a {@link ResourceBundle}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ResourceBundleHint {
|
||||
public final class ResourceBundleHint implements ConditionalHint {
|
||||
|
||||
private final String baseName;
|
||||
|
||||
@Nullable
|
||||
private TypeReference reachableType;
|
||||
|
||||
ResourceBundleHint(String baseName) {
|
||||
this.baseName = baseName;
|
||||
|
||||
ResourceBundleHint(Builder builder) {
|
||||
this.baseName = builder.baseName;
|
||||
this.reachableType = builder.reachableType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,4 +49,70 @@ public class ResourceBundleHint {
|
||||
return this.baseName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TypeReference getReachableType() {
|
||||
return this.reachableType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ResourceBundleHint that = (ResourceBundleHint) o;
|
||||
return this.baseName.equals(that.baseName)
|
||||
&& Objects.equals(this.reachableType, that.reachableType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.baseName, this.reachableType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link ResourceBundleHint}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private String baseName;
|
||||
|
||||
@Nullable
|
||||
private TypeReference reachableType;
|
||||
|
||||
/**
|
||||
* Make this hint conditional on the fact that the specified type
|
||||
* can be resolved.
|
||||
* @param reachableType the type that should be reachable for this
|
||||
* hint to apply
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder onReachableType(TypeReference reachableType) {
|
||||
this.reachableType = reachableType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the the {@code baseName} of the resource bundle.
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder baseName(String baseName) {
|
||||
this.baseName = baseName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ResourceBundleHint} based on the state of this
|
||||
* builder.
|
||||
* @return a resource bundle hint
|
||||
*/
|
||||
ResourceBundleHint build() {
|
||||
return new ResourceBundleHint(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.aot.hint.ResourcePatternHint.Builder;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -37,9 +36,9 @@ public class ResourceHints {
|
||||
|
||||
private final Set<TypeReference> types;
|
||||
|
||||
private final List<Builder> resourcePatternHints;
|
||||
private final List<ResourcePatternHints> resourcePatternHints;
|
||||
|
||||
private final Set<String> resourceBundleHints;
|
||||
private final Set<ResourceBundleHint> resourceBundleHints;
|
||||
|
||||
|
||||
public ResourceHints() {
|
||||
@@ -50,10 +49,10 @@ public class ResourceHints {
|
||||
|
||||
/**
|
||||
* Return the resources that should be made available at runtime.
|
||||
* @return a stream of {@link ResourcePatternHint}
|
||||
* @return a stream of {@link ResourcePatternHints}
|
||||
*/
|
||||
public Stream<ResourcePatternHint> resourcePatterns() {
|
||||
Stream<ResourcePatternHint> patterns = this.resourcePatternHints.stream().map(Builder::build);
|
||||
public Stream<ResourcePatternHints> resourcePatterns() {
|
||||
Stream<ResourcePatternHints> patterns = this.resourcePatternHints.stream();
|
||||
return (this.types.isEmpty() ? patterns
|
||||
: Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
|
||||
}
|
||||
@@ -63,22 +62,21 @@ public class ResourceHints {
|
||||
* @return a stream of {@link ResourceBundleHint}
|
||||
*/
|
||||
public Stream<ResourceBundleHint> resourceBundles() {
|
||||
return this.resourceBundleHints.stream().map(ResourceBundleHint::new);
|
||||
return this.resourceBundleHints.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the resources matching the specified pattern should be
|
||||
* made available at runtime.
|
||||
* @param include a pattern of the resources to include
|
||||
* @param resourceHint a builder to further customize the resource pattern
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public ResourceHints registerPattern(String include, @Nullable Consumer<Builder> resourceHint) {
|
||||
Builder builder = new Builder().includes(include);
|
||||
public ResourceHints registerPattern(@Nullable Consumer<ResourcePatternHints.Builder> resourceHint) {
|
||||
ResourcePatternHints.Builder builder = new ResourcePatternHints.Builder();
|
||||
if (resourceHint != null) {
|
||||
resourceHint.accept(builder);
|
||||
}
|
||||
this.resourcePatternHints.add(builder);
|
||||
this.resourcePatternHints.add(builder.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -89,7 +87,7 @@ public class ResourceHints {
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public ResourceHints registerPattern(String include) {
|
||||
return registerPattern(include, null);
|
||||
return registerPattern(builder -> builder.includes(include));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,15 +115,30 @@ public class ResourceHints {
|
||||
* Register that the resource bundle with the specified base name should
|
||||
* be made available at runtime.
|
||||
* @param baseName the base name of the resource bundle
|
||||
* @param resourceHint a builder to further customize the resource bundle
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public ResourceHints registerResourceBundle(String baseName) {
|
||||
this.resourceBundleHints.add(baseName);
|
||||
public ResourceHints registerResourceBundle(String baseName, @Nullable Consumer<ResourceBundleHint.Builder> resourceHint) {
|
||||
ResourceBundleHint.Builder builder = new ResourceBundleHint.Builder().baseName(baseName);
|
||||
if (resourceHint != null) {
|
||||
resourceHint.accept(builder);
|
||||
}
|
||||
this.resourceBundleHints.add(builder.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
private ResourcePatternHint typesPatternResourceHint() {
|
||||
Builder builder = new Builder();
|
||||
/**
|
||||
* Register that the resource bundle with the specified base name should
|
||||
* be made available at runtime.
|
||||
* @param baseName the base name of the resource bundle
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public ResourceHints registerResourceBundle(String baseName) {
|
||||
return registerResourceBundle(baseName, null);
|
||||
}
|
||||
|
||||
private ResourcePatternHints typesPatternResourceHint() {
|
||||
ResourcePatternHints.Builder builder = new ResourcePatternHints.Builder();
|
||||
this.types.forEach(type -> builder.includes(toIncludePattern(type)));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -16,89 +16,61 @@
|
||||
|
||||
package org.springframework.aot.hint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A hint that describes resources that should be made available at runtime.
|
||||
*
|
||||
* <p>The patterns may be a simple path which has a one-to-one mapping to a
|
||||
* resource on the classpath, or alternatively may contain the special
|
||||
* {@code *} character to indicate a wildcard search.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ResourcePatternHint {
|
||||
public final class ResourcePatternHint implements ConditionalHint {
|
||||
|
||||
private final List<String> includes;
|
||||
private final String pattern;
|
||||
|
||||
private final List<String> excludes;
|
||||
@Nullable
|
||||
private final TypeReference reachableType;
|
||||
|
||||
|
||||
private ResourcePatternHint(Builder builder) {
|
||||
this.includes = new ArrayList<>(builder.includes);
|
||||
this.excludes = new ArrayList<>(builder.excludes);
|
||||
ResourcePatternHint(String pattern, @Nullable TypeReference reachableType) {
|
||||
this.pattern = pattern;
|
||||
this.reachableType = reachableType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the include patterns to use to identify the resources to match.
|
||||
* @return the include patterns
|
||||
* Return the pattern to use for identifying the resources to match.
|
||||
* @return the patterns
|
||||
*/
|
||||
public List<String> getIncludes() {
|
||||
return this.includes;
|
||||
public String getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exclude patterns to use to identify the resources to match.
|
||||
* @return the exclude patterns
|
||||
*/
|
||||
public List<String> getExcludes() {
|
||||
return this.excludes;
|
||||
@Nullable
|
||||
@Override
|
||||
public TypeReference getReachableType() {
|
||||
return this.reachableType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link ResourcePatternHint}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Set<String> includes = new LinkedHashSet<>();
|
||||
|
||||
private final Set<String> excludes = new LinkedHashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* Includes the resources matching the specified pattern.
|
||||
* @param includes the include patterns
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder includes(String... includes) {
|
||||
this.includes.addAll(Arrays.asList(includes));
|
||||
return this;
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude resources matching the specified pattern.
|
||||
* @param excludes the excludes pattern
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder excludes(String... excludes) {
|
||||
this.excludes.addAll(Arrays.asList(excludes));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ResourcePatternHint} based on the state of this
|
||||
* builder.
|
||||
* @return a resource pattern hint
|
||||
*/
|
||||
ResourcePatternHint build() {
|
||||
return new ResourcePatternHint(this);
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ResourcePatternHint that = (ResourcePatternHint) o;
|
||||
return this.pattern.equals(that.pattern)
|
||||
&& Objects.equals(this.reachableType, that.reachableType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.pattern, this.reachableType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.hint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A collection of {@link ResourcePatternHint} describing whether
|
||||
* resources should be made available at runtime through a matching
|
||||
* mechanism or inclusion/exclusion.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ResourcePatternHints {
|
||||
|
||||
private final List<ResourcePatternHint> includes;
|
||||
|
||||
private final List<ResourcePatternHint> excludes;
|
||||
|
||||
|
||||
private ResourcePatternHints(Builder builder) {
|
||||
this.includes = new ArrayList<>(builder.includes);
|
||||
this.excludes = new ArrayList<>(builder.excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the include patterns to use to identify the resources to match.
|
||||
* @return the include patterns
|
||||
*/
|
||||
public List<ResourcePatternHint> getIncludes() {
|
||||
return this.includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exclude patterns to use to identify the resources to match.
|
||||
* @return the exclude patterns
|
||||
*/
|
||||
public List<ResourcePatternHint> getExcludes() {
|
||||
return this.excludes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link ResourcePatternHints}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Set<ResourcePatternHint> includes = new LinkedHashSet<>();
|
||||
|
||||
private final Set<ResourcePatternHint> excludes = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Includes the resources matching the specified pattern.
|
||||
* @param reachableType the type that should be reachable for this hint to apply
|
||||
* @param includes the include patterns
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder includes(TypeReference reachableType, String... includes) {
|
||||
List<ResourcePatternHint> newIncludes = Arrays.stream(includes)
|
||||
.map(include -> new ResourcePatternHint(include, reachableType)).toList();
|
||||
this.includes.addAll(newIncludes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the resources matching the specified pattern.
|
||||
* @param includes the include patterns
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder includes(String... includes) {
|
||||
return includes(null, includes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude resources matching the specified pattern.
|
||||
* @param reachableType the type that should be reachable for this hint to apply
|
||||
* @param excludes the excludes pattern
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder excludes(TypeReference reachableType, String... excludes) {
|
||||
List<ResourcePatternHint> newExcludes = Arrays.stream(excludes)
|
||||
.map(include -> new ResourcePatternHint(include, reachableType)).toList();
|
||||
this.excludes.addAll(newExcludes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude resources matching the specified pattern.
|
||||
* @param excludes the excludes pattern
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder excludes(String... excludes) {
|
||||
return excludes(null, excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ResourcePatternHints} based on the state of this
|
||||
* builder.
|
||||
* @return a resource pattern hint
|
||||
*/
|
||||
ResourcePatternHints build() {
|
||||
return new ResourcePatternHints(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class RuntimeHints {
|
||||
|
||||
private final ResourceHints resources = new ResourceHints();
|
||||
|
||||
private final JavaSerializationHints javaSerialization = new JavaSerializationHints();
|
||||
private final SerializationHints serialization = new SerializationHints();
|
||||
|
||||
private final ProxyHints proxies = new ProxyHints();
|
||||
|
||||
@@ -59,10 +59,10 @@ public class RuntimeHints {
|
||||
|
||||
/**
|
||||
* Provide access to serialization-based hints.
|
||||
* @return java serialization hints
|
||||
* @return serialization hints
|
||||
*/
|
||||
public JavaSerializationHints javaSerialization() {
|
||||
return this.javaSerialization;
|
||||
public SerializationHints serialization() {
|
||||
return this.serialization;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.hint;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Gather the need for Java serialization at runtime.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @see Serializable
|
||||
*/
|
||||
public class SerializationHints {
|
||||
|
||||
private final Set<JavaSerializationHint> javaSerializationHints;
|
||||
|
||||
|
||||
public SerializationHints() {
|
||||
this.javaSerializationHints = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link JavaSerializationHint java serialization hints} for types
|
||||
* that need to be serialized using Java serialization at runtime.
|
||||
* @return a stream of {@link JavaSerializationHint java serialization hints}
|
||||
*/
|
||||
public Stream<JavaSerializationHint> javaSerialization() {
|
||||
return this.javaSerializationHints.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the type defined by the specified {@link TypeReference}
|
||||
* need to be serialized using java serialization.
|
||||
* @param type the type to register
|
||||
* @param serializationHint a builder to further customize the serialization
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public SerializationHints registerType(TypeReference type, @Nullable Consumer<JavaSerializationHint.Builder> serializationHint) {
|
||||
JavaSerializationHint.Builder builder = new JavaSerializationHint.Builder(type);
|
||||
if (serializationHint != null) {
|
||||
serializationHint.accept(builder);
|
||||
}
|
||||
this.javaSerializationHints.add(builder.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the type defined by the specified {@link TypeReference}
|
||||
* need to be serialized using java serialization.
|
||||
* @param type the type to register
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public SerializationHints registerType(TypeReference type) {
|
||||
return registerType(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the specified type need to be serialized using java
|
||||
* serialization.
|
||||
* @param type the type to register
|
||||
* @param serializationHint a builder to further customize the serialization
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public SerializationHints registerType(Class<? extends Serializable> type, @Nullable Consumer<JavaSerializationHint.Builder> serializationHint) {
|
||||
return registerType(TypeReference.of(type), serializationHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register that the specified type need to be serialized using java
|
||||
* serialization.
|
||||
* @param type the type to register
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public SerializationHints registerType(Class<? extends Serializable> type) {
|
||||
return registerType(type, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import org.springframework.util.Assert;
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class TypeHint {
|
||||
public final class TypeHint implements ConditionalHint {
|
||||
|
||||
private final TypeReference type;
|
||||
|
||||
@@ -81,12 +81,8 @@ public final class TypeHint {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type that should be reachable for this hint to apply, or
|
||||
* {@code null} if this hint should always been applied.
|
||||
* @return the reachable type, if any
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public TypeReference getReachableType() {
|
||||
return this.reachableType;
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.Map;
|
||||
|
||||
import org.springframework.aot.hint.JavaSerializationHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* Write a {@link JavaSerializationHints} to the JSON output expected by the
|
||||
* GraalVM {@code native-image} compiler, typically named
|
||||
* {@code serialization-config.json}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.0/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
class JavaSerializationHintsWriter {
|
||||
|
||||
public static final JavaSerializationHintsWriter INSTANCE = new JavaSerializationHintsWriter();
|
||||
|
||||
public void write(BasicJsonWriter writer, JavaSerializationHints hints) {
|
||||
writer.writeArray(hints.types().map(this::toAttributes).toList());
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(TypeReference typeReference) {
|
||||
LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
|
||||
attributes.put("name", typeReference);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,11 +18,11 @@ package org.springframework.aot.nativex;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aot.hint.JavaSerializationHints;
|
||||
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,7 +30,7 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @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/22.1/reference-manual/native-image/BuildConfiguration/">Native Image Build Configuration</a>
|
||||
*/
|
||||
public abstract class NativeConfigurationWriter {
|
||||
|
||||
@@ -39,8 +39,8 @@ public abstract class NativeConfigurationWriter {
|
||||
* @param hints the hints to handle
|
||||
*/
|
||||
public void write(RuntimeHints hints) {
|
||||
if (hints.javaSerialization().types().findAny().isPresent()) {
|
||||
writeJavaSerializationHints(hints.javaSerialization());
|
||||
if (hints.serialization().javaSerialization().findAny().isPresent()) {
|
||||
writeJavaSerializationHints(hints.serialization());
|
||||
}
|
||||
if (hints.proxies().jdkProxies().findAny().isPresent()) {
|
||||
writeProxyHints(hints.proxies());
|
||||
@@ -62,9 +62,9 @@ public abstract class NativeConfigurationWriter {
|
||||
*/
|
||||
protected abstract void writeTo(String fileName, Consumer<BasicJsonWriter> writer);
|
||||
|
||||
private void writeJavaSerializationHints(JavaSerializationHints hints) {
|
||||
private void writeJavaSerializationHints(SerializationHints hints) {
|
||||
writeTo("serialization-config.json", writer ->
|
||||
JavaSerializationHintsWriter.INSTANCE.write(writer, hints));
|
||||
SerializationHintsWriter.INSTANCE.write(writer, hints));
|
||||
}
|
||||
|
||||
private void writeProxyHints(ProxyHints hints) {
|
||||
|
||||
@@ -30,9 +30,10 @@ import org.springframework.aot.hint.TypeReference;
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.0/reference-manual/native-image/DynamicProxy/">Dynamic Proxy 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/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 {
|
||||
|
||||
@@ -44,9 +45,18 @@ class ProxyHintsWriter {
|
||||
|
||||
private Map<String, Object> toAttributes(JdkProxyHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
handleCondition(attributes, hint);
|
||||
attributes.put("interfaces", hint.getProxiedInterfaces().stream()
|
||||
.map(TypeReference::getCanonicalName).toList());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.aot.hint.ConditionalHint;
|
||||
import org.springframework.aot.hint.ResourceBundleHint;
|
||||
import org.springframework.aot.hint.ResourceHints;
|
||||
import org.springframework.aot.hint.ResourcePatternHint;
|
||||
import org.springframework.aot.hint.ResourcePatternHints;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -36,9 +38,10 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.0
|
||||
* @see <a href="https://www.graalvm.org/22.0/reference-manual/native-image/Resources/">Accessing Resources in Native Images</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/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 {
|
||||
|
||||
@@ -53,9 +56,9 @@ class ResourceHintsWriter {
|
||||
|
||||
private Map<String, Object> toAttributes(ResourceHints hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
addIfNotEmpty(attributes, "includes", hint.resourcePatterns().map(ResourcePatternHint::getIncludes)
|
||||
addIfNotEmpty(attributes, "includes", hint.resourcePatterns().map(ResourcePatternHints::getIncludes)
|
||||
.flatMap(List::stream).distinct().map(this::toAttributes).toList());
|
||||
addIfNotEmpty(attributes, "excludes", hint.resourcePatterns().map(ResourcePatternHint::getExcludes)
|
||||
addIfNotEmpty(attributes, "excludes", hint.resourcePatterns().map(ResourcePatternHints::getExcludes)
|
||||
.flatMap(List::stream).distinct().map(this::toAttributes).toList());
|
||||
return attributes;
|
||||
}
|
||||
@@ -66,13 +69,15 @@ class ResourceHintsWriter {
|
||||
|
||||
private Map<String, Object> toAttributes(ResourceBundleHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
handleCondition(attributes, hint);
|
||||
attributes.put("name", hint.getBaseName());
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private Map<String, Object> toAttributes(String pattern) {
|
||||
private Map<String, Object> toAttributes(ResourcePatternHint hint) {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
attributes.put("pattern", patternToRegexp(pattern));
|
||||
handleCondition(attributes, hint);
|
||||
attributes.put("pattern", patternToRegexp(hint.getPattern()));
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@@ -96,4 +101,12 @@ 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());
|
||||
attributes.put("condition", conditionAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.Map;
|
||||
|
||||
import org.springframework.aot.hint.ConditionalHint;
|
||||
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}.
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class SerializationHintsWriter {
|
||||
|
||||
public static final SerializationHintsWriter INSTANCE = new SerializationHintsWriter();
|
||||
|
||||
public void write(BasicJsonWriter writer, SerializationHints hints) {
|
||||
writer.writeArray(hints.javaSerialization().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());
|
||||
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());
|
||||
attributes.put("condition", conditionAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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