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)
  • AWS Lamba is used as the serverless runtime
  • Gradle is used as the build automation system to compile, build, and deploy the serverless function(s).
  • JUnit 4 is used for unit-testing
  • Jackson is used as the the JSON processor to serialize and deserialize objects
  • Apache Log4J 1.2 is used as the remote logger on the serverless runtime system

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}

For instance, by sending an HTTP POST request like so:

Before looking at Java code and Gradle build script, let’s get some administrative stuff out of the way 1st …

1 Creating an AWS Account

Create an AWS Account (aka Root User Account) here:
https://portal.aws.amazon.com/gp/aws/developer/registration/index.html
A credit card number and working phone is needed during the setup process. Select “Basic” for the support plan. (This will take a while, take your time.)

2 Creating an IAM Account

Create an IAM Account (aka AWS Identity and Access Management (IAM) user) here:
https://console.aws.amazon.com/iam

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.

Download Python 3.x.x from here https://www.python.org/downloads/ and run the package installer.

In terminal, run these commands to install pip and awscli

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:

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

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

~/.aws/config

5 Lambda Function Implementation

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

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>

5.1 RequestHandler Implementation

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

5.3 Response

5.4 Logging

To see the log statements in cloudwatch, another AWS service, accessible here: https://console.aws.amazon.com/cloudwatch, the ./src/main/resources/log4j.properties file should look like this:

 

6 Build – Deploy – Invoke

The AWS Command Line Client provides commands to create, update, or invoke a lambda function:

However, using Gradle talks for this instead, allows for more a convenient deployment from inside an IDE. The first few lines declare the necessary values:

  1. awsProfileName –  The AWS User Profile name, like declared in ~/.aws/credentials
  2. roleName – That is the role ARN, available in the IAM console and should look something like this:  arn:aws:iam::123…….:role/MyEduLambdaExecRole
  3. lambdaFunctionName – The name of the Lambda function, e.g., lambda-pojo
  4. handlerName – That is the fully qualified class name of the class implementing the RequestHandler interface, e.g. com.wolfpaulus.aws.Prime
  5. jsonPayload –  A JSON encoded String that can be used to test the Lambda function, e.g. {\”number\”: 17}
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.
The invoke task will send the payload and display the tail of the log and the response.

7 Web Service

The lambda function still needs to be made available as a Web service, which can be achieved with the API-Gateway.

Login to the API-Gateway here: https://console.aws.amazon.com/apigateway/home and click “Get Started”, then select ‘New API’ and enter a name, e.g. MyEduLambdaApi. and click the “Create API” button.

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:

Request/
Status: 200
Latency: 3062 ms

Response Body

Response Headers

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

Posting a payload

Result

8 Git Repository

https://github.com/wolfpaulus/LambdaTemplate

 

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:

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.

Deploy the new Lambda function before moving on.

9.2 Bot Creation

Login to the Lex console https://console.aws.amazon.com/lex/home and click the “Create button”

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.

Select AWS Lambda in fulfillment and select the newly deployed Lambda function (e.g. lambda-lex) and select None for the Follow-up message.

After clicking the build button  (top-right), the bot can be tested.

Publish the bot, before moving on.

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. //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 …

Leave a Reply