Skip to content

Commit c979e2c

Browse files
committed
Reduce reflection requirements and simplify handling handlers
1 parent eb579d9 commit c979e2c

File tree

2 files changed

+78
-81
lines changed

2 files changed

+78
-81
lines changed

extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package io.quarkus.amazon.lambda.deployment;
22

33
import java.lang.reflect.Method;
4-
import java.lang.reflect.Modifier;
54
import java.util.ArrayList;
6-
import java.util.Collections;
75
import java.util.HashMap;
86
import java.util.List;
97
import java.util.Map;
@@ -16,7 +14,6 @@
1614
import org.jboss.jandex.AnnotationInstance;
1715
import org.jboss.jandex.ClassInfo;
1816
import org.jboss.jandex.DotName;
19-
import org.jboss.jandex.MethodInfo;
2017
import org.jboss.logging.Logger;
2118
import org.joda.time.DateTime;
2219

@@ -46,6 +43,7 @@
4643
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
4744
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
4845
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
46+
import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem;
4947
import io.quarkus.deployment.pkg.steps.NativeBuild;
5048
import io.quarkus.deployment.recording.RecorderContext;
5149
import io.quarkus.runtime.LaunchMode;
@@ -90,88 +88,51 @@ List<AmazonLambdaBuildItem> discover(CombinedIndexBuildItem combinedIndexBuildIt
9088
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchy,
9189
BuildProducer<ReflectiveClassBuildItem> reflectiveClassBuildItemBuildProducer) throws BuildException {
9290

93-
List<ClassInfo> allKnownImplementors = new ArrayList<>(
94-
combinedIndexBuildItem.getIndex().getAllKnownImplementors(REQUEST_HANDLER)
91+
List<ClassInfo> requestHandlers = new ArrayList<>(
92+
combinedIndexBuildItem.getIndex().getAllKnownImplementations(REQUEST_HANDLER)
9593
.stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
96-
allKnownImplementors.addAll(combinedIndexBuildItem.getIndex()
97-
.getAllKnownImplementors(REQUEST_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
98-
allKnownImplementors.addAll(combinedIndexBuildItem.getIndex()
94+
95+
List<ClassInfo> streamHandlers = new ArrayList<>(combinedIndexBuildItem.getIndex()
96+
.getAllKnownImplementations(REQUEST_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
97+
streamHandlers.addAll(combinedIndexBuildItem.getIndex()
9998
.getAllKnownSubclasses(SKILL_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
10099

101-
if (allKnownImplementors.size() > 0 && providedLambda.isPresent()) {
100+
if ((!requestHandlers.isEmpty() || !streamHandlers.isEmpty()) && providedLambda.isPresent()) {
102101
throw new BuildException(
103102
"Multiple handler classes. You have a custom handler class and the " + providedLambda.get().getProvider()
104103
+ " extension. Please remove one of them from your deployment.",
105-
Collections.emptyList());
104+
List.of());
106105

107106
}
108-
AdditionalBeanBuildItem.Builder builder = AdditionalBeanBuildItem.builder().setUnremovable();
109-
List<AmazonLambdaBuildItem> ret = new ArrayList<>();
107+
AdditionalBeanBuildItem.Builder additionalBeansBuilder = AdditionalBeanBuildItem.builder().setUnremovable();
108+
List<AmazonLambdaBuildItem> amazonLambdas = new ArrayList<>();
110109

111-
for (ClassInfo info : allKnownImplementors) {
112-
if (Modifier.isAbstract(info.flags())) {
110+
for (ClassInfo requestHandler : requestHandlers) {
111+
if (requestHandler.isAbstract()) {
113112
continue;
114113
}
115114

116-
final DotName name = info.name();
117-
final String lambda = name.toString();
118-
builder.addBeanClass(lambda);
115+
additionalBeansBuilder.addBeanClass(requestHandler.name().toString());
116+
amazonLambdas.add(new AmazonLambdaBuildItem(requestHandler.name().toString(), getCdiName(requestHandler), false));
117+
}
119118

120-
String cdiName = null;
121-
AnnotationInstance named = info.declaredAnnotation(NAMED);
122-
if (named != null) {
123-
cdiName = named.value().asString();
119+
for (ClassInfo streamHandler : streamHandlers) {
120+
if (streamHandler.isAbstract()) {
121+
continue;
124122
}
125123

126-
ClassInfo current = info;
127-
boolean done = false;
128-
boolean streamHandler = info.superName().equals(SKILL_STREAM_HANDLER);
129-
while (current != null && !done) {
130-
for (MethodInfo method : current.methods()) {
131-
if (method.name().equals("handleRequest")) {
132-
if (method.parametersCount() == 3) {
133-
streamHandler = true;
134-
done = true;
135-
break;
136-
} else if (method.parametersCount() == 2
137-
&& !method.parameterType(0).name().equals(DotName.createSimple(Object.class.getName()))) {
138-
String source = getClass().getSimpleName() + " > " + method.declaringClass() + "[" + method + "]";
139-
140-
reflectiveHierarchy.produce(ReflectiveHierarchyBuildItem
141-
.builder(method.parameterType(0))
142-
.source(source)
143-
.build());
144-
reflectiveHierarchy.produce(ReflectiveHierarchyBuildItem
145-
.builder(method.returnType())
146-
.source(source)
147-
.build());
148-
done = true;
149-
break;
150-
}
151-
}
152-
}
153-
if (!done) {
154-
current = combinedIndexBuildItem.getIndex().getClassByName(current.superName());
155-
}
156-
}
157-
if (done) {
158-
String handlerClass = current.name().toString();
159-
ret.add(new AmazonLambdaBuildItem(handlerClass, cdiName, streamHandler));
160-
reflectiveClassBuildItemBuildProducer.produce(ReflectiveClassBuildItem.builder(handlerClass).methods()
161-
.reason(getClass().getName()
162-
+ ": reflectively accessed in io.quarkus.amazon.lambda.runtime.AmazonLambdaRecorder.discoverHandlerMethod")
163-
.build());
164-
} else {
165-
// Fall back to the root implementor if a matching `handleRequest` is not found in the class hierarchy
166-
ret.add(new AmazonLambdaBuildItem(lambda, cdiName, streamHandler));
167-
}
124+
additionalBeansBuilder.addBeanClass(streamHandler.name().toString());
125+
amazonLambdas.add(new AmazonLambdaBuildItem(streamHandler.name().toString(), getCdiName(streamHandler), true));
168126
}
169-
additionalBeanBuildItemBuildProducer.produce(builder.build());
127+
128+
additionalBeanBuildItemBuildProducer.produce(additionalBeansBuilder.build());
129+
170130
reflectiveClassBuildItemBuildProducer
171131
.produce(ReflectiveClassBuildItem.builder(FunctionError.class).methods().fields()
172132
.reason(getClass().getName())
173133
.build());
174-
return ret;
134+
135+
return amazonLambdas;
175136
}
176137

177138
@BuildStep
@@ -225,7 +186,9 @@ public void recordStaticInitHandlerClass(CombinedIndexBuildItem index,
225186
LambdaObjectMapperInitializedBuildItem mapper, // ordering!
226187
Optional<ProvidedAmazonLambdaHandlerBuildItem> providedLambda,
227188
AmazonLambdaStaticRecorder recorder,
228-
RecorderContext context) {
189+
RecorderContext context,
190+
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
191+
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchies) {
229192
// can set handler within static initialization if only one handler exists in deployment
230193
if (providedLambda.isPresent()) {
231194
boolean useStreamHandler = false;
@@ -243,6 +206,7 @@ public void recordStaticInitHandlerClass(CombinedIndexBuildItem index,
243206
} else {
244207
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
245208
.discoverHandlerMethod(providedLambda.get().getHandlerClass().getName(), index.getComputingIndex());
209+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
246210
recorder.setHandlerClass(toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
247211
}
248212
} else if (lambdas != null && lambdas.size() == 1) {
@@ -255,6 +219,7 @@ public void recordStaticInitHandlerClass(CombinedIndexBuildItem index,
255219
} else {
256220
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
257221
.discoverHandlerMethod(item.getHandlerClass(), index.getComputingIndex());
222+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
258223
recorder.setHandlerClass(toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
259224
}
260225
} else if (lambdas == null || lambdas.isEmpty()) {
@@ -282,7 +247,9 @@ public void recordHandlerClass(CombinedIndexBuildItem index,
282247
BeanContainerBuildItem beanContainerBuildItem,
283248
AmazonLambdaRecorder recorder,
284249
List<ServiceStartBuildItem> orderServicesFirst, // try to order this after service recorders
285-
RecorderContext context) {
250+
RecorderContext context,
251+
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
252+
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchies) {
286253
// Have to set lambda class at runtime if there is not a provided lambda or there is more than one lambda in
287254
// deployment
288255
if (!providedLambda.isPresent() && lambdas != null && lambdas.size() > 1) {
@@ -305,10 +272,12 @@ public void recordHandlerClass(CombinedIndexBuildItem index,
305272
if (i.getName() == null) {
306273
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
307274
.discoverHandlerMethod(i.getHandlerClass(), index.getComputingIndex());
275+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
308276
unnamed.add(toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
309277
} else {
310278
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
311279
.discoverHandlerMethod(i.getHandlerClass(), index.getComputingIndex());
280+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
312281
named.put(i.getName(), toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
313282
}
314283
}
@@ -357,7 +326,40 @@ void recordExpectedExceptions(LambdaBuildTimeConfig config,
357326
recorder.setExpectedExceptionClasses(classes);
358327
}
359328

360-
public RequestHandlerDefinition toRequestHandlerDefinition(RequestHandlerJandexDefinition jandexDefinition,
329+
private static String getCdiName(ClassInfo handler) {
330+
AnnotationInstance named = handler.declaredAnnotation(NAMED);
331+
if (named == null) {
332+
return null;
333+
}
334+
return named.value().asString();
335+
}
336+
337+
private static void registerForReflection(RequestHandlerJandexDefinition requestHandlerJandexDefinition,
338+
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
339+
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchies) {
340+
String source = AmazonLambdaProcessor.class.getSimpleName() + " > "
341+
+ requestHandlerJandexDefinition.method().declaringClass() + "[" + requestHandlerJandexDefinition.method()
342+
+ "]";
343+
reflectiveHierarchies.produce(ReflectiveHierarchyBuildItem
344+
.builder(requestHandlerJandexDefinition.inputOutputTypes().inputType())
345+
.source(source)
346+
.build());
347+
reflectiveHierarchies.produce(ReflectiveHierarchyBuildItem
348+
.builder(requestHandlerJandexDefinition.inputOutputTypes().outputType())
349+
.source(source)
350+
.build());
351+
if (requestHandlerJandexDefinition.inputOutputTypes().isCollection()) {
352+
reflectiveMethods.produce(new ReflectiveMethodBuildItem(
353+
"method reflectively accessed in io.quarkus.amazon.lambda.runtime.AmazonLambdaRecorder.discoverHandlerMethod",
354+
requestHandlerJandexDefinition.method()));
355+
reflectiveHierarchies.produce(ReflectiveHierarchyBuildItem
356+
.builder(requestHandlerJandexDefinition.inputOutputTypes().elementType())
357+
.source(source)
358+
.build());
359+
}
360+
}
361+
362+
private static RequestHandlerDefinition toRequestHandlerDefinition(RequestHandlerJandexDefinition jandexDefinition,
361363
RecorderContext context) {
362364
return new RequestHandlerDefinition(
363365
(Class<? extends RequestHandler<?, ?>>) context.classProxy(jandexDefinition.handlerClass().name().toString()),

extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/RequestHandlerJandexUtilTest.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import java.util.List;
1212
import java.util.Set;
1313

14-
import org.jboss.jandex.Index;
14+
import org.jboss.jandex.IndexView;
1515
import org.jboss.jandex.Indexer;
1616
import org.junit.jupiter.api.BeforeAll;
1717
import org.junit.jupiter.api.Test;
@@ -20,16 +20,19 @@
2020
import com.amazonaws.services.lambda.runtime.RequestHandler;
2121

2222
import io.quarkus.amazon.lambda.deployment.RequestHandlerJandexUtil.RequestHandlerJandexDefinition;
23+
import io.quarkus.deployment.index.IndexWrapper;
24+
import io.quarkus.deployment.index.PersistentClassIndex;
2325

2426
public class RequestHandlerJandexUtilTest {
2527

26-
private static Index index;
28+
private static IndexView index;
2729

2830
@BeforeAll
2931
public static void setup() throws IOException {
3032
Indexer indexer = new Indexer();
3133

32-
// Index all the test classes
34+
// Mimick what would happen in a regular Quarkus app:
35+
// forcefully index all test classes, but let the computing index index JDK classes
3336
indexer.indexClass(SimpleStringHandler.class);
3437
indexer.indexClass(SimpleIntegerHandler.class);
3538
indexer.indexClass(BaseHandler.class);
@@ -69,16 +72,8 @@ public static void setup() throws IOException {
6972
indexer.indexClass(GenericListBase.class);
7073
indexer.indexClass(GenericListConcrete.class);
7174

72-
// Index required AWS Lambda classes
73-
indexer.indexClass(RequestHandler.class);
74-
indexer.indexClass(Context.class);
75-
76-
// Index collection classes
77-
indexer.indexClass(List.class);
78-
indexer.indexClass(Set.class);
79-
indexer.indexClass(Collection.class);
80-
81-
index = indexer.complete();
75+
index = new IndexWrapper(indexer.complete(), Thread.currentThread().getContextClassLoader(),
76+
new PersistentClassIndex());
8277
}
8378

8479
private static void indexClass(Indexer indexer, Class<?> clazz) throws IOException {

0 commit comments

Comments
 (0)