Make Any Java Web App Serverless

Alexey Balchunas
4 min readJul 24, 2018

The idea behind the serverless architecture is simple but powerful: your infrastructure has no servers (that you maintain). You focus on your application code, and it gets executed and scaled on its own by a cloud provider.

You may not even realize that parts of your infrastructure are formally serverless. Virtually any part can be: frontend (e.g. AWS S3 hosting), API (e.g. AWS Lambda + AWS API Gateway), backend (e.g. AWS Lambda), Database (e.g. DynamoDB), File Storage (e.g. S3), etc.

In this particular post, we will take a look at how to deploy your Java code to AWS Lambda. For some programming languages, AWS has better support: e.g. for nodejs, you may just use the official wrapper for express. For Java, there are a number of solutions made by the community, but frankly, nothing universal like we have for nodejs.

Preparing Your Java Servlet

I assume that you have the plain old java servlets, and you want to make them serverless using AWS Lambda. We’ll use the following super simple servlet class as an example:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class PingServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// In real life, you'd have your application logic here.
// We omit any complexity here on purpose. response.setContentType("text/html");
response.setStatus(200);
PrintWriter out = response.getWriter();
out.print("OK");
out.close();
}
}

This servlet just returns “OK” on any request. Now, since there was no ready solution for Java to be able to deploy it to AWS Lambda, we came up with our own, universal one. You may take a look at the code here.

In order to use the library, you will have to add my bintray repository to your pom.xml (here I assume you’re using Maven):

<repository>
<id>bintray</id>
<url>http://dl.bintray.com/bleshik/maven</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>

Then add the dependency:

<dependency>
<groupId>com.github.bleshik</groupId>
<artifactId>aws-lambda-servlet</artifactId>
<version>1.0</version>
</dependency>

One more thing — you have to build a fat jar in order to deploy your code to AWS Lambda. In Maven you’ll need to add this:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

And the last thing, you need to create the handler, a class that will be responsible for handling requests to AWS Lambda. The whole idea behind the aws-lambda-servlet lib that you’ve just added is to provide an easy and efficient way of wrapping your plain old java servlet into an AWS Lambda handler. Hence, your handler would look something like:

import util.ServletRequestHandler;public class ServletAdapter extends ServletRequestHandler {
public ServletAdapter() {
super("/", new PingServlet());
}
}

BOOM! You’re ready to deploy your jar to AWS Lambda.

The awesome thing about it is that no HTTP server is used when executing the handler. Everything happens in memory.

Deploying the Jar to AWS Lambda

Here comes the most difficult part: you need to upload your jar, create an API in AWS API Gateway, create a PROXY API resource, create a stage and initiate a deployment of the API. Sounds complex, huh?

To make things easier for you, here is a template for AWS CloudFormation that will create everything your API needs to get deployed. Upload your jar to AWS S3 and specify S3 bucket and S3 key of the jar in the template’s parameters:

https://console.aws.amazon.com/cloudformation/home?#/stacks/new?templateURL=https://s3-eu-west-1.amazonaws.com/bleshik/aws-lambda-servlet-example-simple-CloudFormation.json

After the template is executed successfully, find your API in the API Gateway dashboard, then go to Stages and find the “api” stage, click and you will see the URL of your API. Here is what I got (if you click on it, it won’t work):

https://5smlp6f63h.execute-api.eu-west-1.amazonaws.com/api

However, in order to have it working, you need to specify a request path. For example (you can click on it, it will work):

https://5smlp6f63h.execute-api.eu-west-1.amazonaws.com/api/ping

The Example Sources

The complete source code for this particular example including the CloudFormation template can be found here.

What’s Next?

I know what you’re thinking when you see this URL:

https://5smlp6f63h.execute-api.eu-west-1.amazonaws.com/api

Most probably, instead of this you want to have a nice looking URL with a custom domain. This will be covered later in this blog.

Also, you may notice that the first request is much slower than the consequent ones. That’s called a “cold start”, after which the handler is sitting in memory cached for about 5 minutes. After these 5 minutes, the first request will be slow again. You may try to warm up the lambda by scheduling it for every 5 minutes using the CloudWatch Event Rules, but this will not work well. Here’s why.

Fortunately, I have a good solution for warming up your lambdas.

If you’re into highly robust and scalable applications, check out my other article: How to Make Multiregional Application (and Pay Zero).

Comments, likes, and shares are highly appreciated. Cheers! ❤️

--

--