BAEL-6255 - Run a Spring Boot application in AWS Lambda (#13629)
* BAEL-6255 - Run a Spring Boot application in AWS Lambda * BAEL-6255 - Run a Spring Boot application in AWS Lambda * fix on template.yaml * fix on template.yaml * removed log from test * resolved issues reported on PR --------- Co-authored-by: Cesare <cesare.valenti@hotmail.com>
This commit is contained in:
parent
b877f01511
commit
6a2d723155
|
@ -0,0 +1 @@
|
|||
### Relevant Articles:
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>spring-boot-aws</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>spring-boot-data-3</name>
|
||||
<description>spring-boot-boot</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.spring-boot-modules</groupId>
|
||||
<artifactId>spring-boot-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<springboot2.aws.version>1.9.1</springboot2.aws.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws.serverless</groupId>
|
||||
<artifactId>aws-serverless-java-container-springboot2</artifactId>
|
||||
<version>${springboot2.aws.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.apache.tomcat.embed:*</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.aws;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.aws.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baeldung.aws.data.User;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/")
|
||||
public class ProfileController {
|
||||
|
||||
@GetMapping(value = "users", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public List<User> getUser() {
|
||||
return List.of(new User("John", "Doe", "john.doe@baeldung.com"), new User("John", "Doe", "john.doe-2@baeldung.com"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.aws.data;
|
||||
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
private String surname;
|
||||
private String emailAddress;
|
||||
|
||||
public User(String name, String surname, String emailAddress) {
|
||||
this.name = name;
|
||||
this.surname = surname;
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
public void setSurname(String surname) {
|
||||
this.surname = surname;
|
||||
}
|
||||
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public void setEmailAddress(String emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.baeldung.aws.handler;
|
||||
|
||||
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
|
||||
import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
|
||||
import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder;
|
||||
import com.amazonaws.services.lambda.runtime.Context;
|
||||
import com.amazonaws.services.lambda.runtime.RequestHandler;
|
||||
import com.baeldung.aws.Application;
|
||||
|
||||
public class AsynchronousLambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
|
||||
private SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
|
||||
|
||||
public AsynchronousLambdaHandler() throws ContainerInitializationException {
|
||||
handler = (SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse>) new SpringBootProxyHandlerBuilder().springBootApplication(Application.class)
|
||||
.asyncInit()
|
||||
.buildAndInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwsProxyResponse handleRequest(AwsProxyRequest input, Context context) {
|
||||
return handler.proxy(input, context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.aws.handler;
|
||||
|
||||
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
|
||||
import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest;
|
||||
import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
|
||||
import com.amazonaws.services.lambda.runtime.Context;
|
||||
import com.amazonaws.services.lambda.runtime.RequestHandler;
|
||||
import com.baeldung.aws.Application;
|
||||
|
||||
public class HttpV2LambdaHandler implements RequestHandler<HttpApiV2ProxyRequest, AwsProxyResponse> {
|
||||
private static SpringBootLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse> handler;
|
||||
|
||||
static {
|
||||
try {
|
||||
handler = SpringBootLambdaContainerHandler.getHttpApiV2ProxyHandler(Application.class);
|
||||
} catch (ContainerInitializationException ex) {
|
||||
throw new RuntimeException("Unable to load spring boot application", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwsProxyResponse handleRequest(HttpApiV2ProxyRequest input, Context context) {
|
||||
return handler.proxy(input, context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.aws.handler;
|
||||
|
||||
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
|
||||
import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
|
||||
import com.amazonaws.services.lambda.runtime.Context;
|
||||
import com.amazonaws.services.lambda.runtime.RequestHandler;
|
||||
import com.baeldung.aws.Application;
|
||||
|
||||
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
|
||||
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
|
||||
|
||||
static {
|
||||
try {
|
||||
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
|
||||
} catch (ContainerInitializationException ex) {
|
||||
throw new RuntimeException("Unable to load spring boot application", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwsProxyResponse handleRequest(AwsProxyRequest input, Context context) {
|
||||
return handler.proxy(input, context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
|
||||
spring.datasource.username=myusername
|
||||
spring.datasource.password=mypassword
|
|
@ -0,0 +1,36 @@
|
|||
package com.baeldung;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
|
||||
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
|
||||
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
|
||||
import com.baeldung.aws.handler.LambdaHandler;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ProfileIntegrationTest {
|
||||
|
||||
MockLambdaContext lambdaContext = new MockLambdaContext();
|
||||
|
||||
@Test
|
||||
void whenTheUsersPathIsInvokedViaLambda_thenShouldReturnAList() throws IOException {
|
||||
LambdaHandler lambdaHandler = new LambdaHandler();
|
||||
AwsProxyRequest req = new AwsProxyRequestBuilder("/api/v1/users", "GET").build();
|
||||
AwsProxyResponse resp = lambdaHandler.handleRequest(req, lambdaContext);
|
||||
Assertions.assertNotNull(resp.getBody());
|
||||
Assertions.assertEquals(200, resp.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenWrongPathPathIsInvokedViaLambda_thenShouldNotFound() throws IOException {
|
||||
LambdaHandler lambdaHandler = new LambdaHandler();
|
||||
AwsProxyRequest req = new AwsProxyRequestBuilder("/api/v1/users/plus-one-level", "GET").build();
|
||||
AwsProxyResponse resp = lambdaHandler.handleRequest(req, lambdaContext);
|
||||
Assertions.assertEquals(404, resp.getStatusCode());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
AWSTemplateFormatVersion: '2010-09-09'
|
||||
Transform: AWS::Serverless-2016-10-31
|
||||
|
||||
Globals:
|
||||
Function:
|
||||
Timeout: 30
|
||||
|
||||
Resources:
|
||||
ProfileApiFunction:
|
||||
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
|
||||
Properties:
|
||||
CodeUri: .
|
||||
Handler: com.baeldung.aws.handler.LambdaHandler::handleRequest
|
||||
Runtime: java11
|
||||
AutoPublishAlias: production
|
||||
SnapStart:
|
||||
ApplyOn: PublishedVersions
|
||||
Architectures:
|
||||
- x86_64
|
||||
MemorySize: 2048
|
||||
Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
|
||||
Variables:
|
||||
JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 # More info about tiered compilation https://aws.amazon.com/blogs/compute/optimizing-aws-lambda-function-performance-for-java/
|
||||
Policies:
|
||||
- AWSSecretsManagerGetSecretValuePolicy:
|
||||
SecretArn: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${DatabaseSecretName}
|
||||
Events:
|
||||
HelloWorld:
|
||||
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
|
||||
Properties:
|
||||
Path: /{proxy+}
|
||||
Method: ANY
|
Loading…
Reference in New Issue