Outbound Email Service

Introduction

Up to this point we have been building on top of existing Spring Framework services to create out our application service infrastructure. We will now begin building our services based on the Spring Boot foundation alone. To get our feet wet, we will start by building the Outbound Email Service. This service provides the application with the ability to send emails messages.

The service acts as a facade to a third party email provider. In this example we will be connecting to Google GMail as our email provider. The service contains several core elements:

  • An email client that wraps the Spring's JavaMailSender utility.
  • A REST-based service endpoint.
  • A Message Queue Listener which provides a queue-based interface to the service. The Message Queue Listener monitors a designated message queue, retrieves queued outbound email messages, and calls the email client.

Requirements

Before you get started, you will need the following:
  • Java
  • Maven
  • Docker
  • Docker-Compose
Refer to the Development Toolbox article if you do not have these installed locally.

Additional Infrastructure

In addition to our existing infrastructure, this service will required an instance of RabbitMQ message broker. The RabbitMQ instance we will be using is provided as a container service from the official RabbitMQ Docker Hub repository, and will not need to be installed locally. To add the RabbitMQ instance, we will include the following into our Docker Compose file:

loading...

This will:

  • Pull the rabbitmq:3.5.3-management image from the official RabbitMQ Docker Hub repository.
  • configure its ports:
    • 5672- RabbitMQ Message Broker Service.
    • 15672- RabbitMQ browser-based Administration Service.
  • set RabbitMQ's default user credentials.
  • configure RabbitMQ to use the spring_ri_network Docker network.

Building the Outbound Email Service

In this service we will introduce several concepts that we will be using extensively in this, and future services:
  • Spring Service Components-The business logic of our application.
  • Spring Rest Controllers-These provide our external REST API, and connect to our Spring service's
  • Message Queue Listeners-Message Queue Listeners allow the application to listen to designated message queues, and then retrieve those queued messages for processing. This allows the service to integrate with other message-oriented services.
  • Internationalization (I18N)- Each REST service may return error messages. We will be using Spring's support for I18N to return error messages in multiple languages.

The source

Maven Pom.xml

loading...


In addition to many of the dependencies seen in previous articles, this project file includes several notable additions:

  • spring-cloud-stream-this dependency includes support for building messaging.
  • spring-cloud-stream-binder-rabbit-this dependency provides the stream binder for RabbitMQ.
  • springfox-swagger2-this dependency provides support for annotating and generating Swagger documentation form our REST controller.
  • springfox-swagger-ui-this dependency will use the generated Swagger document to generate a web-based user-interface we can use to interactively exercise the REST endpoint.

Configuration files

bootstrap.yml

loading...

This bootstrap.yml configures the application name, the remote configuration client, the default profiles and the service discovery client.

application.yml

loading...

Of note in this configuration file are:

  • Declaration of the RabbitMQ host, port and credentials.
  • Stream bindings to the EmailMessages queue and the emailMessageGroup. By configuring a group, we can instruct the message queue to remove the message from the queue when a copy it received by the service. In this manner, we can ensure multiple service instance consumers do not receive duplicate messages.q
  • Mail provider configuration parameters. In this instance we are connecting to a Gmail as our delivery provider. To authenticate to this service, you will of course need a valid Gmail account and will need to substitute your account credentials. You will also need to configure your Gmail account security to allow Less Secure Apps. To do this logon to Google Account Security and turn on the Less Secure app access feature.

    You can connect to any other Spring JavaMail supported provider, however, you will need to provide the appropriate configuration parameters for that provider.

Swagger configuration

This is the first service we will create in which we define our own REST endpoint. Since this endpoint acts as the service's public API it is important that we provide documentation for it's consuming clients. Rather than manually generate the documentation, we will be using Swagger & SpringFox to dynamically generate both the Swagger document and the corresponding interactive web interface. By generating the document and interface dynamically, we can ensure that the documentation and the Swagger UI are always in sync with the REST controller's implemented API.

