Skip to content

Commit 61d5096

Browse files
authored
Performance improvements, replace several streams w/loops (#2404)
* Improve efficiency of method override retrieval Signed-off-by: Michael Edgar <[email protected]> * Replace several streams with plain loops Signed-off-by: Michael Edgar <[email protected]> * Eliminate duplicate param processing, replace more streams with loops Signed-off-by: Michael Edgar <[email protected]> * Use several new Jandex 3.5.2 APIs for efficiency improvements Signed-off-by: Michael Edgar <[email protected]> * Split up some methods that became more complex due to streams to loops Signed-off-by: Michael Edgar <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]>
1 parent 8aeb7b8 commit 61d5096

File tree

17 files changed

+321
-249
lines changed

17 files changed

+321
-249
lines changed

core/src/main/java/io/smallrye/openapi/api/constants/KotlinConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
*/
1010
public class KotlinConstants {
1111

12+
public static final DotName METADATA = DotName
13+
.createSimple("kotlin.Metadata");
14+
1215
public static final DotName CONTINUATION = DotName
1316
.createSimple("kotlin.coroutines.Continuation");
1417

core/src/main/java/io/smallrye/openapi/runtime/io/Names.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,35 @@ public static DotName containerOf(DotName repeatable) {
7474
return repeatContainers.get(repeatable);
7575
}
7676

77+
/**
78+
* Create componentized {@link DotName} instances for the given collection of
79+
* names. The results will be returned in a map, keyed by the input element from
80+
* which each entry was derived.
81+
*/
82+
public static Map<String, DotName> componentize(Collection<String> names) {
83+
Map<DotName, DotName> workingSet = new HashMap<>();
84+
Map<String, DotName> results = new HashMap<>();
85+
86+
for (String name : names) {
87+
String[] elements = name.split("\\.");
88+
DotName current = null;
89+
90+
for (String element : elements) {
91+
current = DotName.createComponentized(current, element);
92+
93+
if (workingSet.containsKey(current)) {
94+
current = workingSet.get(current);
95+
} else {
96+
workingSet.put(current, current);
97+
}
98+
}
99+
100+
results.put(name, current);
101+
}
102+
103+
return results;
104+
}
105+
77106
public static final DotName OPENAPI_DEFINITION = createIndexable(OpenAPIDefinition.class);
78107
public static final DotName API_RESPONSE = createIndexable(APIResponse.class);
79108
public static final DotName API_RESPONSES = createIndexable(APIResponses.class);

core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiDataObjectScanner.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import io.smallrye.openapi.runtime.scanner.dataobject.AnnotationTargetProcessor;
3535
import io.smallrye.openapi.runtime.scanner.dataobject.AugmentedIndexView;
3636
import io.smallrye.openapi.runtime.scanner.dataobject.DataObjectDeque;
37+
import io.smallrye.openapi.runtime.scanner.dataobject.DataObjectDeque.PathEntry;
3738
import io.smallrye.openapi.runtime.scanner.dataobject.IgnoreResolver;
3839
import io.smallrye.openapi.runtime.scanner.dataobject.TypeResolver;
3940
import io.smallrye.openapi.runtime.scanner.spi.AnnotationScannerContext;
@@ -281,14 +282,7 @@ private void depthFirstGraphSearch() {
281282
reference);
282283

283284
processClassAnnotations(currentSchema, currentClass);
284-
285-
// Handle fields
286-
properties.values()
287-
.stream()
288-
.filter(resolver -> !resolver.isIgnored())
289-
.forEach(resolver -> AnnotationTargetProcessor.process(context, objectStack, resolver,
290-
currentPathEntry));
291-
285+
processProperties(currentPathEntry, properties);
292286
processInheritance(currentPathEntry);
293287
}
294288
}
@@ -327,6 +321,14 @@ private void processClassAnnotations(Schema schema, ClassInfo classInfo) {
327321
}
328322
}
329323

