AWS for Java Developers Part 4
AWS Lambda
- AWS Lambda is a serverless and event-driven compute service. It allows you to upload a piece of source code to execute against a valid event. The uploaded piece of code is called a Lambda function.
-
AWS Lambda functions includes source code along with all dependencies.
-
Each Lambda function has its own configuration informations, such as runtime, environment variables,
handler, IAM role, tag(s), memory, timeout, VPC, and many other details that are defined at the time of creating.
-
Lambda function can be configured to execute in between 1 to 900 seconds. Lambda function execution time is called timeout. If the Lambda function is running after the defined timeout, it is automatically terminated.
While creating a Lambda function along with memory, here are few more parameters that need to be defined:
-
Maximum execution time (timeout):
The maximum it can be 15 minutes. It helps to prevent the Lambda function from running indefinitely. When timeout has been reached, the Lambda function execution terminates.
-
IAM role (execution role):
Lambda can assume an IAM role at the time of execution. Based on the privileges granted to the IAM role, the Lambda function can inherit the privileges for executing the function.
-
Handler name :
It refers to the method name to be used by AWS Lambda to start the execution. AWS Lambda passes an event information that triggers the invocation as a parameter to the handler method.
Lambda Function Invocation Types
-
AWS Lambda supports two invocation methods: synchronous and asynchronous.
-
The invocation type can be only specified at the time of manually executing a Lambda function.
-
This Lambda function execution is called on-demand invocation.
-
On-demand invocation is done by the invoke operation. It allows you to specify the invocation type, synchronous or asynchronous.
Writing a Lambda Function
- AWS lambda supports Node.js, Java, Python and C#
-
Irrespective of the programming language used to write the AWS Lambda function there is a common pattern to write a code for a Lambda function. It includes the following concept.
-
Handler
-
The Context Object
-
Logging
-
Lambda function Handler
- The general syntax of handler function is as follows :
outputType handler-name(inputType input, Context context) {
...}
- Here is a list of components that are part of the syntax :
- inputType : This can be an event data or custom input that is provided as a string or any custom data object. To successfully invoke this handler, the Lambda function must be invoked with the input data that can be serialized into thedefined data type of the input parameter.
- outputType : When the Lambda function is invoked synchronously using the RequestResponse invocation type, it is possible to return the output of the Lambda function using the valid and supported data type.
inputType and outputType can be one of the following:
-
Predefined AWS event types: They are defined in the aws-lambda-java-events library.
-
Plain Old Java Object (POJO) class: It allows to create own POJO class. Lambda function automatically serializes, deserializes input, and output on the POJO type
or JSON.
-
Primitive Java types: It supports primitive Java types such as String or int.
Simple Lambda
build.gradle
plugins {
id 'java'
}
group 'com.lambda.demo'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
dependencies {
compile group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.0'
compile 'com.amazonaws:aws-lambda-java-events:2.2.6'
compile group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: '1.11.659'
compile group: 'com.amazonaws', name: 'aws-java-sdk-dynamodb', version: '1.11.659'
compile group: 'com.amazonaws', name: 'aws-java-sdk-lambda', version: '1.11.118'
}
package com.lambda.demo;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.Map;
public class LambdaRequestHandler implements RequestHandler<Map<String,Object>,String> {
@Override
public String handleRequest(Map<String, Object> input, Context context) {
return "Hello" + input.get("name");
}
}
Java Code

Go to AWS lambda service and click on Create Function
Now Provide lambda configuration

- Select author from scratch option
- Provide the function name
- Provide Java 8 for Runtime

Select Create a new role with basic Lambda Permission in Execution role and Now click on Create Function

Please make sure that following Policies are attached in your Role assigned to lambda


Click on Upload button and Upload the Jar of the Java file which we have created earlier and in the handler specify package with Class and method name in the format specified

Select the Execution Role and in basic Setting provide CPU proportion and Time out
Select the VPC settings and Click on Test Button to provide the test case


Specify the event name and Parameter for testing and click on Create button