The first step we must take is to provide a Swagger configuration class.

loading...

The class has three class-level annotations:

  • @Profile (value={"swagger"})-This Spring annotation instructs the framework to only load this class when the application is started with the swagger profile. This allows the Swagger features to be enabled/disabled at deployment time.
  • @Configuration-This Spring annotation marks the class as a configuration class so the framework will process it accordingly.
  • @EnableSwagger2-This SpringFox annotation enables Swagger

The class is then comprised of two methods:

  • productApi()-This method creates the Swagger2 Docket and instructs the Docket builder where to scan for controller classes and call the aspEndpointInfo() method. Additionally, we define all globalOperationParameters that will be added to every endpoint method. To support I18N, we include a global operation parameter in include the Accept-Language header.
  • apiEndpointInfo()-This method creates an ApiInfoBuilder that is used to generate the service endpoint information.
The SwaggerConfiguration class will be present in all of the services we build that exposes REST endpoints. The primary differences in the SwaggerConfiguration classes will be the basePackage value that will be scanned at runtime, and the details supplied to the ApiInfoBuilder to describe the endpoint.

We will continue our coverage of Swagger when we detail the service's REST controller.

Internationalization (I18N)

Modern applications often require support for multiple language. The Java language supports I18N through Locales and ResourceBundles. Spring extends Java's I18N support through the use of AcceptHeaderLocaleResolver which intercepts client requests, extracts the accept-language header and adds the corresponding locale's MessageSource to the context. In all of the services we will create that contain REST endpoints we will include the following Resolver class.

loading...

ResourceBundleMessageSource As the source of our messages, we will be using Java property files that contain a set of key-value pairs that correspond to the translated messages for a given locale. We create the following:

loading...


The ResourceBundleMessageSource will contain the set of key-value pairs representing the property file designated by the I18NLocaleResolver's BASE_I18N_RESOURCE_PATH value concatenated with an underscore ( _) and the locales two character code (e.g., en-english, de-german).

A default resource file named message.properties is create that contains every I18N key used by the service. If a given key is not found in the specified locale, the default resource file's value is used. If no locale is supplied to the call, the default resource value is used.

To perform the translation, simply call the I18NResourceBundle.translateForLocale( <message key>)

	    try{
	    // ... exception generating code
    } catch (MessagingException ex) {
        // translate the error message based on the context locale
     logger.error(I18NResourceBundle.translateForLocale("error.unable.to.send.mime.message"), ex);
    }

Here is an example of the English (message.properties) & German (message_de.properties) files.

loading...

loading...

Outbound Email models

The Outbound Email Service contains two models representing email messages. The first is a plain email message:

loading...

The second represents an email message with a list of attachment references.
loading...
It is important to note that the attachment array contains references to files stored within the OutboundEmailService's classpath. If the file exists, it will be added to the email message.

Services

We will be using Springs @Service mechanism which requires us to defines both an interface and implementation of the interface. This approach allows us to support multiple implementations of the interface, and select which one will run using runtime configuration parameters.

OutboundEmailService

The outbound email service can send simple email messages, or email messages with attachments. When using attachments, the incoming email message request contains an array of file references twithin the application classpath that map to the attachments. The service interface is defined as:

loading...

OutboundEmailServiceImpl

Here we see the implementation of the OutboundEmailService interface.

loading...

In the OutboundEmailServiceImpl we are using the I18NResourceBundle.translateForLocale to translate exceptions messages.

Resources

https://github.com/thinkmicroservices/email-outbound-service.git

Coming Up

In the next article we will complete the >b>OutboundEmailService by implementing its external API using a REST endpoint as well as a message-driven message queue listener. We also be exercising the REST endpoint with our service's dynamically generated Swagger UI, and publish messages to our services message queue using RabbitMQ's web administration interface. We are covering a lot of ground in both part 1 and 2 of this service. These elements will be reappearing in most of the reference implementation's future services.