324+
private void processProperties(PathEntry currentPathEntry, Map<String, TypeResolver> properties) {
325+
for (TypeResolver resolver : properties.values()) {
326+
if (!resolver.isIgnored()) {
327+
AnnotationTargetProcessor.process(context, objectStack, resolver, currentPathEntry);
328+
}
329+
}
330+
}
331+
330332
private void processInheritance(DataObjectDeque.PathEntry currentPathEntry) {
331333
ClassInfo currentClass = currentPathEntry.getClazz();
332334
Schema currentSchema = currentPathEntry.getSchema();

core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/AugmentedIndexView.java

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -111,36 +111,33 @@ public Map<ClassInfo, MethodInfo> ancestry(MethodInfo method) {
111111
Map<ClassInfo, MethodInfo> ancestry = new LinkedHashMap<>();
112112

113113
for (ClassInfo classInfo : chain.keySet()) {
114-
ancestry.put(classInfo, null);
115-
116-
classInfo.methods()
117-
.stream()
118-
.filter(m -> !m.isSynthetic())
119-
.filter(m -> isSameSignature(method, m))
120-
.findFirst()
121-
.ifPresent(m -> ancestry.put(classInfo, m));
122-
123-
interfaces(classInfo)
124-
.stream()
125-
.filter(type -> !TypeUtil.knownJavaType(type.name()))
126-
.map(this::getClass)
127-
.filter(Objects::nonNull)
128-
.map(iface -> {
114+
if (!saveOverride(method, classInfo, ancestry)) {
115+
ancestry.put(classInfo, null);
116+
}
117+
118+
for (Type ifaceType : interfaces(classInfo)) {
119+
if (!TypeUtil.knownJavaType(ifaceType.name())) {
120+
ClassInfo iface = getClass(ifaceType);
121+
122+
if (!saveOverride(method, iface, ancestry)) {
129123
ancestry.put(iface, null);
130-
return iface;
131-
})
132-
.flatMap(iface -> iface.methods().stream())
133-
.filter(m -> isSameSignature(method, m))
134-
.forEach(m -> ancestry.put(m.declaringClass(), m));
124+
}
125+
}
126+
}
135127
}
136128

137129
return ancestry;
138130
}
139131

140-
private static boolean isSameSignature(MethodInfo m1, MethodInfo m2) {
141-
return Objects.equals(m1.name(), m2.name())
142-
&& m1.parametersCount() == m2.parametersCount()
143-
&& Objects.equals(m1.parameterTypes(), m2.parameterTypes());
132+
private static boolean saveOverride(MethodInfo searchMethod, ClassInfo clazz, Map<ClassInfo, MethodInfo> results) {
133+
MethodInfo classMethod = clazz.method(searchMethod);
134+
135+
if (classMethod != null && !classMethod.isSynthetic()) {
136+
results.put(clazz, classMethod);
137+
return true;
138+
}
139+
140+
return false;
144141
}
145142

146143
@Override

core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/TypeResolver.java

Lines changed: 65 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -539,49 +539,23 @@ public static Map<String, TypeResolver> getAllFields(AnnotationScannerContext co
539539
Map<ClassInfo, Type> chain = index.inheritanceChain(leafKlazz, leaf);
540540
Map<String, TypeResolver> properties = new LinkedHashMap<>();
541541
Deque<Map<String, Type>> stack = new ArrayDeque<>();
542-
boolean skipPropertyScan = false;
542+
boolean skipScan = false;
543543
List<ClassInfo> descendants = new ArrayList<>(chain.size());
544544

545545
for (Map.Entry<ClassInfo, Type> entry : chain.entrySet()) {
546546
ClassInfo currentClass = entry.getKey();
547547
Type currentType = entry.getValue();
548-
maybeAddResolutionParams(stack, currentType, currentClass);
549548

550-
if (skipPropertyScan || (!currentType.equals(leaf) && TypeUtil.isAllOf(context, leafKlazz, currentType))
551-
|| TypeUtil.knownJavaType(currentClass.name())) {
552-
/*
553-
* Do not attempt to introspect fields of Java/JDK types or if the @Schema
554-
* annotation indicates the use of a `ref` for superclass fields.
555-
*/
556-
skipPropertyScan = true;
549+
skipScan = skipCheck(skipScan, context, leaf, leafKlazz, currentType, currentClass);
550+
551+
if (skipScan) {
557552
continue;
558553
}
559554

560-
// Store all field properties
561-
JandexUtil.fields(context, currentClass)
562-
.stream()
563-
.filter(TypeResolver::acceptField)
564-
.filter(field -> field.name().chars().allMatch(Character::isJavaIdentifierPart))
565-
.forEach(field -> scanField(context, properties, field, stack, reference, descendants));
566-
567-
methods(context, currentClass)
568-
.stream()
569-
.filter(TypeResolver::acceptMethod)
570-
.filter(method -> method.name().chars().allMatch(Character::isJavaIdentifierPart))
571-
.forEach(method -> scanMethod(context, properties, method, stack, reference, descendants));
572-
573-
index.interfaces(currentClass)
574-
.stream()
575-
.filter(type -> !TypeUtil.knownJavaType(type.name()))
576-
.map(type -> {
577-
ClassInfo clazz = index.getClass(type);
578-
maybeAddResolutionParams(stack, type, clazz);
579-
return clazz;
580-
})
581-
.filter(Objects::nonNull)
582-
.flatMap(clazz -> methods(context, clazz).stream())
583-
.filter(method -> method.name().chars().allMatch(Character::isJavaIdentifierPart))
584-
.forEach(method -> scanMethod(context, properties, method, stack, reference, descendants));
555+
maybeAddResolutionParams(stack, currentType, currentClass);
556+
scanFields(context, currentClass, properties, stack, reference, descendants);
557+
scanMethods(context, currentClass, properties, stack, reference, descendants);
558+
scanInterfaces(context, currentClass, properties, stack, reference, descendants);
585559

586560
descendants.add(currentClass);
587561
}
@@ -605,6 +579,63 @@ private static void maybeAddResolutionParams(Deque<Map<String, Type>> stack, Typ
605579
}
606580
}
607581

582+
private static boolean skipCheck(boolean skip, AnnotationScannerContext context, Type leafType,
583+
ClassInfo leafKlazz,
584+
Type currentType,
585+
ClassInfo currentClass) {
586+
if (skip || (!currentType.equals(leafType) && TypeUtil.isAllOf(context, leafKlazz, currentType))
587+
|| TypeUtil.knownJavaType(currentClass.name())) {
588+
/*
589+
* Do not attempt to introspect fields of Java/JDK types or if the @Schema
590+
* annotation indicates the use of a `ref` for superclass fields.
591+
*/
592+
skip = true;
593+
}
594+
595+
return skip;
596+
}
597+
598+
private static void scanFields(AnnotationScannerContext context, ClassInfo currentClass,
599+
Map<String, TypeResolver> properties, Deque<Map<String, Type>> stack, AnnotationTarget reference,
600+
List<ClassInfo> descendants) {
601+
for (FieldInfo field : JandexUtil.fields(context, currentClass)) {
602+
if (acceptField(field) && field.name().chars().allMatch(Character::isJavaIdentifierPart)) {
603+
scanField(context, properties, field, stack, reference, descendants);
604+
}
605+
}
606+
}
607+
608+
private static void scanMethods(AnnotationScannerContext context, ClassInfo currentClass,
609+
Map<String, TypeResolver> properties, Deque<Map<String, Type>> stack, AnnotationTarget reference,
610+
List<ClassInfo> descendants) {
611+
for (MethodInfo method : methods(context, currentClass)) {
612+
if (acceptMethod(method) && method.name().chars().allMatch(Character::isJavaIdentifierPart)) {
613+
scanMethod(context, properties, method, stack, reference, descendants);
614+
}
615+
}
616+
}
617+
618+
private static void scanInterfaces(AnnotationScannerContext context, ClassInfo currentClass,
619+
Map<String, TypeResolver> properties, Deque<Map<String, Type>> stack, AnnotationTarget reference,
620+
List<ClassInfo> descendants) {
621+
AugmentedIndexView index = context.getAugmentedIndex();
622+
623+
for (Type type : index.interfaces(currentClass)) {
624+
if (!TypeUtil.knownJavaType(type.name())) {
625+
ClassInfo clazz = index.getClass(type);
626+
maybeAddResolutionParams(stack, type, clazz);
627+
628+
if (clazz != null) {
629+
for (MethodInfo method : methods(context, clazz)) {
630+
if (method.name().chars().allMatch(Character::isJavaIdentifierPart)) {
631+
scanMethod(context, properties, method, stack, reference, descendants);
632+
}
633+
}
634+
}
635+
}
636+
}
637+
}
638+
608639
private static List<MethodInfo> methods(AnnotationScannerContext context, ClassInfo currentClass) {
609640
if (context.getConfig().sortedPropertiesEnable()) {
610641
return currentClass.methods();

core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AbstractParameterProcessor.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Set;
2020
import java.util.concurrent.ConcurrentHashMap;
2121
import java.util.function.Consumer;
22-
import java.util.function.Function;
2322
import java.util.function.Predicate;
2423
import java.util.function.Supplier;
2524
import java.util.regex.Matcher;
@@ -78,7 +77,6 @@ public abstract class AbstractParameterProcessor {
7877
protected final AnnotationScannerContext scannerContext;
7978
protected final String contextPath;
8079
protected final IndexView index;
81-
protected final Function<AnnotationInstance, Parameter> readerFunction;
8280
protected final Optional<BeanValidationScanner> beanValidationScanner;
8381
protected final ClassInfo resourceClass;
8482
protected final MethodInfo resourceMethod;
@@ -114,13 +112,11 @@ public abstract class AbstractParameterProcessor {
114112

115113
protected AbstractParameterProcessor(AnnotationScannerContext scannerContext,
116114
String contextPath,
117-
Function<AnnotationInstance, Parameter> reader,
118115
ClassInfo resourceClass,
119116
MethodInfo resourceMethod) {
120117
this.scannerContext = scannerContext;
121118
this.contextPath = contextPath;
122119
this.index = scannerContext.getIndex();
123-
this.readerFunction = reader;
124120
this.beanValidationScanner = scannerContext.getBeanValidationScanner();
125121
this.resourceClass = resourceClass;
126122
this.resourceMethod = resourceMethod;
@@ -300,7 +296,7 @@ protected void reset() {
300296
matrixParams.clear();
301297
}
302298

303-
protected ResourceParameters process() {
299+
public ResourceParameters process() {
304300

305301
ResourceParameters parameters = new ResourceParameters();
306302

@@ -397,6 +393,10 @@ protected void processFinalize(ClassInfo resourceClass, MethodInfo resourceMetho
397393
parameters.setFormBodyContent(getFormBodyContent());
398394
}
399395

396+
public void updateFormBodyContent(ResourceParameters parameters) {
397+
parameters.setFormBodyContent(getFormBodyContent());
398+
}
399+
400400
/**
401401
* Generate the paths for the provided annotation target, either a class or a method.
402402
* Add the name of any discovered matrix parameters.
@@ -1265,11 +1265,11 @@ List<String> methodPaths(MethodInfo method) {
12651265
protected abstract List<String> pathsOf(AnnotationTarget target);
12661266

12671267
protected boolean isReadableParameterAnnotation(DotName name) {
1268-
return Names.PARAMETER.equals(name) && readerFunction != null;
1268+
return Names.PARAMETER.equals(name);
12691269
}
12701270

12711271
protected void readParameterAnnotation(AnnotationInstance annotation, boolean overriddenParametersOnly) {
1272-
Parameter oaiParam = readerFunction.apply(annotation);
1272+
Parameter oaiParam = scannerContext.io().parameterIO().read(annotation);
12731273

12741274
if (oaiParam.getRef() != null) {
12751275
Parameter commonParam = ModelUtil.dereference(scannerContext.getOpenApi(), oaiParam);

core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public interface AnnotationScanner {
7878

7979
public boolean isDeleteMethod(final MethodInfo method);
8080

81-
public boolean containsScannerAnnotations(List<AnnotationInstance> instances,
81+
public boolean containsScannerAnnotations(Collection<AnnotationInstance> instances,
8282
List<AnnotationScannerExtension> extensions);
8383

8484
// Allow runtimes to set the context root path
@@ -977,7 +977,7 @@ default Type getRequestBodyParameterClassType(final AnnotationScannerContext con
977977
.filter(position -> !isFrameworkContextType(methodParams.get(position)))
978978
.filter(position -> !isPathParameter(context, method.parameterName(position), params))
979979
.filter(position -> {
980-
List<AnnotationInstance> annotations = context.annotations().getMethodParameterAnnotations(method,
980+
Collection<AnnotationInstance> annotations = context.annotations().getMethodParameterAnnotations(method,
981981
position);
982982
return annotations.isEmpty() || !containsScannerAnnotations(annotations, context.getExtensions());
983983
})
@@ -988,7 +988,7 @@ default Type getRequestBodyParameterClassType(final AnnotationScannerContext con
988988

989989
default void setRequestBodyConstraints(AnnotationScannerContext context, RequestBody requestBody, MethodInfo method,
990990
Type requestBodyType) {
991-
List<AnnotationInstance> paramAnnotations = context.annotations().getMethodParameterAnnotations(method,
991+
Collection<AnnotationInstance> paramAnnotations = context.annotations().getMethodParameterAnnotations(method,
992992
requestBodyType);
993993
Optional<BeanValidationScanner> constraintScanner = context.getBeanValidationScanner();
994994

0 commit comments

Comments
 (0)