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
1
spring-boot-modules/spring-boot-aws/README.md
Normal file
1
spring-boot-modules/spring-boot-aws/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
### Relevant Articles:
|
77
spring-boot-modules/spring-boot-aws/pom.xml
Normal file
77
spring-boot-modules/spring-boot-aws/pom.xml
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
32
spring-boot-modules/spring-boot-aws/template.yaml
Normal file
32
spring-boot-modules/spring-boot-aws/template.yaml
Normal file
@ -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…
x
Reference in New Issue
Block a user