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:
Brian Clozel
2022-06-02 11:12:09 +02:00
parent 2269c0063a
commit 8af1496b37
29 changed files with 806 additions and 275 deletions

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
/**

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}