serverless-java-container icon indicating copy to clipboard operation
serverless-java-container copied to clipboard

NPE on org.springframework.boot.SpringApplication.getMainApplicationClass() with AOT

Open andreaabram-eutelsat opened this issue 2 months ago • 3 comments

Hi! I'm trying to implement AOT into a simple java lambda

I'm using the com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler handler

Dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>3.5.7</version>
</dependency>
<dependency>
    <groupId>com.amazonaws.serverless</groupId>
    <artifactId>aws-serverless-java-container-springboot3</artifactId>
    <version>2.1.5</version>
</dependency>

Logs:

Picked up JAVA_TOOL_OPTIONS: -Dspring.aot.enabled=true
Picked up _JAVA_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=*:5005 -XX:MaxHeapSize=2834432k -XX:+UseSerialGC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Djava.net.preferIPv4Stack=true
11:25:50.498 [main] INFO org.springframework.cloud.function.serverless.web.FunctionClassUtils -- Main class: class org.example.IssueApplication
11:25:50.520 [main] INFO com.amazonaws.serverless.proxy.AsyncInitializationWrapper -- Async initialization will wait for 5775ms (init grace time is configured to 150)
11:25:50.522 [Thread-0] INFO com.amazonaws.serverless.proxy.AsyncInitializationWrapper -- Starting async initializer
11:25:50.550 [Thread-1] INFO org.springframework.cloud.function.serverless.web.ServerlessMVC -- Starting application with the following configuration classes:
11:25:50.551 [Thread-1] INFO org.springframework.cloud.function.serverless.web.ServerlessMVC -- IssueApplication
11:28:37.898 [Thread-1] ERROR org.springframework.boot.SpringApplication -- Application run failed
java.lang.NullPointerException: Cannot invoke "java.lang.Class.getName()" because the return value of "org.springframework.boot.SpringApplication.getMainApplicationClass()" is null
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.addAotGeneratedEnvironmentPostProcessorIfNecessary(EnvironmentPostProcessorApplicationListener.java:160)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:130)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:115)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
        at java.base/java.lang.Iterable.forEach(Unknown Source)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:353)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
        at org.springframework.cloud.function.serverless.web.ServerlessMVC.initContext(ServerlessMVC.java:125)
        at org.springframework.cloud.function.serverless.web.ServerlessMVC.lambda$initializeContextAsync$1(ServerlessMVC.java:112)
        at java.base/java.lang.Thread.run(Unknown Source)
11:28:37.900 [Thread-1] INFO org.springframework.cloud.function.serverless.web.ServerlessMVC -- Application is started successfully.
Exception in thread "Thread-1" java.lang.IllegalStateException: java.lang.NullPointerException: Cannot invoke "java.lang.Class.getName()" because the return value of "org.springframework.boot.SpringApplication.getMainApplicationClass()" is null
        at org.springframework.cloud.function.serverless.web.ServerlessMVC.lambda$initializeContextAsync$1(ServerlessMVC.java:115)
        at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Class.getName()" because the return value of "org.springframework.boot.SpringApplication.getMainApplicationClass()" is null
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.addAotGeneratedEnvironmentPostProcessorIfNecessary(EnvironmentPostProcessorApplicationListener.java:160)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:130)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:115)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
        at java.base/java.lang.Iterable.forEach(Unknown Source)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:353)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
        at org.springframework.cloud.function.serverless.web.ServerlessMVC.initContext(ServerlessMVC.java:125)
        at org.springframework.cloud.function.serverless.web.ServerlessMVC.lambda$initializeContextAsync$1(ServerlessMVC.java:112)
        ... 1 more
Cannot invoke "org.springframework.web.servlet.DispatcherServlet.getServletContext()" because "this.dispatcher" is null: java.lang.NullPointerException
java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.DispatcherServlet.getServletContext()" because "this.dispatcher" is null
        at org.springframework.cloud.function.serverless.web.ServerlessMVC.getServletContext(ServerlessMVC.java:138)
        at com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler.handleRequest(SpringDelegatingLambdaContainerHandler.java:68)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)

