How to create dynamic content using AWS Lambda?
Tags: aws, java, lambda, cloud
Published: 2020-10-21 03:44:19

Hello Everyone! Today we are going to see how you can generate dynamic content in  Lamdba. We are going to use Java 8, Apache Maven, AWS SDK for Java, Apache Velocity to achieve this.

## Uses Cases

There are several scenarios where you want to create dynamic content. For example, to create an HTML page response for an API method instead of returning just data in JSON/ XML. The other scenario is to generate a file or message with dynamic content. For instance, create welcome letters for your customers.

## Let's Dive In

We are going to create welcome letters for our credit card customers. The letter's template is :

``` Template

Hello ${name},

Say hi to your new credit card!
Your credit card number is ${creditCardNumber}.

Regards,
Your Credit Company

``` 
Lambda would populate `name` and `credit card number`. The generated file is uploaded to S3 for further processing like printing and mailing.

#### STEP 1:
Create an empty maven project and update pom to the following contents:

```XML

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>net.rajanpanchal</groupId>
	<artifactId>lambda-java</artifactId>
	<version>1</version>
	<name>Dynamic-content-Lambda</name>
	<description>Generate Dynamic content in Lambda</description>
	<properties>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<aws.java.sdk.version>2.14.11</aws.java.sdk.version>
	</properties>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>software.amazon.awssdk</groupId>
				<artifactId>bom</artifactId>
				<version>${aws.java.sdk.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<dependencies>
		<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3 -->
		<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>aws-java-sdk-s3</artifactId>
			<version>1.11.865</version>
		</dependency>

		<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>aws-lambda-java-core</artifactId>
			<version>1.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity-engine-parent</artifactId>
			<version>2.2</version>
			<type>pom</type>
		</dependency>
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity-tools</artifactId>
			<version>2.0</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${maven.compiler.source}</source>
					<target>${maven.compiler.target}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>3.2.4</version>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>
```
We are adding AWS dependencies and apache velocity dependencies. The `maven-shade-plugin` to create a fat jar.

#### STEP 2:
We will have our template, discussed earlier, in the `resources` folder (`src/main/resources`) with the name `welcomeLetter.vm`. 

#### STEP 3:
Create a class file with the following content:

``` Java
package net.rajanpanchal.handlers;

import java.io.File;
import java.io.FileWriter;
import java.util.Map;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;

public class WelcomeLetterGeneratorHandler implements RequestHandler<Map<String, String>, String> {
	Regions clientRegion = Regions.US_EAST_1;
	String fileObjKeyName = "welcome_letter.txt";
	String bucketName = "welcomelettersbucket";
	static VelocityContext vContext;
	static Template t;
	AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(clientRegion).build();
	static {
		try {
			// Create a new Velocity Engine
			VelocityEngine velocityEngine = new VelocityEngine();
			// Set properties that allow reading vm file from classpath.
			Properties p = new Properties();
			velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file,class");
			velocityEngine.setProperty("class.resource.loader.class",
					"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
			velocityEngine.init(p);
			t = velocityEngine.getTemplate("welcomeLetter.vm");
			vContext = new VelocityContext();

		} catch (Exception e) {
			throw new RuntimeException(e);

		}
	}

	public String handleRequest(Map<String, String> event, Context context) {
		String response = null;
		LambdaLogger logger = context.getLogger();
		try {
			// Add data to velocity context
			vContext.put("name", "Rajan");
			vContext.put("creditCardNumber", "1234 5678 9012 3456");
			File f = new File("/tmp/"+fileObjKeyName);

			FileWriter writer = new FileWriter(f);
			// merge template and Data
			t.merge(vContext, writer);
			writer.flush();
			writer.close();

			// Upload a file as a new object with ContentType and title specified.
			PutObjectRequest request = new PutObjectRequest(bucketName, fileObjKeyName, f);
			ObjectMetadata metadata = new ObjectMetadata();
			metadata.setContentType("plain/text");
			metadata.addUserMetadata("title", "Welcome Letter");
			request.setMetadata(metadata);
			s3Client.putObject(request);
			response  = new String("Success");
		} catch (Exception ex) {
			response =  new String("Error:"+ex.getMessage());
		}

		return response;
	}
}
```
In the `static` block we are creating a velocity engine, setting resource loaders for the engine ('class' and 'file' resource loaders). Then we get the template and create a context to hold data to populate on the template.

In Lambda handler `handleRequest`, we add data like name and credit card number to the context, and using a FileWriter object we merge the template and the data. Note that we create the file in `/tmp/` directory in the Lambda execution environment. Lambda provides some disk space to the function to use. Then we upload this file to the S3 bucket (`welcomelettersbucket`) with the filename `welcome_letter.txt`. My bucket exists in US-EAST-1 as indicated by the variable `clientRegion`.

#### STEP 4
Now execute maven `package` command to generate the fat jar with all dependencies. 
A jar would be generated in the **target** folder.

#### STEP 5:
Go to AWS Console, create a Lambda function with runtime Java 8, let it create a default execution role. Upload the jar under *Function Code* section. Edit *Basic Settings* and add our handler package and method name

```
net.rajanpanchal.handlers.WelcomeLetterGeneratorHandler::handleRequest
```
#### STEP 6:
Add S3 access policy to the Lambda execution role.  Click on *Permissions* tab - > *Execution Role* hyperlink. It will take you to the IAM Role. For simplicity, I added   **AmazonS3FullAccess** permission, but you can add fine-grain S3 access.

#### STEP 7:
Go back to lambda and create Test event. Lambda executed successfully.
![AWS Lamdba Dynamic Content](https://s3.amazonaws.com/blog-images-rajanpanchal.net/Lambda-dynamic-content/success.PNG)

Check S3 bucket, the file is saved.
![AWS Lambda Dynamic Content](https://s3.amazonaws.com/blog-images-rajanpanchal.net/Lambda-dynamic-content/file_generated.PNG)

![AWS Lambda Dynamic Content](https://s3.amazonaws.com/blog-images-rajanpanchal.net/Lambda-dynamic-content/file_content.PNG)

The file is ready for further processing!

#### Source Code:

[![AWS Lambda dynamic content](https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&repo=Lambda-dynamic-content)](https://github.com/rajanpanchal/Lambda-dynamic-content)

### Conclusion

It's super easy to create dynamic content in Lambda using Apache Velocity and Java SDK. In addition, using lambda you only pay for what you use.   Let me know your thoughts on the article in the comments and how you are going to use this?

*If you like the post, feel free to share and follow me on [Twitter](https://twitter.com/PanchalRajan) for updates!*