AWS Lambda Java: Sending S3 event notification email using SES – Part 2

By October 12, 2020 October 14th, 2020 Powerlearnings

Written by Tejaswee Das, Software Engineer, Powerupcloud Technologies

Collaborator: Neenu Jose, Senior Software Engineer

Introduction

In the first part of this series, we have discussed in-depth about creating a Lambda deployment package for Java 8/11 using Maven in Eclipse & S3 event triggers. know more here

In this post, we will showcase how we can send emails using AWS Simple Email Service (SES) with S3 Event triggers in Java.

Use Case

One of our clients had their workloads running on Azure Cloud. They had few serverless applications in Java 8 in Azure Functions. They wanted to upgrade Java from Java 8 to Java 11. Since Java 11 was not supported (Java 11 for Azure Functions has recently been released in Preview), they wanted to try out other cloud services – that’s when AWS Lambda was the one to come into the picture. We did a POC feasibility check for Java 11 applications running on AWS Lambda. 

Step 1:

Make sure you are following Part 1 of this series. This is a continuation of the first part, so it will be difficult to follow Part 2 separately.

Step 2:

Add SES Email Addresses

Restrictions are added to all SES accounts to prevent fraud and abuse. For this reason, for all test emails that you intend to use, you will have to add both the sender & receiver email addresses to SES, which by default is placed in SES sandbox.

2.1 To add email addresses, go to AWS Console → Services → Customer Engagement → Simple Email Service (SES)

2.2  SES Home → Email Addresses → Verify a New Email Address

2.3 Add Addresses to be verified

2.4 A verification email is sent to the added email address

2.5 Until the email address is verified, it cannot be used to send or receive emails. Status shown in SES is pending verification (resend)

2.6 Go to your email client inbox and click on the URL to authorize your email address

2.7 On successful verification, we can check the new status in SES Home, status verified.

Step 3:

In the pom.xml add the below Maven dependencies. To use SES, we will require aws-java-sdk-ses

Below in our pom.xml file for reference

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.amazonaws.lambda</groupId>
  <artifactId>demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.6.0</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
          <forceJavacCompilerUse>true</forceJavacCompilerUse>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.0.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-bom</artifactId>
        <version>1.11.256</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>3.3.3</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-java-sdk-s3</artifactId>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-events</artifactId>
      <version>1.3.0</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-core</artifactId>
      <version>1.1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-ses -->
<dependency>
     <groupId>com.amazonaws</groupId>
     <artifactId>aws-java-sdk-ses</artifactId>
     <version>1.11.256</version><!--$NO-MVN-MAN-VER$-->
     <scope>compile</scope>
   </dependency>
  </dependencies>
</project>

Step 4:

Edit your LambdaFunctionHandler.java file with the latest code

4.1 Add email components as string

final String FROM = "neenu.j@powerupcloud.com";
final String TO = "neenu.j@powerupcloud.com";
final String SUBJECT = "Upload Successful";
final String HTMLBODY = key+" has been successfully uploaded to "+bucket;
final String TEXTBODY = "This email was sent through Amazon SES using the AWS SDK for Java.";

4.2 Create SES client

AmazonSimpleEmailService client = AmazonSimpleEmailServiceClientBuilder.standard()
                // Replace US_WEST_2 with the AWS Region you're using for
                // Amazon SES.
                  .withRegion(Regions.US_EAST_1).build();

4.3 Send email using SendEmailRequest

SendEmailRequest request = new SendEmailRequest()
                .withDestination(
                    new Destination().withToAddresses(TO))
                .withMessage(new Message()
                    .withBody(new Body()
                        .withHtml(new Content()
                            .withCharset("UTF-8").withData(HTMLBODY))
                        .withText(new Content()
                            .withCharset("UTF-8").withData(TEXTBODY)))
                    .withSubject(new Content()
                        .withCharset("UTF-8").withData(SUBJECT)))
                .withSource(FROM);
            client.sendEmail(request);

You can refer the complete code below

package com.amazonaws.lambda.demo;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.S3Object; 
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder;
import com.amazonaws.services.simpleemail.model.Body;
import com.amazonaws.services.simpleemail.model.Content;
import com.amazonaws.services.simpleemail.model.Destination;
import com.amazonaws.services.simpleemail.model.Message;
import com.amazonaws.services.simpleemail.model.SendEmailRequest;


public class LambdaFunctionHandler implements RequestHandler<S3Event, String> {

    private AmazonS3 s3 = AmazonS3ClientBuilder.standard()
    		.withRegion(Regions.US_EAST_1)
    		.build();

    public LambdaFunctionHandler() {}

    // Test purpose only.
    LambdaFunctionHandler(AmazonS3 s3) {
        this.s3 = s3;
    }

    @Override
    public String handleRequest(S3Event event, Context context) {
        context.getLogger().log("Received event: " + event);

        // Get the object from the event and show its content type
        String bucket = event.getRecords().get(0).getS3().getBucket().getName();
        String key = event.getRecords().get(0).getS3().getObject().getKey();
        final String FROM = "neenu.j@powerupcloud.com";
        final String TO = "neenu.j@powerupcloud.com";
        final String SUBJECT = "Upload Successful";
        final String HTMLBODY = key+" has been successfully uploaded to "+bucket;
        	            	     

        final String TEXTBODY = "This email was sent through Amazon SES "
        	      + "using the AWS SDK for Java.";
        try {
            AmazonSimpleEmailService client = 
                AmazonSimpleEmailServiceClientBuilder.standard()
                // Replace US_WEST_2 with the AWS Region you're using for
                // Amazon SES.
                  .withRegion(Regions.US_EAST_1).build();
            SendEmailRequest request = new SendEmailRequest()
                .withDestination(
                    new Destination().withToAddresses(TO))
                .withMessage(new Message()
                    .withBody(new Body()
                        .withHtml(new Content()
                            .withCharset("UTF-8").withData(HTMLBODY))
                        .withText(new Content()
                            .withCharset("UTF-8").withData(TEXTBODY)))
                    .withSubject(new Content()
                        .withCharset("UTF-8").withData(SUBJECT)))
                .withSource(FROM);
                // Comment or remove the next line if you are not using a
                // configuration set
               // .withConfigurationSetName(CONFIGSET);
            client.sendEmail(request);
            System.out.println("Email sent!");
          } catch (Exception ex) {
            System.out.println("The email was not sent. Error message: " 
                + ex.getMessage());
          }
       
       context.getLogger().log("Filename: " + key);
        try {
            S3Object response = s3.getObject(new GetObjectRequest(bucket, key));
            String contentType = response.getObjectMetadata().getContentType();
            context.getLogger().log("CONTENT TYPE: " + contentType);
            return contentType;
        } catch (Exception e) {
            e.printStackTrace();
            context.getLogger().log(String.format(
                "Error getting object %s from bucket %s. Make sure they exist and"
                + " your bucket is in the same region as this function.", key, bucket));
            throw e;
        }
    }
}

Step 5:

Build the updated project and upload it to Lambda. Refer to Step 5 (https://www.powerupcloud.com/aws-lambda-java-creating-deployment-package-for-java-8-11-using-maven-in-eclipse-part-1/)

Step 6:

To test this deployment. Upload yet another new file to your bucket. Refer Step 9 of blog Part 1.

On successful upload, SES sends an email with the details. Sample screenshot below.

Conclusion

S3 event notifications can be used for a variety of use-case scenarios. We have tried to showcase just one simple case. This can be used to monitor incoming files & objects into a S3 bucket and appropriate actions & transformations.

Hope this was informative. Do leave you comments for any questions.

References

https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html

Leave a Reply