END RequestId: 8baa5a53-ca92-4fd0-bc7e-b3720868d264
REPORT RequestId: 8baa5a53-ca92-4fd0-bc7e-b3720868d264  Init Duration: 0.02 ms  Duration: 171628.48 ms  Billed Duration: 171629 ms      Memory Size: 4096 MB    Max Memory Used: 4096 MB

andreaabram-eutelsat avatar Nov 11 '25 11:11 andreaabram-eutelsat

I think that the issue is in the async initialization logic of the SpringDelegatingLambdaContainerHandler

Here:

https://github.com/aws/serverless-java-container/blob/4696691989cfcd5d8957dbdfc280018aa267c8ba/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java#L52-L63

Also here:

https://github.com/aws/serverless-java-container/blob/4696691989cfcd5d8957dbdfc280018aa267c8ba/aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/SpringDelegatingLambdaContainerHandler.java#L73-L90

Spring Code:

The deduceMainApplicationClass method use the StackWalker to find, in the stacktrace, the first method that has a static main This method doesn't exists in the different thread Image

andreaabram-eutelsat avatar Nov 11 '25 11:11 andreaabram-eutelsat

Update

Creating a custom lambda handler instead of using the SpringDelegatingLambdaContainerHandler "bypass" this issue but creates another

Handler:

public class StreamLambdaHandler implements RequestStreamHandler {

    private static final Logger logger = LoggerFactory.getLogger(StreamLambdaHandler.class);

    private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;

    static {
        try {
            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(IssueApplication.class);
        } catch (ContainerInitializationException e) {
            // if we fail here. We re-throw the exception to force another cold start
            e.printStackTrace();
            throw new RuntimeException("Could not initialize Spring Boot application", e);
        }
    }

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
        throws IOException {
        logger.info("entered generic stream lambda handler");
        handler.proxyStream(inputStream, outputStream, context);
    }
}

Issue:

Description:

Startup with AOT mode enabled failed: AOT initializer com.amazonaws.services.lambda.runtime.api.client.AWSLambda__ApplicationContextInitializer could not be found

Action:

Consider the following:
        Did you build the application with enabled AOT processing?
        Is the main class com.amazonaws.services.lambda.runtime.api.client.AWSLambda correct?
        If you want to run the application in regular mode, remove the system property 'spring.aot.enabled'

Error loading class org.example.StreamLambdaHandler: java.lang.ExceptionInInitializerError
java.lang.ExceptionInInitializerError
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Unknown Source)
        at java.base/java.lang.Class.forName(Unknown Source)
Caused by: org.springframework.boot.AotInitializerNotFoundException: Startup with AOT mode enabled failed: AOT initializer com.amazonaws.services.lambda.runtime.api.client.AWSLambda__ApplicationContextInitializer could not be found
        at org.springframework.boot.SpringApplication.addAotGeneratedInitializerIfNecessary(SpringApplication.java:426)
        at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:382)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:317)
        at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149)
        at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.initialize(SpringBootLambdaContainerHandler.java:198)
        at com.amazonaws.serverless.proxy.InitializationWrapper.start(InitializationWrapper.java:35)
        at com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder.buildAndInitialize(SpringBootProxyHandlerBuilder.java:80)
        at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.getAwsProxyHandler(SpringBootLambdaContainerHandler.java:96)
        at org.example.StreamLambdaHandler.<clinit>(StreamLambdaHandler.java:23)
        ... 3 more

It this case the deduceMainApplicationClass finds the AWS Runtime Interface Client class: com.amazonaws.services.lambda.runtime.api.client.AWSLambda so AOT looks for the wrong *__ApplicationContextInitializer

I created another issue about this specific problem: #1580

andreaabram-eutelsat avatar Nov 11 '25 13:11 andreaabram-eutelsat

This is a blocking issue for me, too.

xav73400 avatar Nov 18 '25 08:11 xav73400