Exploring Java Spring Core 6 and AWS Lambda for Scalable Enterprise Serverless Solutions
In the realm of enterprise applications, the need for robust, scalable, and efficient solutions is paramount. Java, particularly with the Spring framework, has long been a cornerstone for enterprise development due to its rich ecosystem, extensive library support, and strong community backing. Spring provides comprehensive support for both modern protocols and legacy integrations, making it a versatile choice for a wide range of use cases.
On the other hand, AWS Lambda has revolutionized the way we approach application development and integration. As a serverless computing service, Lambda offers several advantages like rapid development cycles, reduced Operational Overhead: By abstracting away the underlying infrastructure, cost Efficiency pay-as-you-go model.
By combining the power of AWS Lambda with the core capabilities of Java Spring, enterprises can achieve a significant competitive edge.
In this blog we will going to learn more about how we can developed and deployed the application with using custom approach.
Prerequisite: Java17, Spring core6, AWS CLI, and AWS SAM.
Prerequisite
- Install Java JDK 17
- Apache Maven 3.x
- Install AWS CLI
- Install AWS SAM
5. Configure the AWS CLI access:
Download and install the AWS CLI refer the link
$ aws configure
AWS Access Key ID [None]: <<ACCESS-KEY>>
AWS Secret Access Key [None]: << SECRET-ACCESS-KEY >>
Default region name [None]: <<REGION>>
Default output format [None]: json
Installations:
Step by step process to create a project:
1. Create a Java Project
create a java project name spring-core-lambda-poc using maven command line:
mvn archetype:generate -DgroupId=com.springcorepoc -DartifactId=springcorepoc.lambda -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
2. Update Spring and lambda dependencies:
Please refer pom.xml to update the your pom.xml.
3. Create a template.yaml and samconfig.toml
please create blank files in the root directory of the java project.
Please use template.yaml and samconfig.toml for the reference.
4. Adding Spring config File
to scan all the spring class, please add the below java class:
package com.springcore.lambda.poc.handler.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.springcore.lambda.poc")
public class AppConfig {
}
5. Create a Function
will create 2 function one for API and second for cloud watch rule.
- Create Demo API
Please create Demo API as mentioned in the code:
SpringAPIHandler:
package com.springcore.lambda.poc.handler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springcore.lambda.poc.apihandler.DemoAPIHandler;
import com.springcore.lambda.poc.handler.config.AppConfig;
public class SpringAPIHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final Logger Log = LogManager.getLogger(SpringAPIHandler.class);
private ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent request, Context context) {
Log.info("Lambda function started");
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
try {
DemoAPIHandler handler = applicationContext.getBean(DemoAPIHandler.class);
response.setStatusCode(200);
response.setBody(objectMapper.writeValueAsString(handler.requestHanlder()));
} catch (Exception e) {
response.setStatusCode(500);
response.setBody("Internal Server Error");
Log.error(e);
}
Log.info("Lambda function ended");
return response;
}
}
package com.springcore.lambda.poc.apihandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
@Component
public class DemoAPIHandler {
private static final Logger Log = LogManager.getLogger(DemoAPIHandler.class);
public String requestHanlder() {
Log.info("Lambda function started");
String response = "success";
Log.info("Lambda function ended");
return response;
}
}
b. Scheduler
For scheduler please add the below classes:
package com.springcore.lambda.poc.handler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springcore.lambda.poc.eventhandler.ScheduleEventHandler;
import com.springcore.lambda.poc.handler.config.AppConfig;
public class EventProcessor implements RequestHandler<ScheduledEvent, String> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final Logger Log = LogManager.getLogger(EventProcessor.class);
private ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
@Override
public String handleRequest(ScheduledEvent event, Context context) {
Log.info("scheduler function started");
try {
ScheduleEventHandler handler = applicationContext.getBean(ScheduleEventHandler.class);
Log.info("response: {}", objectMapper.writeValueAsString(handler.eventHanlder()));
} catch (Exception e) {
Log.error("Internal Server Error");
Log.error(e);
}
Log.info("scheduler function started");
return "Event processed successfully";
}
}
package com.springcore.lambda.poc.eventhandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
@Component
public class ScheduleEventHandler {
private static final Logger Log = LogManager.getLogger(ScheduleEventHandler.class);
public String eventHanlder() {
Log.info("ScheduleEventHandler function started");
String response = "success";
Log.info("ScheduleEventHandler function ended");
return response;
}
}
6. Update the template.yaml
Please add below code in template file
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Globals:
Function:
Runtime: java17
VpcConfig:
SecurityGroupIds:
- !Ref VPCSecurityGroupIds
SubnetIds:
- !Ref VPCSubnetIds1
- !Ref VPCSubnetIds2
CodeUri: target/springcorepoc.lambda-1.0.jar
Api:
OpenApiVersion: 3.0.1
Resources:
ScheduleEventFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: com.springcore.lambda.poc.handler.EventProcessor
FunctionName: !Sub 'EventProcessor-Function-${StageName}'
Role: !Ref LambdaExecutionRole
MemorySize: 512
Environment:
Variables:
ENV: !Ref StageName
Events:
ScheduledEvent:
Type: Schedule
Properties:
Schedule: cron(0/5 * * * ? *)
Api:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref StageName
OpenApiVersion: 3.0.1
GetDemoAPIFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: com.springcore.lambda.poc.handler.SpringAPIHandler
FunctionName: !Sub 'GetDemoAPIFunction-Function-${StageName}'
Role: !Ref LambdaExecutionRole
Timeout: 29
Environment:
Variables:
SESSION_TIME_OUT: !Ref SessionTimeOut
Events:
SpringDemoApi:
Type: Api
Properties:
Path: /demo
Method: get
RestApiId: !Ref Api
Parameters:
SessionTimeOut:
Type: String
Default: '10'
VPCSecurityGroupIds:
Type: String
Description: List of Security Group IDs
VPCSubnetIds1:
Type: String
Description: List of Subnet IDs
VPCSubnetIds2:
Type: String
Description: List of Subnet IDs
StageName:
Type: String
Description: stage name
LambdaExecutionRole:
Type: String
Description: ARN of the Lambda execution role
Outputs:
ApiUrl:
Description: "URL of the API Gateway"
Value: !Sub "https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/"
7. Update the samconfig.toml
version = 0.1
[default]
[default.global.parameters]
[default.deploy.parameters]
capabilities = "CAPABILITY_IAM"
confirm_changeset = true
[dev.deploy.parameters]
s3_bucket = "<<bucket-name>>"
stack_name = "spring-lambda-poc-sam-deployment"
s3_prefix = "event-scheduler-dev"
region = "us-east-1"
capabilities = "CAPABILITY_IAM"
parameter_overrides = "LambdaExecutionRole=arn:aws:iam::<<accountid>>:role/lambda-execution-role-dev StageName=dev VPCSecurityGroupIds=sg-<<sg_id_1>> VPCSubnetIds1=subnet-<<subnet_id_1>> VPCSubnetIds2=subnet-<subnet_id_2>>"
8. Build and deploy the project
a. Build the java Project
mvn clean package
b. Package the application
sam package --template-file template.yaml --output-template-file packaged.yaml --config-file samconfig.toml --s3-bucket <<bucket_name>>
b. Deploy the application
sam deploy --template-file packaged.yaml --config-file samconfig.toml --config-env dev
9. Testing
- API : Please invoke below in the POSTMAN bowser:
https://<<domain>>api.us-east-1.amazonaws.com/dev/demo
2. Scheduler:
Please check the cloud watch log.
9. Source code:
Please find the github link for source code.
Conclusion:
In this blog, we have learned how we can deploy the Lambda and use it like an API and batch job. This is a base project and can be extended to develop any application or real-world use case.