11package io .quarkus .amazon .lambda .deployment ;
22
33import java .lang .reflect .Method ;
4- import java .lang .reflect .Modifier ;
54import java .util .ArrayList ;
6- import java .util .Collections ;
75import java .util .HashMap ;
86import java .util .List ;
97import java .util .Map ;
1614import org .jboss .jandex .AnnotationInstance ;
1715import org .jboss .jandex .ClassInfo ;
1816import org .jboss .jandex .DotName ;
19- import org .jboss .jandex .MethodInfo ;
2017import org .jboss .logging .Logger ;
2118import org .joda .time .DateTime ;
2219
2320import com .amazonaws .services .lambda .runtime .RequestHandler ;
2421import com .amazonaws .services .lambda .runtime .RequestStreamHandler ;
2522
23+ import io .quarkus .amazon .lambda .deployment .RequestHandlerJandexUtil .RequestHandlerJandexDefinition ;
2624import io .quarkus .amazon .lambda .runtime .AmazonLambdaRecorder ;
25+ import io .quarkus .amazon .lambda .runtime .AmazonLambdaRecorder .RequestHandlerDefinition ;
2726import io .quarkus .amazon .lambda .runtime .AmazonLambdaStaticRecorder ;
2827import io .quarkus .amazon .lambda .runtime .FunctionError ;
2928import io .quarkus .amazon .lambda .runtime .LambdaBuildTimeConfig ;
4443import io .quarkus .deployment .builditem .ShutdownContextBuildItem ;
4544import io .quarkus .deployment .builditem .nativeimage .ReflectiveClassBuildItem ;
4645import io .quarkus .deployment .builditem .nativeimage .ReflectiveHierarchyBuildItem ;
46+ import io .quarkus .deployment .builditem .nativeimage .ReflectiveMethodBuildItem ;
4747import io .quarkus .deployment .pkg .steps .NativeBuild ;
4848import io .quarkus .deployment .recording .RecorderContext ;
4949import io .quarkus .runtime .LaunchMode ;
5252public 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