This lab gives you experience with setting up an AWS (Amazon Web Services) Lambda with Java.
You will use a number of tools to develop this lab.
.jar
files: you will build a .jar
file to be uploaded to AWSAn important thing to know about IntelliJ is that it is likely to show errors in this lab that are not real errors. This means there may be red text you cannot make go away. See the discussion about IntelliJ showing errors in your code at the bottom of this writeup. IDEs try to give you useful information, but there are times they do not. That is a fact of life for all developers and all IDEs.
AWS Classroom is a training environment for using AWS. It is not a certification course; you would need to take specific courses to become a certified AWS developer. If you would like to explore these other courses, talk to your instructor. Our primary reason to use AWS Classroom is to provide access to their services without requiring students to use a credit card to create their own accounts. Be careful of how you use the resources in AWS Classroom; there are limits, and exceeding those limits results in additional work to complete labs.
Note: AWS Classroom is delivered through a Canvas shell. This is an entirely separate instance of Canvas than the one used at MSOE. Do not use your MSOE password with the AWS Classroom because that could give other parties access to your MSOE account.
You should have received an email telling you how to get into the classroom. If you have not, check your Junk Email folder, and contact your instructor if it is in neither place. When you get this email, create a new Canvas account with AWS. Use your MSOE email address to create this account, just not your MSOE password. If required, set up two-factor authentication through your email address. Once you are in AWS,
You should now be seeing the AWS Console with an image similar to
Feel free to explore a bit. Many resources are part of the free tier; you can explore these without any problems. Do talk to your instructor before starting paid resources. But there are many resources that are free for the first year that you can try out on your own.
Warning: Do not hit the ↺ Reset button. It takes up to an hour for a reset to complete, and it is designed for cases where you have a large number of resources to destroy. For this course, you can achieve the same thing by simply deleting any Lambdas you write. If you do hit the reset button, do not hit it multiple times; hitting it restarts the process and means you have to wait even longer before the reset is complete.
To deploy a Java function as a Lambda, you first create a .jar
file with a specific format. Your instructor will give you a starting point for this lab on GitHub Classroom. Use the provided link to clone your repository. The source code is also available in unicorn-locator.zip
.
One of the things you notice quickly is the deeply nested nature of the repositories; for example, the handler for this lab is in src/main/java/com/unicorn/location/UnicornPostLocationHandler.java
. These layers mean AWS Lambda was developed for working on much larger projects! Don’t worry about all of the levels, but also don’t make any changes to the structure. Most of your changes for this lab will be in UnicornPostLocationHandler.java
.
As mentioned above, this project builds using the Maven tool. Maven is similar to other build tools such as Make, CMake, and Ant. A key goal of Maven is to provide a simple, uniform build system, where “simple” means that users need not understand the details of the underlying mechanisms. To run the Maven builder, press the control key twice (quickly) in IntelliJ. This will pop up a “Run Anything” dialog. This dialog allows running commands that are not built in to IntelliJ. For this assignment, enter the command mvn package
. Visit the target/
folder; you should see a Jar file called UnicornLocationApi-1.1-jar-with-dependencies.jar
. This is your java “lambda” that you will upload to AWS. If you cannot find the file, get help from your instructor.
One reason this might fail is needing to download Java 17. Visit the project structure. If there is no Java 17 option for SDK, use the drop-down menu to Download JDK… and pick a Java 17 distribution. Then re-execute mvn package
.
Return to the AWS website that you opened earlier. If it is not open yet, click on the AWS link in the upper left corner.
As mentioned earlier, AWS has a very large number of services. The simplest way to access them is to search. Click on the search bar at the top of the screen (just under the URL line) and enter “lambda”. Click on the lambda option that appears. Then,
unicorn
. Don’t ask why. Apparently, all amazonians love unicorns.AWS is now ready for you to upload your Jar file.
Under the Code tab (where you start), and under the Code Source section, on the right, open the Upload from dropdown and select .zip or .jar file.
Within your IntelliJ project window, go into the target folder and find the UnicornLocationApi-1.1-jar-with-dependencies.jar that you built earlier. You can drag and drop this onto the .jar upload window you just opened.
In the Runtime Settings box (below the Code source box), click the “Edit” button on the right side. Set the Handler to
com.unicorn.location.UnicornPostLocationHandler::handleRequest
Note that
this is simply the fully qualified method
reference
for the handleRequest
method. If
you build your own IntelliJ project, folders may get compressed. In that case
the handler name may be slightly different such as adding main.java
at the start:
main.java.com.unicorn.location.UnicornPostLocationHandler::handleRequest
You are now ready to test. Click on the Test tab, then click the orange “Test” button. This test should pass and you should see a green box with the text “Executing function: succeeded”. Contact your instructor immediately if it does not. Clicking on the arrow in the box shows the detailed test response.
At the top of the Amazon Console, enter api gateway into the search bar. Click on the API Gateway option that pops up.
In the HTTP API option box, click the bright orange Build button. Name your API unicorn-api. Then click on Add integration button and select Lambda. Click on the box for Lambda function and select the unicorn lambda you created earlier. Click on Next.
From the “Configure routes” page, click Add route, select “ANY” for the method, and change the resource path to /add-unicorn-location
. The integration target is the lambda you created (unicorn). Finally, click on Next. This sets up an API Endpoint that you can access from the web.
For “Stage name”, leave the entry to $default
. Click Next.
On the Review and Create screen, click on Create.
On the pane on the left, under Deploy, click on Stages, and select the $default stage. Then, under Invoke URL, click on the URL that will look something like:
https://yszfheuqqvc.execute-api.us-east-1.amazonaws.com. Clicking on this brings up a new web page. Edit the URL at the top, adding your route at the end so the browser points to your API endpoint, something like
https://yszfhaiqvc.execute-api.us-east-1.amazonaws.com/add-unicorn-location
You should get the response from the URL at this point. It should simply say, “Hello, World! I’m a fresh new lambda!”, just like in your Java code you compiled earlier. If you do not, re-check the endpoint name to the end of the URL.
The following steps will have you modify UnicornPostLocationHandler.java
(the only Java source file in the provided code) to add logging. Logging is very useful for debugging your Lambda. Without logging, the only way to track down issues is to return additional information from the Lambda, and that is a very slow way to debug code. Logging gives you the equivalent of System.out.println()
at a console.
Add the libraries you need for logging to your pom.xml
file in the root of your repo:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.12</version>
</dependency>
That file contains a <dependencies>
tag already. Put the new dependency items between the <dependencies>
start tag and the </dependencies>
end tag. For brevity, the next time we say to put some new text between a start and end tag, we’ll say to insert it inside the <dependencies> ... </dependencies>
tag, meaning the same thing.
Whenever you change the Maven file (pom.xml
) from within IntelliJ (the
recommended method), Maven should pop up a button prompting you to reload
the file. Click on that button to reload the dependencies. If this does not
happen, go to IntelliJ’s File menu and select Repair IDE. This
forces re-indexing to recognize new classes.
Go to UnicornPostLocationHandler.java
and add an instance variable for logging:
private final Logger logger = LoggerFactory.getLogger(UnicornPostLocationHandler.class);
IntelliJ should offer to import the package. If it does not, import Logger
and LoggerFactory
from org.slf4j
.
Update handleRequest
to add logging:
public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
logger.info("Received a request here!");
return new APIGatewayProxyResponseEvent()
.withStatusCode(200)
.withBody("This is supposed to be the Unicorn Location API at some point!");
}
Rebuild by tapping the control key twice and running mvn package
again. Upload the new .jar to the lambda as you did before.
Retest the lambda through the web interface. It should still pass. At the bottom of the colorful pane telling you about the passed test, look at the Log output section. This section should match the print statements in your program, including any custom messages you slipped into the .info command above.
So far, your lambda simply prints the same response no matter what it is given as an input. To make the program respond to input, you will rewrite it to process JSON data. JSON stands for JavaScript Object Notation. In this notation, brackets ([
and ]
) are used to mark lists and braces ({
and }
) are used to mark objects. Values are comma-separated within the lists and objects. Using standard libraries, we can encode and decode text that is in JSON format. This means we can type simple text (as opposed to creating binary data) to write JSON data inputs.
Your next step is to extend your application to accept JSON data from the user. The provided code uses the Amazon API Gateway Proxy-Integration which embeds some additional information into the JSON received by the app so that we view the headers later, and not just the JSON body that the client sent. Insert the following directives into your pom.xml
file (between the <dependency> .. </dependency>
tags) to add the Jackson library:
<dependency>
<groupId>com.fasterxml.jackson.jr</groupId>
<artifactId>jackson-jr-objects</artifactId>
<version>2.17.0</version>
</dependency>
Next, create a new file called UnicornLocation.java
and paste the following code into it:
package com.unicorn.location;
public class UnicornLocation {
private String unicornName;
private String longitude;
private String latitude;
public String getUnicornName() {
return unicornName;
}
public void setUnicornName(String unicornName) {
this.unicornName = unicornName;
}
public String getLongitude() {
return longitude;
}
public void setLongitude(String longitude) {
this.longitude = longitude;
}
public String getLatitude() {
return latitude;
}
public void setLatitude(String latitude) {
this.latitude = latitude;
}
}
The Jackson and Java Beans libraries use this structure to generate and parse JSON objects. Exact names of get and set methods matter; the libraries use these names to determine what the fields for the JSON object, and if there are mismatches the libraries will throw exceptions.
Return to the UnicornPostLocationHandler.java
file and update the handler to
public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
try {
UnicornLocation unicornLocation = JSON.std.beanFrom(UnicornLocation.class, input.getBody());
return new APIGatewayProxyResponseEvent()
.withStatusCode(200)
.withBody("Received unicorn location: " + JSON.std.asString(unicornLocation));
} catch (Exception e) {
logger.error("Error while processing the request", e);
return new APIGatewayProxyResponseEvent()
.withStatusCode(400)
.withBody("Error processing the request");
}
}
The key bit in this is JSON.std.beanFrom
. This is the call to the Beans library to generate an instance of the UnicornLocation
class. You could write code to parse the JSON text directly, but that would provide less error checking and generally be less robust. The call to JSON.std
(to turn the location object into a JSON object that can be written as a string) uses the Jackson library. Add
import com.fasterxml.jackson.jr.ob.JSON;
to your code.
Now, rebuild and deploy your jar as above. If it does not build, double-check that you have just the Jackson JSON library; delete any imports for other JSON libraries.
Test your code through the AWS interface. The should pass, but it will show an error code 400 because the application is expecting requests from the API Gateway. Return to the $default
stage of your API’s gateway, to that URL you first got working that looked something like https://yszfhaiqvc.execute-api.us-east-1.amazonaws.com/add-unicorn-location
. Refresh the link. It should show the same 400 error code you saw a moment ago.
Next, download Postman and install it. Launch the app. Note you do not need to create a postman account for this lab. Within Postman, Use POST as the method, set the URL to the gateway URL (such as https://yszvc.execute-api.us-east-1.amazonaws.com/add-unicorn-location
), and set the body format to be raw. Enter the body
{
"unicornName": "Richard",
"longitude": "13.404954",
"latitude": "52.520008"
}
Clicking Send should return the response
Received unicorn location: {"latitude":"52.520008","longitude":"13.404954","unicornName":"Richard"}
You now have a lambda that turns JSON into a Java object and back.
Be sure to commit and push your code to the assignment repository. Your instructor may have additional submission requirements. See Canvas.
You may need to demonstrate this lab to your instructor. In any case, please do not reset your resources for the lab. All these resources fall within the free tier, so completing this lab is unlikely to use many Amazon credits. Leaving them in place allows your instructor to investigate problems or to grade your solution later. Change the data to use your own name (first and last name) for the unicorn’s name and set the last 4 digits of the lattitude and longitude to the time and minute of your test using a 24-hour clock (such as 1435 for 2:35 pm). This confirms your code is actually working.
If the writeup calls for you to capture a screen shot of your solution, do it with PostMan. Copy the appropriate endpoint into the URL window, set the JSON input to the values described in the previous paragraph, change the action to a GET, and click Send. This is important. It documents whose submission this is, and gives us confidence that your Lambda is running. Do not copy the full window, just the portion of the PostMan window that includes the URL, the Body, and the returned response. We do not need to see your history or other windows. Remember that when we are grading in Canvas, we see your output in a small window, so if you capture your full screen then the text gets compressed to a very small size.
A collection of issues and possible solutions:
I’m having difficulties getting into AWS Classroom: Start with your email from AWS inviting you to the classroom. You may need to check your junk mail folder. When you click on the button to accept the invitation, it gives you a choice based on having a Canvas account and creating a new account. What they mean to ask is whether you have an AWS Canvas account already, and the answer is most certainly “no”. Remember that the AWS Canvas is completely separate from the MSOE Canvas, so having an account on the Canvas system at MSOE does not mean you have a Canvas account with AWS. Click on the create new account button.
AWS console will not start up:
Check the top of your browser page for options to allow the browser to open new windows. AWS Learner Lab opens the console in a new browser window, and most browsers are set to require permission to open the new window. Give it the permission.
Consider changing browsers. Browsers that have worked include Firefox, Chrome, and Edge.
Ensure you have popups enabled for the AWS Console site. Your browser should prompt you for permission to open the console, but if it does not you may need to research how to allow it.
When I click on the Test button, I get a message indicating it failed: Try clicking on the Amazon Q helper button that appears; it seems this gives useful diagnostics. If that does not help, talk to your instructor.
IntelliJ shows errors in my code: Do not trust IntelliJ to identify
errors. It often marks things as incorrect that are perfectly fine, and its
corrections for the code in this lab are suspect. Really! IDEs are built
on top of various tools, and if the tool’s output changes slightly the IDE
often displays incorrect information. “Get rid of red markings” is never the
goal; the goal is to get running code. Look for a tiny “m” from Maven; its
suggestions are much more helpful. The ultimate answer is to exit IntelliJ,
reenter, and rebuild with mvn package
. Error messages from mvn
are actual
errors.
I ran mvn package
and still see lots of errors: make sure the pom.xml
file is in the top level of the project. Several students found it was moved to the src
subfolder, and the mvn
command cannot find the file in that case.
In spite of the previous steps, I am still getting errors from mvn
package
: go to IntelliJ’s File menu and select Repair IDE to
force re-indexing so new dependencies are recognized.
This lab is derived from a workshop created by Amazon; you can also review its github repository. The lab was written by J. Yoder and B. Lewis and modified by R. Hasker.