Now hit the Test button and see you lambda executing
Invoking lambda function from Java Code
package com.lambda.demo;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.model.InvokeRequest;
import com.amazonaws.services.lambda.model.InvokeResult;
import java.io.UnsupportedEncodingException;
public class InvokingLambdaFunctions {
public static void main(String[] args) throws UnsupportedEncodingException {
AWSLambda client = AWSLambdaClientBuilder
.standard()
.withRegion(Regions.AP_SOUTH_1)
.build();
InvokeRequest req = new InvokeRequest()
.withFunctionName("BasicLambda")
.withPayload("{ \"name\":\"peter\" }"); // optional
InvokeResult invokeResult = client.invoke(req);
System.out.println(invokeResult);
System.out.println(invokeResult.getPayload());
String converted = new String(invokeResult.getPayload().array(), "UTF-8");
System.out.println(converted);
}
}
Exercise 1
- Create a Aws Lambda and upload it to Amazon AWS lambda Service
- Create a Test for the uploaded lambda
- Run the test and see the logs in cloudwatch
- Invoke the lambda function from Java code.
Create a S3 bucket for lambda invocation

package com.lambda.demo;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
public class LambdaS3RequestHandle implements RequestHandler<S3Event, String> {
@Override
public String handleRequest(S3Event event, Context context) {
LambdaLogger lambdaLogger = context.getLogger();
lambdaLogger.log("Received event" + event);
// Get the object from the event and show its content type
String bucket = event.getRecords().get(0).getS3().getBucket().getName();
String key = event.getRecords().get(0).getS3().getObject().getKey();
lambdaLogger.log("bucket >>>>" + bucket);
lambdaLogger.log("key >>>>" + key);
return "S3 Uploaded...";
}
}
Create a lambda jar with following class
Create a new function

Add Trigger

Select S3 trigger


Fill the details and click on add

You will see that S3 trigger added

Upload the lambda to catch S3 Object create event

Configure VPC for the lambda
Upload file to S3 bucket

In the cloudwatch you can check the logs for the lambda function


You can see the logs for the S3 bucket upload from the lambda
Java Code to Capture SQS Event using lambda
package com.lambda.demo;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
public class LambdaSQSRequestHandler implements RequestHandler<SQSEvent,String> {
@Override
public String handleRequest(SQSEvent event, Context context) {
LambdaLogger lambdaLogger = context.getLogger();
for(SQSEvent.SQSMessage msg : event.getRecords()){
lambdaLogger.log(new String(msg.getBody()));
}
return event.toString();
}
}

Create an SQS to Test with Lambda

Create a Lambda Function and configure VPC and assign a role to it

Upload the Lambda which contains SQS event handling

Go to the SQS right click and choose Configure Trigger for Lambda Function

Select the Lambda which needs to be triggered from SQS
Send a message to SQS


You can check the logs of lambda triggered from cloudwatch
package com.lambda.demo;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
public class LambdaSNSRequestHandler implements RequestHandler<SNSEvent, String> {
@Override
public String handleRequest(SNSEvent input, Context context) {
LambdaLogger lambdaLogger = context.getLogger();
lambdaLogger.log(input.getRecords().get(0).getSNS().getMessage());
return input.toString();
}
}
Java Code for SNS Event

Create SNS topic to Trigger Lambda
Create a lambda Function which need to be triggered by SNS, Specify VPC, upload the jar and enter the Handler

Create SNS Subscription for Lambda

Publish the Message from SNS


Now check the logs in Cloudwatch


Exercise 2
- Create a lambda which triggers when some object is uploaded on the S3.
- Create a lambda which consumes the items from SQS
- Create a lambda which is triggered when data is pushed in SNS
API Gateway
- Amazon API Gateway is a fully managed service that makes it easy for developers to create, maintain and publish APIs.
- API Gateway has no minimum fees or startup costs. You pay only for the API calls you receive and the amount of data transferred out.
Creating API using API Gateway
- Go to the API Gateway Service

Click on create API button

Fill the details for your API and Hit Create API

Go to the Action button and click on create resource

Fill the name of the resource, enable API Gateway for cors and Create Resource

Select the resource, Go to the Actions and Create Method


Select the method type for the resource

Now create a lambda with the code below
package com.lambda.demo;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class LambdaAPIEmployeeGetHandler implements RequestHandler<EmployeeRequest, String> {
@Override
public String handleRequest(EmployeeRequest input, Context context) {
LambdaLogger lambdaLogger = context.getLogger();
lambdaLogger.log(">>>>>>>>>>>>>>>>>>>>>>>"+input.toString());
return "Request Accepted by Lambda";
}
}
package com.lambda.demo;
public class EmployeeRequest {
private String httpMethod;
// Getter, Setter and toString
}
Select the lambda you need to trigger for Get call and Click on Save


