Learning Lambda — Part 2

The Simplest Lambda

Symphonia
Mike Roberts
Jan 27, 2017 · 9 min read

The Simplest Lambda

This is Part 2 of Learning Lambda, a tutorial series about engineering using AWS Lambda. To see the other articles in this series please visit the series home page. To be alerted about future installments subscribe to our newsletter, follow Symphonia on twitter @symphoniacloud, and our blog at The Symphonium.

image

Hello, World — the Lambda version

Welcome to part 2 of Learning Lambda. If you didn't read part 1 you'll need to at least go back and follow the Prerequisites section to get setup.

OK, are you ready for some code? I'm ready for some code. Let's write some code.

Here is the Hello World of Lambda:

This wasn't a lot of code. But it was some. And more importantly this is all the code necessary to implement a Lambda.

We're going to run this code before we're done with this article, but honestly that involves a little annoying work. So for now let's just revel in the code and look at what's here:

  1. Like all Java code, this code is implemented in a Java class, which is in a package. This class has a name (io.symphonia.SimplestLambda) which we'll be using in configuring the Lambda later.
  2. The class has a single method, handler() . This is the method that will be executed by the Lambda runtime. We can name this method whatever we want, but handler() is the convention.
  3. handler() takes one parameter, which is a String, as an input value. There are various valid signatures for a Lambda handler method, and we'll get to that in a later part of that series, but the most simple signature is to take the input to the function as a single String.
  4. handler() has a void return type. This particular Lambda function won't return a value, and so its intended behavior is just to have side effects, i.e. in this case to write something to standard out, but not give a result. Other Lambdas can return values, and we'll come to that in a later part too.
  5. The class has a default, implicit, empty, no-arguments constructor. Every lambda function must be defined in a class with a no-args constructor. You can write your own no-args constructor if you like and it will be called by the Lambda runtime.
  6. The handler performs two operations — it generates a new String, including the input value, and writes this to standard out.

What's not here:

  1. There is no defined parent class (apart from Object implicitly) or interface. Lambda is an extremely lightweight model with respect to imposing type signatures. Optionally you can define a handler using a standard AWS library interface if you'd like, but that is not required. The lack of defined superclass is one of the reasons that Lambdas are very pleasant to unit test since when instantiating Lambda classes in your unit tests you don't need to consider any framework requirements.
  2. No imports are necessary when you create a Lambda. And that means no libraries (including no AWS libraries) are necessary either. Isn't that refreshing? There are optional libraries that you can import, and some of them are very useful, but you're not required to use any of them. We'll be talking about those that you can use later in the series.
  3. No public static void main() method. This is because you're not going to run your code, or even instantiate your class, directly. Instead the AWS Lambda environment will, completely abstracted from you, launch a JVM, instantiate your class and then call your handler method. You don't worry about the lifecycle of the JVM whatsoever.

Building and packaging

Now that we've written our code we need to create an artifact we can deploy. I'll admit at this point that the Javascript and Python folks are going to point and laugh at us because for them there is no build step — they can simply write source code straight into the Lambda console, at least to get started.

The good news for us though is that while we still need to create a deployment artifact it's a lot less heavyweight than you might be used to. We don't need to create any VM images or dockerfiles, or even any WAR or EAR files (hi there 2002!) . All we need to do is create a JAR file that contains our compiled code and our dependencies, a construct often referred to as an uberjar.

“But what dependencies?" I hear you ask. “You just said we didn't need any!" Congratulations — you've clearly been following along well! We don't have dependencies yet, but most real-world Java Lambdas will probably end up with some, and it's easy to set ourselves up to use dependencies now even while we don't have any.

As I mentioned in Part 1 we're going to use Maven as our build tool of choice, and you should make sure you have Maven setup as I described in the Prerequisites section of Part 1. Maven has its .. urrm .. ‘annoyances’ .. but for the sake of what we're doing here it works well enough. And a lot of folks use Maven now so at least it has a good amount of ubiquity.

Assuming you've now created a Java project with our source code from above (which should be in src/main/java/io/symphonia/SimplestLambda.java), you now need to create a pom.xml file in your project root. Copy and paste the below into this file:

