Logo
Published on

Spring Boot EventBridge

Authors
  • Name
    Twitter

This article demonstrates a flow that triggers an event to AWS EventBridge when an object is created or deleted on S3. EventBridge then sends the event to a Java based SpringBoot Lambda that sits behind an API Gateway. EventBridge also sends an email notification to SNS that is registered to an email.

We will use Serverless Application Model (SAM) for building and deploying our Java Lambda. In addition, we will create an API Gateway and when uploading to S3, will trigger event bridge which will send an email as well as invoke our Lambda via the API Gateway.

For development we will see how we can test the lambda by running local either as a SpringBoot application or via SAM.

Lambda

Github repository for this article can be found here.

This is a spring boot application that implements two simple REST API endpoints. One GET and one PUT.

Prerequisites

  • Maven
  • Java 11
  • AWS CLI
  • SAM CLI
  • AWS Access

SpringBoot Lambda

In this article we are using a standard SpringBoot application that can be run locally. With the addition of a Lambda handler and SAM we can easily convert it to work serverless.

This is enabled with the following additions to the project dependencies. The key here is aws-serverless-java-container-springboot3.

<properties>
<springboot3.aws.version>2.0.0-M1</springboot3.aws.version>
<java.version>17</java.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-springboot3</artifactId>
<version>${springboot3.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>

Our handler code looks like this, where are handleRequest method will receive incoming lambda invocations and proxy to our standard SpringBoot code.

public  class  StreamLambdaHandler  implements  RequestStreamHandler {
private  static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;

static {
try {
LambdaContainerHandler.getContainerConfig().setInitializationTimeout(30_000);
long  startTime  = Instant.now().toEpochMilli();
// handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
// we use the SpringBootProxyHandlerBuilder to init Spring Boot as a proxy to the underlying container
handler = new  SpringBootProxyHandlerBuilder<AwsProxyRequest>()
.defaultProxy()
.asyncInit(startTime)
.springBootApplication(Application.class)
.buildAndInitialize();
} 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 framework", e);
}
}

@Override
public  void  handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
LambdaLogger  logger  = context.getLogger();
logger.log("Processing incoming request in " + context.getFunctionName());
handler.proxyStream(inputStream, outputStream, context);
}
}

Local Application Testing

It is a maven project and can be built locally:

mvn clean install

And run locally:

java -jar target/ev-java-lambda-0.0.1-SNAPSHOT.jar

Local running application

and tested locally as a normal Spring Boot application.

curl [http://localhost:8080/api/v1/case/](http://localhost:8080/api/v1/case/)

returning the following hard-coded values.

[
{
"id":  "123456",
"value":  "New Sensor Value 1",
"status":  "OPEN"
},
{
"id":  "654321",
"value":  "New Sensor Value 1",
"status":  "PENDING"
}
]

Local Serverless Testing

The sample application can also be tested locally using SAM.

Make sure you have AWS access keys configured for your AWS instance. I have several so with SAM you can use the — — profile flag.

SAM uses a template for the serverless definition. Ours is shown below.

template.yaml

AWSTemplateFormatVersion:  '2010-09-09'
Transform:  AWS::Serverless-2016-10-31

Globals:
Function:
Timeout:  30

Resources:
SensorApiFunction:
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.brianeno.aws.handler.StreamLambdaHandler::handleRequest
Runtime:  java17
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/
Events:
NewSensorEvent:
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:  /api/v1/sensor/{key}
Method:  PUT
GetSensorEvent:
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:  /api/v1/sensor
Method:  GET

First, validate you template file by running this.

sam validate

Then can build using SAM.

sam build

Testing locally in serverless mode using.

sam local  start-api

Finally once tested we can deploy to AWS

sam deploy --guided

The first time deploy is run, you will need to enter the function name etc. and then the config will be stored by default in a file called samconfig.toml.

SAM Deployment Options

Can then verify the Lambda is deployed in the AWS Console.

In addition, the deployment creates an API Gateway that sits in front of the Lambda.

Within the API Gateway you can go to the Stages menu on the left navigation panel and copy the URL and test it, for instance view POSTMAN (this calls the API Gateway which invokes the Lambda.

S3

An S3 bucket was created and EventBridge notifications were turned on for the bucket. To do this, once the bucket is created, go to Properties and scroll down to Amazon EventBridge.

Notifications

For our email notifications an SNS topic is created and a subscription with email is created. When the subscription is created, a confirmation email will be sent to the address and this must be acknowledged before it will work.

Create Subscription

Email Confirmation

Event Bridge

Within event bridge, since we using AWS (and not application custom) the Default EventBus will be used.

So first create a rule under default:

Enter rule for EventBridge — notice default event bus

And the source trigger (this only works when EventBridge notifications are turned on for the S3 bucket).

Rule Configuration Part 1

Rule Configuration Part 1

The JSON is generated as you select the different options and enter the bucket, but included here also.

{
  "source": ["aws.s3"],
  "detail-type": ["Object Created", "Object Deleted"],
  "detail": {
    "bucket": {
      "name": ["ev-sensor-input"]
    }
  }
}

Next we need to define the targets. For our example we will have two targets configured.

The first for notifications to be sent via out SNS Topic we created.

Event Bridge Target — SNS

The second to invoke the lambda via the API Gateway.

Notice the path parameter is replaced with the value from the input event (which is the object key in S3) via

$.detail.object.key

Test It!

To test it we will upload any file (preferrable small for this test) to our S3 bucket. As we configured, if an object is deleted or uploaded to S3 and email will be sent and the Lambda invocation with the key can be verified in Cloudwatch. Here we see the lambda was invoked when I uploaded a file to S3.

Cloudwatch Lambda

We also got an email notification which was our first Event Bridge Target.

When deleting our object from S3 I got a second notification since we configured deletes also.

Technology Used

EventBridge

EventBridge is a serverless AWS service that uses the concept of events to connect applications and services. It can ingest, filter, transform and deliver events.

The main concepts of AWS EventBridge are:

  • Events: An event is a notification that something has happened in your AWS environment. Events can be generated by AWS services, applications, and IoT devices.
  • Rules: A rule is a statement that defines how to process an event. Rules can be used to route events to specific AWS services, trigger Lambda functions, or write data to Amazon S3 buckets.
  • Targets: A target is the destination of an event. Targets can be AWS services, Lambda functions, or Amazon S3 buckets.

SNS

AWS API Gateway

Java Lambda

For this POC Java was used for Lambda development. Historically Java was not looked favorably as an alternative for serverless development on AWS due to the nature of serverless apps and the extended startup times. However recent developments on AWS and technologies have removed much of the concern.

See

https://aws.amazon.com/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/

and

Event-Driven Application using AWS EventBridge, Lambda and S3 in Java — Part 2

Note this uses both Java 11 and the springboot2 wrapper Java 17 and SpringBoot 3.

In the branch springboot2 uses Java 11 and SpringBoot 2.

In main use springboot3 container wrapper and using Java 17 and Spring Boot 3.

The Lambda is implemented using Spring Boot and uses a wrapper to allow the Spring application to run as a serverless application. This is done through the addition of this dependency:

In addition, the application was developed using SAM — Serverless Application Model. This provided for ease of building and deploying.

  • External Partners/External Gateway Communicate With Gateway
  • Gateway Interface Processes EDI and MDN to/from Gateway to S3
  • EventBridge S3 Source Trigger is raised
  • EventBridge Lambda Target Trigger is raised
  • EDI Processor processes EDI/MDN
  • Notifications are sent by components to Notification Microservice.

As always here is the code.

Enjoy the journey!