Click on ok to add API Gateway Permission to lambda

Click on the Integration Request

Go to the Mapping Templates, click on Add mapping template and enter application/json in text box
Use the velocity script for template mapping to capture request variables and click on save

Now go to the Action and Deploy the API


Create a new Deployment Stage and Click on Deploy
Now copy the Invoke URL

Hit the invoke url after suffix resource name

You can check the logs in Cloudwatch

Creating a POST request with DynamoDB


Click on Create table

Enter the name of the table and primary key and click on Create button
Create a Lambda for Creating Employee
package com.lambda.demo;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.PrimaryKey;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class CreateEmployee implements RequestHandler<EmployeeRequest, Item> {
@Override
public Item handleRequest(EmployeeRequest input, Context context) {
AmazonDynamoDB client = AmazonDynamoDBClientBuilder
.standard()
.withRegion(Regions.AP_SOUTH_1)
.build();
LambdaLogger lambdaLogger = context.getLogger();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("employee");
Employee paramEmployee = input.getEmployee();
try{
lambdaLogger.log("paramEmployee>>"+paramEmployee);
Item item = new Item()
.withPrimaryKey("id", paramEmployee.getId())
.withString("name", paramEmployee.getName())
.withString("age", paramEmployee.getAge());
lambdaLogger.log("Trying to save the Item");
table.putItem(item);
lambdaLogger.log("Saved Item");
}catch (Exception ex){
lambdaLogger.log("Exception :: "+ex.getMessage());
}
return table.getItem(new PrimaryKey("id",paramEmployee.getId()));
}
}
Employee POJO
package com.lambda.demo;
public class Employee {
private String id;
private String name;
private String age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
Employee Request
package com.lambda.demo;
public class EmployeeRequest {
private String httpMethod;
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public String getHttpMethod() {
return httpMethod;
}
public void setHttpMethod(String httpMethod) {
this.httpMethod = httpMethod;
}
@Override
public String toString() {
return "EmployeeRequest{" +
"httpMethod='" + httpMethod + '\'' +
", employee=" + employee +
'}';
}
}
Upload the Lambda for creating Employee
Create POST method employee


Click ok to give API Gateway permission to invoke Lambda
Go to the Integration Request

In the mapping template add application/json and the velocity script

#set($inputRoot = $input.path('$'))
{
"httpMethod" : "$context.httpMethod",
"employee" : $input.json('$')
}

Deploy the API

Deploy Emp

Now hit the Post request for Employee

You can check the entry in Dynamo DB for Employee Created
Cloudwatch logs for Object created

Error Handling For POST request
Go to the Models and Create a new Model as shown below


Go to the Method Request Section of POST Method

Select the Values of Request Validator, HTTP Request Harders and Request Body as shown below
In the Gateway Response Section Change the Velocity Script for Response Template as shown below for Bad Request Body

Fetch the Data from Dynamo DB
package com.lambda.demo;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import java.util.List;
public class LambdaAPIEmployeeGetHandler implements RequestHandler<EmployeeRequest, List<Employee>> {
@Override
public List<Employee> handleRequest(EmployeeRequest input, Context context) {
AmazonDynamoDB client = AmazonDynamoDBClientBuilder
.standard()
.withRegion(Regions.AP_SOUTH_1)
.build();
LambdaLogger lambdaLogger = context.getLogger();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("employee");
DynamoDBMapper mapper = new DynamoDBMapper(client);
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
List<Employee> scanResult = mapper.scan(Employee.class, scanExpression);
lambdaLogger.log(scanResult.toString());
return scanResult;
}
}
package com.lambda.demo;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
@DynamoDBTable(tableName="employee")
public class Employee {
private String id;
private String name;
private String age;
@DynamoDBHashKey(attributeName="id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
Introduce @DynamoDBTable and
Deploy the Latest Jar for GetEmployee


Create a new resource for Path variable employee id

Specify the Path Variable id in Resource name and click on Create Resource
Introduce a Get method for employee/{id}

package com.lambda.demo;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class GetEmployee implements RequestHandler<EmployeeRequest, Employee> {
@Override
public Employee handleRequest(EmployeeRequest input, Context context) {
LambdaLogger lambdaLogger = context.getLogger();
AmazonDynamoDB client = AmazonDynamoDBClientBuilder
.standard()
.withRegion(Regions.AP_SOUTH_1)
.build();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("employee");
DynamoDBMapper mapper = new DynamoDBMapper(client);
Employee employee = mapper.load(Employee.class, input.getEmpId(),
new DynamoDBMapperConfig(DynamoDBMapperConfig.ConsistentReads.CONSISTENT));
lambdaLogger.log(employee.toString());
return employee;
}
}
Create and upload the lambda to Get One Employee

Specify the Lambda function to be triggered

Add Permission to the lambda Function
Go to the integration request

Under the integration Request select the Mapping Template

#set($inputRoot = $input.path('$'))
{
"httpMethod":"$context.httpMethod",
"empId":"$input.params('id')",
"name": "$input.params().querystring.get('name')"
}
Script to generate Template
Deploying the API

Now hit the employee API to get one employee

Logs from Cloudwatch

Create a Plan and Click on Next


Select the API and Stage

Hit Next

Add the Employee API in the plan
Securing the API
Go to API Key and Select Create API Key

Enter the name of the key and Click on save


Enter the name of the plan for the Key
Click on Show to get the Key

Go to the method request and set API Key Required to true

Deploy the API

Hit the API without Token


Hitting the API with response
Exercise 3
- Create a Restful API for Student (id, name, age) to create the employee
- Create a Restful API to read all the records
- Create a Restful API to read the record on the basis on id.
- Secure the API with API TOKEN
Elastic Beanstalk
-
Traditionally, deploying a web application on AWS may have required spending time
-
selecting appropriate AWS services such as EC2, ELB, Auto Scaling, and so on, and creating and configuring an AWS resource from scratch to host a web application can be quite a pain.
-
It could be difficult for developers to build the infrastructure, configure the OS, install the required dependencies, and deploy the web services.
-
AWS Elastic Beanstalk removes the need to
manually build an infrastructure for the developer and makes it possible for them to quickly deploy and manage a web application on AWS of any scale.
Create a Spring Boot Project


Create a dummy Controller and Build the war file

Go to Elastic Bean Stack Service and Click on Get Started

Enter the Application name and platform, upload the spring boot war and then click on Create application
Select the war, click on upload and click on Create Application

You will start getting the logs now

You can check the status of your application in Cloudformation

You can check the various resouces created by Elastic beanstalk


Elastic IP
EC2
Now our Application is Deployed via elastic beanstalk. Url displayed at the top of this screen will be used to access the application


Hit the Url on the browser and you can see the result
Create an RDS instance

Introduce Spring Data JPA in Spring Boot Application
plugins {
id 'org.springframework.boot' version '2.2.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
id 'war'
}
group = 'com.spring.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
implementation 'org.springframework.boot:spring-boot-starter-web'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
spring.datasource.url=jdbc:mysql://mydb.cmetoyhg7bms.ap-south-1.rds.amazonaws.com:3306/mydb
spring.datasource.username=admin
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
application.properties
Employee Entity
package com.spring.demo.elasticbeanstalk.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Employee {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer age;
public Employee() {
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
Employee Repository
package com.spring.demo.elasticbeanstalk.repository;
import com.spring.demo.elasticbeanstalk.entity.Employee;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
}
package com.spring.demo.elasticbeanstalk.event;
import com.spring.demo.elasticbeanstalk.entity.Employee;
import com.spring.demo.elasticbeanstalk.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Bootstrap implements CommandLineRunner {
@Autowired
EmployeeRepository employeeRepository;
@Override
public void run(String... args) throws Exception {
employeeRepository.save(new Employee("Peter",29));
System.out.println(employeeRepository.count());
}
}
build a new jar and upload the new version to the elastic beanstalk application


Select the war and click on deploy

You can check the logs of the deployed application


You can delete the application as shown below. It will also delete all the associated resources created by application

AWS for Java Developers Part 4
By Pulkit Pushkarna
AWS for Java Developers Part 4
- 1,085