Creating a Serverless framework project with Java Lambdas

The Serverless.com cli gives 2 Lambda project type options for new projects – Node,js and Python:

% serverless  
 Serverless: No project detected. Do you want to create a new one? Yes
 Serverless: What do you want to make? 
   AWS Node.js 
   AWS Python 
 ❯ Other 

If you select Other, it prompts you to create a project using a template:

Run “serverless create --help” to view available templates and create a new project from one of those templates.

The ‘create –help’ option tells you to run with the –template option and provides a long list of supported project types. Since I’m using Maven with Java, I’ll use the aws-java-maven option:

serverless create --template 

Since I already had a Maven pom.xml in place as a starting point for my Lambdas in this test project, the serverless cli warns that it won’t overwrite the existing file. I’m not familiar with what additional dependencies the aws-maven-template will add, so I renamed my pom.xml and reran the ‘serverless create’ cli and generated a new pom.xml.

Looking in the new file, there’s a similar and expected use of the Maven Shade plugin to bundle a ‘fat jar’ and other dependencies for Log4J and the addition of Jackson for json parsing.

There’s also a couple of extra Classes generated too that I wasn’t expecting, but they match up with the example code in the serverless docs (article here), so there’s a ApiGatewayResponse class that I wasn’t familiar with (from building AWS Lambdas with Java by hand and not using the API Gateway Lambda Proxy feature).

As a test, I looked into creating a couple of Java Lambdas not using the generated Classes just to confirm that there’s nothing Serverless framework specific that needs to be used. As it turns out, the default usage of the APIGateway Lambda Proxy feature the Lambda runtime is is expecting to map a json payload into the handler parameters and similarly for the response payload. For testing I just wanted to pass a couple of String request params on a GET request. So for my first test I got the following exception:

An error occurred during JSON parsing: java.lang.RuntimeExceptionjava.lang.RuntimeException: An error occurred during JSON parsingCaused by: java.io.UncheckedIOException: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token at [Source: lambdainternal.util.NativeMemoryAsInputStream@4cf777e8; line: 1, column: 78] (through reference chain: java.util.LinkedHashMap["headers"])Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token at [Source: lambdainternal.util.NativeMemoryAsInputStream@4cf777e8; line: 1, column: 78] (through reference chain: java.util.LinkedHashMap["headers"])    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)  

At this point I got distracted with a wide range of Java Lambda together with the API Gateway Lambda Proxy specific issues which I covered in a separate post here.

Long story short, the Serverless config for a Java Lambda enables the API Gateway Lambda Proxy feature by default, which means your Lambda impl needs to have a POJO class for it’s return type that matches exactly what API Gateway expects, so the Lambda to API Gateway Proxy integration can map the return value to the expected JSON structure. You can build this yourself to match what is described in the docs (link above) or just use the provided class generated by the aws-java-lambda template. The generated class ApiGatewayResponse is exactly what you need, so rather than reinventing the wheel I changed to use this generated class as the return value from my Java Lambda handler and now it works as expected.

My handler now looks like this:

public class MyHandler implements RequestHandler, ApiGatewayResponse> {

    @Override public ApiGatewayResponse handleRequest(Map<String, Object> input, 
        Context context) {
    }
}

Note that in order to receive parameters from incoming requests via API Gateway proxy, the first parameter needs to be a Map<String, Object>.

This is the first time I’ve used API Gateway Lambda Proxy with Java Lambdas. Previously the JavaLambdas I’ve built took advantage of API Gateway mapping any parameters to your Lambda automatically using Jackson to a POJO parameter on your Handler method, and even handing a POJO return type serializing that to a JSON response for you. I’ll come back and do some comparisons between these two approaches later.

To deploy your Java Lambda using serverless it’s the same as with Node.js Lambdas or any other supported runtime:

serverless deploy

To test calling your Java Lamdba function locally as if it’s deployed to AWS, use

serverless invoke local --function functionName

where functionName is what to defined your handler as in your serverless.yml.

By default the generated ApGatewayResponse class doesn’t have a toString() so you’ll see the response to your local test print something like:

com.serverless.ApiGatewayResponse@9301672

but you can add a toString() to help with testing locally (this is mentioned in the docs here).

The servless.com framework saves a lot of time in automating the deployment and configuration of your Lambdas and is well worth a look.

AWS CloudFormation basics – part1

Collection of notes, templates and tips for building AWS CloudFormation templates.

The basic structure of CloudFormation files (in JSON):

{
  "Resources" : {
    "ExampleResourceName" : {
      "Type" : "AWS::?::?",
      "Properties" : {
        "Example" : "propertyvalue"
      }
    }
  }
}
  • Resources: the AWS services to be provisioned. There can be multiple repeating Resource elements in this section, to provision/configure multiple services in a stack
  • ExampleResourceName: a name for each resource being provisioned
  • Type: the AWS type for the resource, e.g. AWS::S3::Bucket
  • Properties: properties for the service being provisioned/configured

AWS CLI commands:

aws cloudformation create-stack --stack-name STACK-NAME
  --template-body file://template-file.json
  --parameters ParameterKey=example1,ParameterValue=value1    
    ParameterKey=example2,ParameterValue=value2
aws cloudformation list-stacks
aws cloudformation delete-stack --stack-name STACK-NAME

If your stack creates IAM resources, you’ll also need to pass:

--capabilities CAPABILITY_NAMED_IAM

Otherwise you’ll see this error:

An error occurred (InsufficientCapabilitiesException) when calling the CreateStack operation: Requires capabilities : [CAPABILITY_NAMED_IAM]

Summary: What you need to deploy a Docker container to AWS ECS Fargate

In my previous post I walked through a couple of tutorials to deploy a test Docker container to AWS ECS Fargate. As a summary, here’s the various parts that you need to have in place to deploy a Docker container using Fargate:

  • A Docker image, deployed to a Docker repository, e.g. either Docker Hub, or AWS ECR
  • A VPC with either a public or private subnet (or both)
  • A Security Group to define what traffic is allowed in and out to your running Container
  • A ELB Load Balancer, assuming you’re running more than 1 instance of a container and are not accessing a single instance directly with a public IP
  • An ECS Cluster
  • An ECS Task Definition
  • An ECS Fargate Service Definition to create the running instance of your task

Using Route 53 to create subdomain names for your projects

If you create and deploy your own software projects to the cloud, at some point you probably end up with a number of things deployed to various places and unless you spend time maintaining your bookmarks to all these projects, it becomes hard to keep track after a while.

One of the interesting things about Route 53 is that you can create A records that resolve to IP addresses either within AWS or hosted elsewhere. If you have you own domain setup in Route 53, you can easily create subdomains with A records pointing to where ever these projects are hosted. e.g.

example1.youdomain.com -> x.x.x.x

example2.yourdomain.com -> y.y.y.y

A while back I deployed my Sudoku Solver React app to an S3 bucket hosting the website, and I can never remember the S3 endpoint name. But using a Route 53 Alias to the S3 endpoint, you can create whatever subdomain you need to point to the target resource. Here’s what it looks like setting up an alias:

Notes:

  • when you click in the Alias Target box you should see your S3 bucket already listed (if not, check you’ve enabled Static Website Hosting)
  • the recordset name must be identical to the first part of your bucket name (e.g. ‘example’)
  • the S3 bucket name must be the subdomain name plus full domain, e.g. example.yourdomain.com