In summary: Sometimes my AWS Lambda handler built with Quarkus Funqy is not invoked and Quarkus seem to return some default 404 HTML, both when running locally with Quarkus Dev Mode and deploying native executable to AWS Lambda. I ask for assistance in fixing this behavior. Below I define how the application works and how it is configured.
The purpose of this lambda is registering and fetching a specific entity (POST and GET operations). I intend to build native executable.
@ApplicationScoped
public class DeviceController {
private static final Logger log = LoggerFactory.getLogger(DeviceController.class);
private final DeviceService deviceService;
private final ObjectMapper objectMapper;
@Inject
public DeviceController(DeviceService deviceService) {
this.deviceService = deviceService;
this.objectMapper = new ObjectMapper().registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
}
@Funq
public Map<String, Object> handler(Map<String, Object> apiRequestEvent) {
try {
String path = (String) apiRequestEvent.get("path");
String httpMethod = (String) apiRequestEvent.get("httpMethod");
String body = (String) apiRequestEvent.get("body");
Map<String, String> queryStringParameters = readQueryParameters(apiRequestEvent);
if (!path.equals("/device")) {
throw new ServiceException(String.format("Path %s does not exist", path)
, GlobalExceptionCode.INVALID_PATH);
}
return switch (httpMethod) {
case "POST"-> this.createDevice(body);
case "GET"-> this.getDevice(queryStringParameters);
default -> throw new ServiceException("Invalid method", GlobalExceptionCode.INVALID_METHOD);
};
} catch (ServiceException ex ){
return handleServiceException(ex);
} catch ( ModelException ex){
return handleModelException(ex);
} catch (RuntimeException ex){
return handleRuntimeException(ex);
} catch (Exception e) {
return handleGenericException(e);
}
}
The handler layer is nothing fancy: I can highlight the handler receives a Map<String,Object> for he API Gateway request event (with body, queryStringParameters, httpMethod etc), and returns another Map for the API Gateway response event. It uses dependency injection of a service layer to handle business logic. There's auxiliary methods to parse the input map and to build the output map. I'll attach pom.xml and application.properties at the end.
The handler code mostly fine when debugging locally on Quarkus Dev mode, and also works when building a native executable and deploying to AWS Lambda. However, there's this weird behavior where sometimes the handler is not invoked (does not even trigger debugger breakpoint on the first line), and calling the handler returns a 404.
Here's the lambda running succesfully in quarkus dev mode, calling from Postman:
Here's the intermittent error when running locally:
Here's the intermittent error on AWS Lambda:
Here's application.properties, I don't think there's relevant stuff in here. Today I tried defining the method 'handler' as the Funqy handler, but it changed nothing as I only have one handler annotated with @Funq. There's also configuration for the native build which don't affect the application running locally on Quarkus dev mode.
quarkus.funqy.export=handler
lambda.bastion.write.arn=${BASTION_WRITE_ARN}
lambda.bastion.read.arn=${BASTION_READ_ARN}
quarkus.amazon-services-lambda.sync-client.type=url-connection
quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:23.1-jdk-21
quarkus.native.container-build=true
quarkus.native.container-runtime=docker
quarkus.native.additional-build-args=-march=compatibility
Here's pom.xml. I can highlight the usage of the AWS SDK since I need to call another lambda for database access, MapStruct for DTO mapping and Apache commons commons-lang3 for some legacy utility code I've reused from another company application.
(Omitted project definitions and properties...)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.30.37</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.quarkiverse.amazonservices</groupId>
<artifactId>quarkus-amazon-services-bom</artifactId>
<version>3.3.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkiverse.amazonservices</groupId>
<artifactId>quarkus-amazon-lambda</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-funqy-amazon-lambda</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-lambda-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-lambda</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.apachemons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>21</source>
<target>21</target>
<annotationProcessorPaths>
<path>
<groupId>.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.native.enabled>true</quarkus.native.enabled>
</properties>
</profile>
</profiles>
</project>