Search code examples
javaaws-lambdajava-native-interfacealexa-skills-kitquarkus

Native Quarkus with AWS lambda does not build


I am trying to use Quarkus native to build my AWS Lambda.

My setup is:

  • GraalVM 19.3.0
  • Java 11
  • Ubuntu

When I run

docker run -v /home/mypc/dev/java/quarkus/alexa_swear/target/<my project>-1.0-SNAPSHOT-native-image-source-jar:/project:z --user 1000:1000 --rm quay.io/quarkus/ubi-quarkus-native-image:19.2.1 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime -jar <my project>-1.0-SNAPSHOT-runner.jar -J-Djava.util.concurrent.ForkJoinPool.common.parallelism=1 -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:+AddAllCharsets -H:EnableURLProtocols=http -H:-JNI --no-server -H:-UseServiceLoaderFeature -H:+StackTrace <my project>-1.0-SNAPSHOT-runner

I get the following error:

[alexa_swear-1.0-SNAPSHOT-runner:23]   (typeflow):  52,070.99 ms
[alexa_swear-1.0-SNAPSHOT-runner:23]    (objects):  25,961.57 ms
[alexa_swear-1.0-SNAPSHOT-runner:23]   (features):     803.41 ms
[alexa_swear-1.0-SNAPSHOT-runner:23]     analysis:  81,015.48 ms
[alexa_swear-1.0-SNAPSHOT-runner:23]     (clinit):   1,277.52 ms
[alexa_swear-1.0-SNAPSHOT-runner:23]     universe:   4,416.32 ms
Error: Unsupported features in 5 methods
Detailed message:
Call path from entry point to java.lang.Runtime.traceInstructions(boolean): 
    at java.lang.Runtime.traceInstructions(Runtime.java)
    at com.oracle.svm.reflect.Runtime_traceInstructions_91eaacf084b9d7e2af6dcc0028ee87fea9223b51_77.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.net.www.protocol.http.NTLMAuthenticationProxy.isTrustedSite(NTLMAuthenticationProxy.java:102)
    at sun.net.www.protocol.http.HttpURLConnection.getServerAuthentication(HttpURLConnection.java:2481)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1743)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
    at io.quarkus.amazon.lambda.runtime.AmazonLambdaRecorder$2.run(AmazonLambdaRecorder.java:171)
    at java.lang.Thread.run(Thread.java:748)
    at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:460)
    at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
    at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0) ... 6 more
Error: Image build request failed with exit status 1

The above error is truncated: the same call stack points to different unsupported methods, such as java.lang.Thread.stop.

My basic understanding is thatio.quarkus.amazon.lambda.runtime.AmazonLambdaRecorder$2.run(AmazonLambdaRecorder.java is referencing to some unsupported methods, such as java.lang.Thread.resume(). I have also tried with Quarkus 19.2.1 unsuccessfully.

The above command was executed by mvn clean install -Pnative -Dnative-image.docker-build=true -e.


Solution

  • Finally I have found the cause of my issue.

    In the non-working version of my code, I use a factory of com.amazon.ask.AlexaSkill that is somehow injected in the entry point, as follows:

    package io.mirko.lambda;
    
    import com.amazon.ask.AlexaSkill;
    import com.amazon.ask.Skills;
    import com.amazon.ask.dispatcher.request.handler.HandlerInput;
    import com.amazon.ask.dispatcher.request.handler.RequestHandler;
    import com.amazon.ask.model.RequestEnvelope;
    import com.amazon.ask.model.ResponseEnvelope;
    import com.amazon.ask.request.interceptor.GenericRequestInterceptor;
    import io.mirko.lambda.handlers.*;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.enterprise.inject.Instance;
    import javax.enterprise.inject.Produces;
    import javax.inject.Inject;
    import javax.inject.Named;
    import java.util.*;
    import java.util.stream.StreamSupport;
    
    
    public class SkillFactory {
        @Inject
        Instance<RequestHandler> handlers;
    
        @Produces
        @ApplicationScoped
        @Named
        public AlexaSkill<RequestEnvelope, ResponseEnvelope> createSkill() {
            return Skills.standard()
                    .addRequestHandlers(handlers.stream().toArray(RequestHandler[]::new))
                    .addRequestInterceptor(new GenericRequestInterceptor<HandlerInput>() {
                        @Override
                        public void process(HandlerInput handlerInput) {
                            System.out.format("Processing %s\n", handlerInput.getRequest());
                        }
                    })
                    // Add your skill id below
                    //.withSkillId("")
                    .build();
        }
    }
    
    ...
    
    public class SwearStreamLambda extends SkillStreamHandler {
        @Named("swearStream")
            public SwearStreamLambda() {
                //noinspection unchecked
                super((AlexaSkill<RequestEnvelope, ResponseEnvelope>)
                    getBean(new ParameterizedTypeImpl(AlexaSkill.class, RequestEnvelope.class, ResponseEnvelope.class)));
    
    

    By removing the SkillFactory class and moving its logic inside SwearStreamLambda class the compilation went well.

    Some notes:

    1. The issue is not related to CDI, as it is heavily used throughout the project
    2. The issue is not related to @Produces annotation, which is present in other parts of the project
    3. The issue is not related to javax.enterprise.inject.Instance, as its removal does not solve the issue

    All in all, I could not find the root cause of the problem, but I consider my issue solved.

    P.S. The original problem is eradicated by building with the following:

    mvn clean install -Pnative -Dnative-image.docker-build=true -Dquarkus.native.enable-jni=true

    Please see https://github.com/quarkusio/quarkus/issues/6395#issuecomment-570755587.

    This does not solve all the problems, as Quarkus reflection has to be configured, but it solves the specific issue.