Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit aad2092

Browse files
Refine Spring HATEOAS configuration detection and jackson2 mapping.
To cover Jackson2 de-/serializer, types annotated with (at) Configuration and those implementing EntityLink.
1 parent 8583528 commit aad2092

File tree

17 files changed

+707
-48
lines changed

17 files changed

+707
-48
lines changed

spring-aot/src/main/java/org/springframework/nativex/type/Type.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.function.Consumer;
3838
import java.util.function.Predicate;
3939
import java.util.stream.Collectors;
40+
import java.util.stream.Stream;
4041

4142
import org.apache.commons.logging.Log;
4243
import org.apache.commons.logging.LogFactory;
@@ -1073,7 +1074,7 @@ List<Type> getJavaxAnnotations() {
10731074
return getJavaxAnnotations(new HashSet<>());
10741075
}
10751076

1076-
private boolean isAnnotated(String Ldescriptor) {
1077+
public boolean isAnnotated(String Ldescriptor) {
10771078
if (dimensions > 0) {
10781079
return false;
10791080
}

spring-aot/src/main/java/org/springframework/nativex/type/TypeProcessor.java

+30-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Arrays;
2020
import java.util.LinkedHashSet;
2121
import java.util.List;
22+
import java.util.Objects;
2223
import java.util.Set;
2324
import java.util.TreeSet;
2425
import java.util.function.BiConsumer;
@@ -110,7 +111,15 @@ public static TypeProcessor namedProcessor(String componentLogName) {
110111
* @param annotationAccessDescriptor the {@link AccessDescriptor} to use for annotations.
111112
*/
112113
public TypeProcessor(AccessDescriptor typeAccessDescriptor, AccessDescriptor annotationAccessDescriptor) {
113-
this((type, context) -> context.addReflectiveAccess(type, typeAccessDescriptor), (type, context) -> context.addReflectiveAccess(type, annotationAccessDescriptor));
114+
this((type, context) -> {
115+
116+
context.log(String.format("TypeProcessor - Registering %s with access %s.", type.getName(), typeAccessDescriptor));
117+
context.addReflectiveAccess(type, typeAccessDescriptor);
118+
}, (type, context) -> {
119+
120+
context.log(String.format("TypeProcessor - Registering %s with access %s.", type.getName(), annotationAccessDescriptor));
121+
context.addReflectiveAccess(type, annotationAccessDescriptor);
122+
});
114123
}
115124

116125
/**
@@ -461,7 +470,8 @@ private void processSignatureTypesOfType(Type domainType, NativeContext imageCon
461470

462471
domainType.getTypesInSignature()
463472
.stream()
464-
.map(imageContext.getTypeSystem()::resolve)
473+
.map(it -> imageContext.getTypeSystem().resolve(it, true))
474+
.filter(Objects::nonNull)
465475
.forEach(it -> process(it, imageContext, seen));
466476
}
467477

@@ -501,7 +511,8 @@ private void processFieldsOfType(Type domainType, NativeContext imageContext, Se
501511

502512
imageContext.log(String.format(componentLogName + ": inspecting field %s of %s", field.getName(), domainType.getDottedName()));
503513

504-
field.getTypesInSignature().stream().map(imageContext.getTypeSystem()::resolve)
514+
field.getTypesInSignature().stream().map(it -> imageContext.getTypeSystem().resolve(it, true))
515+
.filter(Objects::nonNull)
505516
.forEach(signatureType -> process(signatureType, imageContext, seen));
506517

507518
field.getAnnotationTypes().forEach(annotation -> {
@@ -543,6 +554,10 @@ private void processAnnotation(Type annotation, NativeContext imageContext, Set<
543554
processAnnotationsOnType(annotation, imageContext, seen);
544555
}
545556

557+
public String getComponentLogName() {
558+
return componentLogName;
559+
}
560+
546561
public static boolean isExcludedByDefault(Type type) {
547562
return type.getDottedName().equals("java.lang.Object") || type.isPartOfDomain("org.hibernate.engine") || type.isPartOfDomain("sun") || type.isPartOfDomain("jdk.");
548563
}
@@ -727,6 +742,14 @@ public void addReflectiveAccess(String typeName, AccessDescriptor descriptor) {
727742

728743
HintDeclaration hintDeclaration = new HintDeclaration();
729744
hintDeclaration.addDependantType(typeName, descriptor);
745+
if (requiresResourceAccess(descriptor)) {
746+
747+
String resourceName = TypeName.fromClassName(typeName.replace("[]", "")).toSlashName();
748+
if (!resourceName.endsWith(".class")) {
749+
resourceName = resourceName + ".class";
750+
}
751+
hintDeclaration.addResourcesDescriptor(new ResourcesDescriptor(new String[]{resourceName}, false));
752+
}
730753
reflectionHints.add(hintDeclaration);
731754
}
732755

@@ -739,6 +762,10 @@ public Set<String> addReflectiveAccessHierarchy(String typename, int accessBits)
739762
return added;
740763
}
741764

765+
private boolean requiresResourceAccess(AccessDescriptor accessDescriptor) {
766+
return accessDescriptor.getAccessBits() != null && AccessBits.isSet(AccessBits.RESOURCE, accessDescriptor.getAccessBits());
767+
}
768+
742769
private void registerHierarchy(Type type, Set<String> visited, int accessBits) {
743770

744771
String typename = type.getDottedName();

spring-native-configuration/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaHints.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,15 @@
120120
EntityManagerMessageLogger.class, // CoreMessageLogger.class,
121121
Session.class, EventListener.class,
122122

123-
EnumType.class
123+
EnumType.class,
124+
InheritanceType.class,
125+
124126
}, typeNames = {
125127
"org.hibernate.internal.EntityManagerMessageLogger_$logger",
126128
"org.hibernate.service.jta.platform.internal.NoJtaPlatform",
127129
"org.hibernate.annotations.common.util.impl.Log_$logger",
128130
"org.hibernate.annotations.common.util.impl.Log",
131+
"org.hibernate.cfg.beanvalidation.TypeSafeActivator",
129132
}),
130133
@TypeHint(types = {org.hibernate.internal.CoreMessageLogger_$logger.class,
131134
// These from EntityTuplizerFactory

spring-native-configuration/src/main/java/org/springframework/data/JpaComponentProcessor.java

+2
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ public boolean requiresSpecialTreatment(Type type) {
147147
public void process(Type type, NativeContext context) {
148148

149149
if (type.isEnum() && !context.hasReflectionConfigFor(ENUM_TYPE)) {
150+
151+
context.log(String.format("JpaComponentProcessor: detected enum usage in entity. Adding load/construct for %s.", type, ENUM_TYPE));
150152
context.addReflectiveAccess(ENUM_TYPE, new AccessDescriptor(AccessBits.LOAD_AND_CONSTRUCT));
151153
}
152154
}

spring-native-configuration/src/main/java/org/springframework/data/web/config/DataRestHints.java

+45-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
2626
import org.springframework.boot.autoconfigure.web.servlet.WebMvcHints;
27+
import org.springframework.nativex.type.AccessDescriptor;
28+
import org.springframework.nativex.type.MissingTypeException;
2729
import org.springframework.nativex.type.TypeProcessor;
2830
import org.springframework.nativex.type.TypeProcessor.TypeHintCreatingProcessor;
2931
import org.springframework.hateoas.config.HateoasHints;
@@ -116,6 +118,7 @@ public class DataRestHints implements NativeConfiguration {
116118
private static final String BASE_PATH_AWARE_CONTROLLER = "Lorg/springframework/data/rest/webmvc/BasePathAwareController;";
117119
private static final String REPOSITORY_REST_CONFIGURER = "org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer";
118120
private static final String REPOSITORY_REST_RESOURCE = "Lorg/springframework/data/rest/core/annotation/RepositoryRestResource;";
121+
private static final String JACKSON_ANNOTATION = "Lcom/fasterxml/jackson/annotation/JacksonAnnotation;";
119122

120123
@Override
121124
public List<HintDeclaration> computeHints(TypeSystem typeSystem) {
@@ -125,14 +128,15 @@ public List<HintDeclaration> computeHints(TypeSystem typeSystem) {
125128
hints.addAll(computeRestControllerHints(typeSystem));
126129
hints.addAll(computeRepositoryRestConfigurer(typeSystem));
127130
hints.addAll(computeExcerptProjectionHints(typeSystem));
131+
hints.addAll(computeJacksonMappingCandidates(typeSystem));
128132

129133
// TODO: what about RestResource and others
130134

131135
return hints;
132136
}
133137

134138
/**
135-
* Compute {@link HintDeclaration hints} for all types that extend org.springframework.data.rest.webmvc.configRepositoryRestConfigurer.
139+
* Compute {@link HintDeclaration hints} for all types that extend org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer.
136140
* like "org.springframework.boot.autoconfigure.data.rest.SpringBootRepositoryRestConfigurer"
137141
*
138142
* @param typeSystem must not be {@literal null}.
@@ -145,6 +149,9 @@ private List<HintDeclaration> computeRepositoryRestConfigurer(TypeSystem typeSys
145149
.skipMethodInspection()
146150
.skipFieldInspection()
147151
.skipConstructorInspection()
152+
.onTypeDiscovered((type, context) -> {
153+
context.addReflectiveAccess(type, new AccessDescriptor(AccessBits.ALL));
154+
})
148155
.use(typeSystem)
149156
.toProcessTypesMatching(it ->
150157
it.implementsInterface(REPOSITORY_REST_CONFIGURER, true)
@@ -221,5 +228,42 @@ private List<HintDeclaration> computeExcerptProjectionHints(TypeSystem typeSyste
221228
.collect(Collectors.toList());
222229
}
223230

231+
List<HintDeclaration> computeJacksonMappingCandidates(TypeSystem typeSystem) {
232+
233+
return TypeProcessor.namedProcessor("RestMvcConfigurationProcessor - Jackson Mapping Candidates")
234+
.skipTypesMatching(type -> {
235+
if(type.isPartOfDomain("com.fasterxml.jackson.") || type.isPartOfDomain("java.")) {
236+
return true;
237+
}
238+
if(type.isPartOfDomain("org.springframework.")) {
239+
if(!type.isPartOfDomain("org.springframework.data.rest")) {
240+
return true;
241+
}
242+
}
243+
return false;
244+
})
245+
.filterAnnotations(annotation -> annotation.isPartOfDomain("com.fasterxml.jackson."))
246+
.use(typeSystem)
247+
.toProcessTypesMatching(this::usesJackson);
248+
}
249+
250+
private boolean usesJackson(Type type) {
251+
252+
try {
253+
if (type.getAnnotations().stream().filter(ann -> ann.isAnnotated(JACKSON_ANNOTATION)).findAny().isPresent()) {
254+
return true;
255+
}
256+
257+
if (!type.getMethodsWithAnnotation(JACKSON_ANNOTATION, true).isEmpty()) {
258+
return true;
259+
}
260+
261+
return !type.getFieldsWithAnnotation(JACKSON_ANNOTATION, true).isEmpty();
262+
} catch (MissingTypeException e) {
263+
264+
}
265+
266+
return false;
267+
}
224268
// TODO: split up and refine access to hateos and plugin configuration classes
225269
}

0 commit comments

Comments
 (0)