Skip to content

Commit 331e766

Browse files
authored
Merge pull request #50739 from gsmet/lambda-handler-resolution
Fix AWS Lambda RequestHandler resolution
2 parents 8e95583 + c979e2c commit 331e766

File tree

6 files changed

+948
-137
lines changed

6 files changed

+948
-137
lines changed

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

Lines changed: 99 additions & 84 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,14 +14,15 @@
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

2320
import com.amazonaws.services.lambda.runtime.RequestHandler;
2421
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
2522

23+
import io.quarkus.amazon.lambda.deployment.RequestHandlerJandexUtil.RequestHandlerJandexDefinition;
2624
import io.quarkus.amazon.lambda.runtime.AmazonLambdaRecorder;
25+
import io.quarkus.amazon.lambda.runtime.AmazonLambdaRecorder.RequestHandlerDefinition;
2726
import io.quarkus.amazon.lambda.runtime.AmazonLambdaStaticRecorder;
2827
import io.quarkus.amazon.lambda.runtime.FunctionError;
2928
import io.quarkus.amazon.lambda.runtime.LambdaBuildTimeConfig;
@@ -44,6 +43,7 @@
4443
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
4544
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
4645
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
46+
import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem;
4747
import io.quarkus.deployment.pkg.steps.NativeBuild;
4848
import io.quarkus.deployment.recording.RecorderContext;
4949
import io.quarkus.runtime.LaunchMode;
@@ -52,8 +52,8 @@
5252
public final class AmazonLambdaProcessor {
5353
public static final String AWS_LAMBDA_EVENTS_ARCHIVE_MARKERS = "com/amazonaws/services/lambda/runtime/events";
5454

55-
private static final DotName REQUEST_HANDLER = DotName.createSimple(RequestHandler.class.getName());
56-
private static final DotName REQUEST_STREAM_HANDLER = DotName.createSimple(RequestStreamHandler.class.getName());
55+
private static final DotName REQUEST_HANDLER = DotName.createSimple(RequestHandler.class);
56+
private static final DotName REQUEST_STREAM_HANDLER = DotName.createSimple(RequestStreamHandler.class);
5757
private static final DotName SKILL_STREAM_HANDLER = DotName.createSimple("com.amazon.ask.SkillStreamHandler");
5858

5959
private static final DotName NAMED = DotName.createSimple(Named.class.getName());
@@ -88,88 +88,51 @@ List<AmazonLambdaBuildItem> discover(CombinedIndexBuildItem combinedIndexBuildIt
8888
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchy,
8989
BuildProducer<ReflectiveClassBuildItem> reflectiveClassBuildItemBuildProducer) throws BuildException {
9090

91-
List<ClassInfo> allKnownImplementors = new ArrayList<>(
92-
combinedIndexBuildItem.getIndex().getAllKnownImplementors(REQUEST_HANDLER)
91+
List<ClassInfo> requestHandlers = new ArrayList<>(
92+
combinedIndexBuildItem.getIndex().getAllKnownImplementations(REQUEST_HANDLER)
9393
.stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
94-
allKnownImplementors.addAll(combinedIndexBuildItem.getIndex()
95-
.getAllKnownImplementors(REQUEST_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
96-
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()
9798
.getAllKnownSubclasses(SKILL_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList());
9899

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

105106
}
106-
AdditionalBeanBuildItem.Builder builder = AdditionalBeanBuildItem.builder().setUnremovable();
107-
List<AmazonLambdaBuildItem> ret = new ArrayList<>();
107+
AdditionalBeanBuildItem.Builder additionalBeansBuilder = AdditionalBeanBuildItem.builder().setUnremovable();
108+
List<AmazonLambdaBuildItem> amazonLambdas = new ArrayList<>();
108109

109-
for (ClassInfo info : allKnownImplementors) {
110-
if (Modifier.isAbstract(info.flags())) {
110+
for (ClassInfo requestHandler : requestHandlers) {
111+
if (requestHandler.isAbstract()) {
111112
continue;
112113
}
113114

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

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

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

175138
@BuildStep
@@ -218,11 +181,14 @@ void processProvidedLambda(Optional<ProvidedAmazonLambdaHandlerBuildItem> provid
218181

219182
@BuildStep
220183
@Record(ExecutionTime.STATIC_INIT)
221-
public void recordStaticInitHandlerClass(List<AmazonLambdaBuildItem> lambdas,
184+
public void recordStaticInitHandlerClass(CombinedIndexBuildItem index,
185+
List<AmazonLambdaBuildItem> lambdas,
222186
LambdaObjectMapperInitializedBuildItem mapper, // ordering!
223187
Optional<ProvidedAmazonLambdaHandlerBuildItem> providedLambda,
224188
AmazonLambdaStaticRecorder recorder,
225-
RecorderContext context) {
189+
RecorderContext context,
190+
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
191+
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchies) {
226192
// can set handler within static initialization if only one handler exists in deployment
227193
if (providedLambda.isPresent()) {
228194
boolean useStreamHandler = false;
@@ -238,10 +204,10 @@ public void recordStaticInitHandlerClass(List<AmazonLambdaBuildItem> lambdas,
238204
.classProxy(providedLambda.get().getHandlerClass().getName());
239205
recorder.setStreamHandlerClass(handlerClass);
240206
} else {
241-
Class<? extends RequestHandler<?, ?>> handlerClass = (Class<? extends RequestHandler<?, ?>>) context
242-
.classProxy(providedLambda.get().getHandlerClass().getName());
243-
244-
recorder.setHandlerClass(handlerClass);
207+
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
208+
.discoverHandlerMethod(providedLambda.get().getHandlerClass().getName(), index.getComputingIndex());
209+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
210+
recorder.setHandlerClass(toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
245211
}
246212
} else if (lambdas != null && lambdas.size() == 1) {
247213
AmazonLambdaBuildItem item = lambdas.get(0);
@@ -251,11 +217,10 @@ public void recordStaticInitHandlerClass(List<AmazonLambdaBuildItem> lambdas,
251217
recorder.setStreamHandlerClass(handlerClass);
252218

253219
} else {
254-
Class<? extends RequestHandler<?, ?>> handlerClass = (Class<? extends RequestHandler<?, ?>>) context
255-
.classProxy(item.getHandlerClass());
256-
257-
recorder.setHandlerClass(handlerClass);
258-
220+
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
221+
.discoverHandlerMethod(item.getHandlerClass(), index.getComputingIndex());
222+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
223+
recorder.setHandlerClass(toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
259224
}
260225
} else if (lambdas == null || lambdas.isEmpty()) {
261226
String errorMessage = "Unable to find handler class, make sure your deployment includes a single "
@@ -276,17 +241,20 @@ public void recordBeanContainer(BeanContainerBuildItem beanContainerBuildItem,
276241

277242
@BuildStep
278243
@Record(ExecutionTime.RUNTIME_INIT)
279-
public void recordHandlerClass(List<AmazonLambdaBuildItem> lambdas,
244+
public void recordHandlerClass(CombinedIndexBuildItem index,
245+
List<AmazonLambdaBuildItem> lambdas,
280246
Optional<ProvidedAmazonLambdaHandlerBuildItem> providedLambda,
281247
BeanContainerBuildItem beanContainerBuildItem,
282248
AmazonLambdaRecorder recorder,
283249
List<ServiceStartBuildItem> orderServicesFirst, // try to order this after service recorders
284-
RecorderContext context) {
250+
RecorderContext context,
251+
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods,
252+
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchies) {
285253
// Have to set lambda class at runtime if there is not a provided lambda or there is more than one lambda in
286254
// deployment
287255
if (!providedLambda.isPresent() && lambdas != null && lambdas.size() > 1) {
288-
List<Class<? extends RequestHandler<?, ?>>> unnamed = new ArrayList<>();
289-
Map<String, Class<? extends RequestHandler<?, ?>>> named = new HashMap<>();
256+
List<RequestHandlerDefinition> unnamed = new ArrayList<>();
257+
Map<String, RequestHandlerDefinition> named = new HashMap<>();
290258

291259
List<Class<? extends RequestStreamHandler>> unnamedStreamHandler = new ArrayList<>();
292260
Map<String, Class<? extends RequestStreamHandler>> namedStreamHandler = new HashMap<>();
@@ -302,9 +270,15 @@ public void recordHandlerClass(List<AmazonLambdaBuildItem> lambdas,
302270
}
303271
} else {
304272
if (i.getName() == null) {
305-
unnamed.add((Class<? extends RequestHandler<?, ?>>) context.classProxy(i.getHandlerClass()));
273+
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
274+
.discoverHandlerMethod(i.getHandlerClass(), index.getComputingIndex());
275+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
276+
unnamed.add(toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
306277
} else {
307-
named.put(i.getName(), (Class<? extends RequestHandler<?, ?>>) context.classProxy(i.getHandlerClass()));
278+
RequestHandlerJandexDefinition requestHandlerJandexDefinition = RequestHandlerJandexUtil
279+
.discoverHandlerMethod(i.getHandlerClass(), index.getComputingIndex());
280+
registerForReflection(requestHandlerJandexDefinition, reflectiveMethods, reflectiveHierarchies);
281+
named.put(i.getName(), toRequestHandlerDefinition(requestHandlerJandexDefinition, context));
308282
}
309283
}
310284
}
@@ -352,4 +326,45 @@ void recordExpectedExceptions(LambdaBuildTimeConfig config,
352326
recorder.setExpectedExceptionClasses(classes);
353327
}
354328

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,
363+
RecorderContext context) {
364+
return new RequestHandlerDefinition(
365+
(Class<? extends RequestHandler<?, ?>>) context.classProxy(jandexDefinition.handlerClass().name().toString()),
366+
context.classProxy(jandexDefinition.method().declaringClass().name().toString()),
367+
context.classProxy(jandexDefinition.inputOutputTypes().inputType().name().toString()),
368+
context.classProxy(jandexDefinition.inputOutputTypes().outputType().name().toString()));
369+
}
355370
}

0 commit comments

Comments
 (0)