Serverless compute with Java, on AWS Lambda

Serverless computing is a cloud computing execution model, in which the cloud provider dynamically manages the allocation of machine resources. Serverless computing allows running applications and services, without thinking much about servers, runtime resources, or scaling issues.

A very simple serverless computing application is intended to be used as a template project, a model, or starting point, for your next Java-based serverless cloud project. Here are the cornerstones:

Java 8 is used as the implementation language of the serverless function(s)

In the last section of this article, I show how the lambda function can be adjusted, to provide the business logic (serve as fulfillment) for Amazon Lex, and how the Lex chatbot can be exposed as a Slack application.

Task

Using Java, to implement an AWS-Lambda function and expose it through the AWS-API-Gateway. The task of the AWS Lambda function is simple, to determine if a provided number is a prime number or not. Here is an example of the input and output:

PrimeCheck: Find out if the number posted in a json-payload is a prime number, and return a response like this:

{“number”:139} -> {“answer”:”Yes, 139 is a prime number, divisible only by itself and 1″,”n”:139,”d”:1}

{“number”:141} -> {“answer”:”No, 141 is not a prime number. For instance, it’s divisible by 3″,”n”:141,”d”:3}

2 Creating an IAM Account

2.1 Group

Select ‘Group’ on the left side and then click the “Create new Group” button.
Enter a name (e.g. MyEduGroup) and in the next step add these three policies:

AWSLambdaFullAccess

IAMFullAccess

AmazonAPIGatewayAdministrator

Review and click the “Create Group” button.

2.2 User

Select ‘User’ on the left side and then click the “Add user” button, to create a new IAM user.
Create a user name (e.g. MyEduUser) and select “Programmatic access” only.
Next, add the user to the group, mentioned above, (e.g. MyEduGroup)
Review and click the “Create user” button and don’t forget to download the csv file, containing the credentials.

2.3 Role

Select ‘Roles’ on the left side and then click the “Create new role” button, to create a new role.
Select AWS Lambda
Select the AWSLambdaBasicExecutionPolicy
Assign a name to the new role (e.g. MyEduLambdaExecRole) and click the “Create role” button.

3 AWS CLI

Having the AWS Command Line Client available can be convenient, but almost everything it does, can also be accomplished using the web UI; Moreover, we will do the more repetitive things, like creating, updating, or invoking Lambda functions, with Gradle tasks. So installing the AWS CLI is optional.

The AWS Command Line Client is now installed in this directory: ~/Library/Python/3.6/bin and after adding this line to the hidden ~/.bash_profile file:

1

export PATH=$PATH:~/Library/Python/3.6/bin

the aws cli can be launched easily, e.g.:

1

~/aws--version

4 AWS Profile and Credentials

The default file location for the config file can be overridden by setting the AWS_CONFIG_FILE environment variable. However, by default, a ~/.aws/credentials and a ~/.aws/config file are expected. The AWS CLI can be used to create those files (e.g. aws configure –profile MyEduUser) or the files can be manually created, copying values from the csv file, downloaded in Step 2.2. The awscli will not consume the credentials file directly. I.e., you have to copy and paste the key_id and access_key values.

~/.aws/credentials

1

2

3

[MyEduUser]

aws_access_key_id=AKIA****************

aws_secret_access_key=fnlBt/nB********************************

~/.aws/config

1

2

3

[profile MyEduUser]

region=us-east-1

output=json

5 Lambda Function Implementation

The aws-lambda-java-core library contains java interfaces, mostly for convenience. There is this RequestHandler interface for instance:

1

2

3

4

5

6

7

8

9

publicinterfaceRequestHandler<I,O>{

/**

* Handles a Lambda Function request

* @param input The Lambda Function input

* @param context The Lambda execution environment context object.

* @return The Lambda Function output

*/

publicOhandleRequest(Iinput,Context context);

}

The environment context object provides access to log, configured memory, execution time and more. It also provides access to the ClientContext, which has a mutable custom Map<String,String>

Declaring that the Prime class implements RequestHandler<Request, Response>, means that it will receive a Request object and return a Response object, both of which still need to be defined. Since Lambda need to deserialize the Request object and eventually serialize the Response object, both classes will need to be carefully crafted, either by providing (JavaBean-style) getter and setter methods or by using jackson.annotation. To show both, the Request class uses getter and setter methods, while the Response class uses Jackson’s JsonProperty annotation.

5.2 Request

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

publicclassRequest{

privatelongnumber;

publicRequest(){

}

longgetNumber(){

returnnumber;

}

publicvoidsetNumber(finallongnumber){

this.number=number;

}

@Override

publicStringtoString(){

returnString.valueOf(number);

}

}

5.3 Response

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

importcom.fasterxml.jackson.annotation.JsonProperty;

importorg.apache.log4j.Logger;