Ugh, XML, I'm sorry. Just a quick explanation of what's going on here — first of all we have a bunch of namespace and maven metadata. Then we have Maven's standard groupID, artifactID and version stuff. It's not hugely important what goes here since we're not going to be publishing to a repository, but you should probably change theses values into something that makes more sense to you. And then we have some standard properties, two of which are for specifying use of Java 8.

The rest of the file is defining the one extra plugin we're going to use — Apache Maven Shade. Simply put when we run the mvn package command Maven will compile our code, and then Shade will package it into an uberjar. Try this out by running mvn package from the root directory of your project.

Near the bottom of the output you should see a line saying:

[INFO] BUILD SUCCESS

Now do jar tf target/learning-lambda-1.0-SNAPSHOT.jar __ (substituting learning-lambda for whatever you put in your pom.xml) . You should see one class file in the JAR, corresponding to your Lambda class. If you did — congratulations! We're now ready to move onto to deploying our Lambda.

Defining, deploying and configuring

Following the section above we now have a valid JVM Lambda JAR file and it's time to send it to Amazon. To do that we're going to need to do a lot of mouse clicking. There are tools that make this more automated for real-world use, but it's good to know what's going on under the covers. Apart from anything else this knowledge is useful for the times when those tools fail.

First of all you'll need to open the AWS Console. You can do that by navigating to https://console.aws.amazon.com in your browser, and it will redirect you to your default AWS region. If you want to use a different region do so, but for most of you the default should be fine.

Next you need to open the Lambda section of the console. AWS move these things around so you might see something slightly different, but right now I click on ‘Services’ top left, then ‘Lambda’ :

image

Since we're deploying our Lambda for the first time we need to create it in AWS. To start this click the friendly blue button:

image

On the next screen click ‘Blank Function’ :

image

The next screen is about configuring triggers, which we'll ignore for now, so just hit Next. And now we get onto our main configuration screen. In the top section give your Lambda a name, and select Java 8 :

image

In the Lambda function code section we need to actually upload (deploy!) our code. To do that, click the Upload button and find the JAR file we inspected earlier (target/learning-lambda-1.0-SNAPSHOT.jar).

Now scroll down to Lambda function and role section. In the Handler box type the package, class and method for your handler method in the form package.Class::method, e.g. for my code from earlier I enter io.symphonia.SimplestLambda::handler .

Now it starts getting a little tricky. AWS are sticklers for security configuration. It's rare you come across situations where a sensible default is fully assumed, and that's no different with Lambda. You have to specify for every Lambda what role it is running as. A role is (in Amazon's language) ‘an AWS identity with permission policies that determine what the identity can and cannot do in AWS’ . In other words we need to tell AWS what privileges we want our Lambda to have.

Assuming you haven't created any Lambdas before, we'll need to create one. To do this click the Role dropdown (1), then click Create a custom role (2):

image

This will open the Roles console in a new browser tab, and will fill in a default new role for you (these are sensible default details, but we need to explicitly tell AWS we're ok to use them):

image

This is what we want, so click Allow bottom right.

Now go back to the Lambda Definition screen (likely in a different browser tab) and it should look like this:

image

Our role is now setup, and the remaining settings on that screen will be fine as default, so just click Next. That takes you to the review screen — check the values look like what we just went through, then click Create Function. If you have a problem here you might not have permissions to create a Lambda function, in which case you may want to go check out the Prerequisites section of Part 1 again. If everything was successful you should now be a on a new console screen for your new Lambda. It's time to run it!

Running

Click the Test button:

image

You'll get an event template screen with a JSON map in it. We don't want anything nearly as detailed as that, but it does need to be valid JSON, so put whatever you like in quotes, e.g. "world" :

image

Then scroll down and hit Save and test. You'll go back to the previous screen and after 2 or 3 seconds the bottom section should look like this:

image

If it does, congratulations! You've successfully coded, built, deployed and run your first Lambda! Since we had a Lambda with a void return type there is no return value, which is why we see null in the result section. However we did write to System.out, and that text appears in the Log output section as shown.

The next question is what the heck just happened there? To find out read Part 3 here. To see the other articles in this series please visit the series home page. To be alerted about future installments subscribe to our newsletter, follow Symphonia on twitter @symphoniacloud, and our blog at The Symphonium.

Need help with Lambda, or other Serverless technologies? We're the experts! Contact us at Symphonia for expert advice, architectural review, training, and on-team development.