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
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.
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.
Twitter
Facebook
Reddit
LinkedIn
Email