publicclassResponse{

staticfinalLogger log=Logger.getLogger(Response.class);

@JsonProperty("answer")

publicfinalStringanswer;

@JsonProperty("n")

publiclongn;

@JsonProperty("d")

publiclongd=1;

publicResponse(finallongn,finallongd){

this.n=n;

this.d=d;

answer=String.format("No, %d is not a prime number. It's divisible by %d",n,d);

}

publicResponse(finallongn){

log.info("Found a prime number "+n);

this.n=n;

answer=String.format("Yes, %d is a prime number, divisible only by itself and 1",n);

}

publicResponse(){

answer="A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself.";

With this grade build script in place, the deploy task will build the project, package the distribution in a ZIP file, and deploy the Lambda function. If is doesn’t exist already, it will also create the function.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

1:32:18PM:Executing external task'deploy'...

:compileJava

:processResources

:classes

:jar

:assemble

:compileTestJava

:processTestResources

:testClasses

:test

:buildZip

:check

:build

:deploy

BUILD SUCCESSFUL

Total time:10.407secs

1:32:29PM:External task execution finished'deploy'.

The invoke task will send the payload and display the tail of the log and the response.

On the next page, select ‘Resources’ on the left side and select “Create Resource” in the Actions dropdown. Provide a resource name, e.g. PrimeCheck and click the “Create Resource” button to create the resource.

Now select “Create Method” in the Actions dropdown, select ‘Post’ and click the checkmark icon on its right, to confirm your selection. For the “Integration Type”, choose “Lambda Function”. The Lambda Region must match the region where the Lambda function is hosted and the Lambda function is of course the name of the Lambda function (not the name of the handler).

7.1 Testing

Clicking on Test, provides a text field on the bottom of the page, to enter a payload. I entered {“number”:17}, clicked the Test button and saw this response:

7.2 Deploying

Finally, select “Deploy API” in the Actions dropdown, select [New Stage] enter provide a name, e.g. beta and click the “Deploy” button. The invoke URL will be displayed on the next page and looks something like this:

Invoke URL: https://123.execute-api.us-east-1.amazonaws.com/beta

It’s important to note that the URL is not complete yet. The Resource Path, created in the step where the post method was generated, e.g., “/primecheck” still needs to be attached.

Therefore, the complete URL to access this AWS Lambda function would look like this:
https://123.execute-api.us-east-1.amazonaws.com/beta/primecheck

Result

8 Git Repository

9 Using AWS Lambda with Amazon Lex

Lex is Amazon’s service for building conversational interfaces (CUI). Lex provides the advanced deep learning functionalities of automatic speech recognition (ASR) for converting speech to text, and natural language understanding (NLU) to recognize the intent of the text, to enable you to build applications with highly engaging user experiences and lifelike conversational interactions.

9.1 Request from Lex

Before configuring the NLU, i.e. telling Lex what user intents we want it to identify and which entities (aka slots) we accept, lets add some code to the Prime project.

Eventually, Lex will send a request that contains more than just the payload we are accepting so far. Lex also expects a different response. So we are implementing ./lex/LexRequest and ./lex/Response classes and adding a new method to the ./Prime class:

We leave the old Lambda function out there, create a new one, replacing the value of the lambdFunctionName and handlerName in the gradle build script like so:

1

2

3

4

//def lambdaFunctionName= "lambda-pojo"

//def handlerName="com.intuit.iat.Prime"

def lambdaFunctionName="lambda-lex"

def handlerName="com.wolfpaulus.aws.Prime::handleLexRequest"

The implementation of the LexRequest and enclosed CurrentIntent class are direct JSON to Java Class translation, both used when the request arrives and gets deserialized (with Jackson). The same is true for the LexResponse and DialogAction classes. Both are used to deserialize objects into JSON. The model definition was found here.

9.2 Bot Creation

Select ‘Custom Bot’ and give the bot a name (e.g. PrimeCheck). I selected Joanna as the output voice and set the session timeout to 1 min.

Name the intent (e.g. isPrime) and type a sample utterance, using braces to mark a variable, like so: Is ​{number}​ a prime number. Click to blue + sign to confirm. Enter several sample utterances, for how you think one might ask.

Type the name of the variable into the slot name field and select AMAZON.NUMBER for its type. Enter a prompt, for asking the user, if a number could not be found in his input. Again, click the blue + to enter the slot definition.

10 Creating a Slack Application

Sign into Slack https://slack.com/apps and click the “Build” button, then click “Building Slack apps” and “Create a slack app”

In the left menu, click ‘Bot Users’ on the left add a bot user, (e.g., @prime.) Select “Always Show My Bot as Online”, before adding the Bot User.
Now click ‘Interactive Messages’ on the left and click “Enable Interactive Messages”. Enter any valid URL (e.g. https://wolfpaulus.com) and click the “Enable Interactive Messages” button.
Leave this browser window open, as we will need to copy the values for ClientID, Client Secret, and Verification Token.

10.1 Integrating a Slack app with Lex

Login to the Lex console https://console.aws.amazon.com/lex/home and select your Lex bot, then click the Channels tab on top and select Slack (on the left side).
Fill out the form, by providing a name (e.g.,PrimeSlackIntegration), leave the KMS key as “was/lex”, select the alias (e.g., prime), and copy the three values from the other browser, still showing the Slack app: ClientID, Client Secret, and Verification Token.
Don’t forget to click the “Activate” button and leave this browser open, as we need the here displayed OAuth URL and Postback URL in the next step.

.. back on Slack ..

Now back in the browser with the Slack app, click on ‘OAuth & Permissions’ on the left. Click the Add a new Redirect URL button and paste the OAuth URL and click “Apply”.
In the “Permission Scope” drop-down below, start typing “chat:write:bot”, and select. Then do it again with “team:read”. Click Save Changes.

Now click on ‘Interactive Messages’ on the left. Update the “Request URL” with the Postback URL from step 10.1. Apply by clicking the “Save changes” button.

Click on ‘Event Subscriptions’ on the left and select “On”. Again, set the Request URL to the Postback URL. Click the “Add Bot User Event” button, and find the “message.im” event. Apply by clicking the “Save changes” button.

Click on ‘Manage Distribution’ on the left and click the “Add to Slack” button. After authorizing the app for the slack team, the prime user should appear active and ready to talk …