Some years ago I needed to write a Java application that receives emails and process some business stuff on those. It wasn't an easy task as I didn't find a lot of information. Because of that I decided, one day, I'll write about it and maybe that could helps 🙂.
Full source code available here.
Context
Why ?
You would ask why do you need that ? I would say :
Business requirements can surprise you every day
Below a small list of some use cases when this incoming emails processing can be helpful.
- Testing : Can be good option for integration testing the email capabilities of your services/applications (The old FakeSMTP is a good GUI example)
- Analyse received emails and extract some specific information
- ...
What ?
SubEtha SMTP is low-level API for writing almost any kind of SMTP mail-receiving application.
To avoid you a long history presentation, the most updated version of SubEtha SMTP is a fork by David Moten (many thanks to him for his excellent work and keeping this library updated). We'll use this fork in this post.
I think no need to describe what's Spring Boot in nowadays.
Let's code
Before starting the code, I suggest to see how we can send an email via terminal. We'll need that to send an email to our future emails receiver application.
Send email with cURL
Below an example of sending email to specific mail server with parameters like from, recipient, auth and the content of the email.
curl --url 'smtp://localhost:25000' \
--mail-from 'customer-address@mail.com' \
--mail-rcpt 'info@marketing.company.com' \
--user 'superus3r:passw0rd' \
-T <(echo -e 'From: customer-address@mail.com\nTo: info@marketing.company.com\nSubject: cURL Test\n\nHello, this is the content')
SubEtha dependency
Here we add the maven dependency of SubEtha SMTP.
<dependency>
<groupId>com.github.davidmoten</groupId>
<artifactId>subethasmtp</artifactId>
<version>5.2.4</version>
</dependency>
Start the embedded SMTP server
It's very easy to start a simple SMTP server with the library. Just create/build an instance of SMTPServer with the right parameters like port and start this instance.
// Build the embedded SMTP server
SMTPServer smtpServer = SMTPServer.port(25000).build();
// Start server asynchronously
smtpServer.start();
Now our SMTP server is started but not fully ready to receive emails. We need to implement a MessageListener to extract important data like from and recipient addresses but also the subject and the body of the received email.
For that we can implement the SimpleMessageListener interface :
public class MarketingMailListener implements SimpleMessageListener {
private static final String MARKETING_DOMAIN = "@marketing.company.com";
/**
* Do some business logic here
*/
@Override
public boolean accept(String from, String recipient) {
return recipient != null && recipient.endsWith(MARKETING_DOMAIN);
}
@Override
public void deliver(String from, String recipient, InputStream data)
throws TooMuchDataException, IOException {
System.out.println("deliver message of MARKETING department");
System.out.println("From : "+from);
System.out.println("Recipient : "+recipient);
}
As you can see here, I added a simple business rule inside accept method to handle only emails sent to marketing department.
The accept method is responsible for defining if the received message will be proceeded by this listener or not.
The deliver method is responsible for extracting :
- From : The sender address
- Recipient : The receiver address
- Data : It's an InputStream type from which we can extract content and relevant information related to the received email like : subject, body, CC, attachments and else.
Let's see how we can extract the content of the email :
Convert to MimeMessage
We can retrieve content from multipart content-typed message using MimeMessage instance.
public MimeMessage convertToMimeMessage(InputStream data)
throws MessagingException {
Session session = Session.getDefaultInstance(new Properties());
try {
return new MimeMessage(session, data);
}catch (MessagingException e){
throw new MessagingException();
}
}
Extract email content
To extract the email content, we'll create an ReceivedEmail model to facilitate the processing.
public class ReceivedEmail {
private String subject;
private String senderAddress;
private String senderName;
private String recipientAddress;
private String recipientName;
private String cc;
private String bcc;
private String contentType;
private List<DataSource> attachments = new ArrayList();
// Getters and setters
// ...
}
For the sake of simplicity I used String for recipientAddress, CC and BCC but it's can be a list of addresses (example in the code).
We create dedicated service for email content extraction and the extract method could looks like :
public ReceivedEmail extractReceivedEmail(InputStream data) throws Exception {
ReceivedEmail receivedEmail = new ReceivedEmail();
MimeMessage message;
try {
message = this.convertToMimeMessage(data);
receivedEmail.setSubject(message.getSubject());
receivedEmail.setSenderAddress(InternetAddress.toString(message.getFrom()));
InternetAddress[] recipientAddresses = InternetAddress.parse(InternetAddress.toString(message.getAllRecipients()));
receivedEmail.setRecipientAddress(InternetAddress.toString(recipientAddresses));
receivedEmail.setRecipientName(recipientAddresses[0].getPersonal());
receivedEmail.setContentType(message.getContentType());
// Use here Apache library for parsing
MimeMessageParser messageParser = new MimeMessageParser(message);
messageParser.parse(); // very important to parse before getting data
receivedEmail.setCc(messageParser.getCc().toString());
receivedEmail.setBcc(messageParser.getBcc().toString());
receivedEmail.setAttachments(messageParser.getAttachmentList());
System.out.println(receivedEmail);
return receivedEmail;
} catch (Exception e) {
throw new Exception();
}
}
Now we have a full complete email receiver with minimal lines of code.
Secure SMTP Server
Let's go further step to see how we can secure our mail server.
First thing to do is to indicate that the authentication is required and implement basic auth with username and password.
this.smtpServer = SMTPServer
.port(25000)
.simpleMessageListener(marketingMsgListener)
.requireAuth(true) // auth required
.authenticationHandlerFactory(easyAuth) // implement type of auth
.build();
Different implementations of authenticationHandlerFactory can be done, I chose here, again for the sake of simplicity, the EasyAuthenticationHandlerFactory class which needs implementation of the UsernamePasswordValidator interface.
@Configuration
public class SimpleAuthValidatorImpl implements UsernamePasswordValidator {
private final String CREDENTIALS_LOGIN = "superus3r";
private final String CREDENTIALS_PASSWORD = "passw0rd";
@Override
public void login(String username, String password, MessageContext context) throws LoginFailedException {
if(CREDENTIALS_LOGIN.equals(username) && CREDENTIALS_PASSWORD.equals(password)){
System.out.println("Authenticated successfully");
}else{
System.err.println("Invalid authentication !");
throw new LoginFailedException();
}
}
}
And this how our SMTPServerConfig looks like :
@Configuration
public class SMTPServerConfig {
private final SMTPServer smtpServer;
private final SimpleMessageListener marketingMsgListener;
private final UsernamePasswordValidator authValidator;
private final EasyAuthenticationHandlerFactory easyAuth;
public SMTPServerConfig(SimpleMessageListener marketingMsgListener) {
authValidator = new SimpleAuthValidatorImpl();
easyAuth = new EasyAuthenticationHandlerFactory(authValidator);
this.marketingMsgListener = marketingMsgListener;
this.smtpServer = SMTPServer
.port(25000)
.simpleMessageListener(this.marketingMsgListener)
.requireAuth(true)
.authenticationHandlerFactory(easyAuth)
.build();
this.smtpServer.start();
}
}
Go deeper
More parameters can be customised to fit your needs and improve your SubEtha server, I list those below and you can try to explore them to improve your mail receiver implementation :
- Max connections and connection timeout
- TLS support
- Messages size
- Shutdown of the SMTP server
- Testing
Conclusion
I presented how we can, with small lines of code, write an application that can receive emails, handle those and apply some magic to them with SubEtha SMTP and Spring Boot.
If you have any questions or suggestions, please, leave your comments down below.
Thanks for reading and happy coding.
Resources
Source code : https://github.com/redamessoudi/subethasmtp-springboot.
Header photo by Liam Truong on Unsplash
Top comments (1)
Thank you for sharing this guide. The code snippets, explanations and overall structure make it brilliant for those interested in implementing similar functionality.
Btw, there's an option to send emails with SpringBoot using Mailtrap to gain more insights on deliverability than with JavaMail API all alone