Sending emails to your application's clients or customers is a common enterprise use case. The emails usually contain invoices, reports, or confirmations for a given business transaction. With Java, we have a mature and robust API for this: The JavaMail API. The API standard has its own website providing official documentation and quickstart examples. It's part of the Java Standard Edition (Java SE) and Java Enterprise Edition (Java EE).
In this blog post, I'll show you how you can send an email with an attachment to an email address of your choice using this API and Java EE 8, MicroProfile 3.2, Payara 5.201, Java 8, Maven and Docker.
Let's get right into it.
Setting up the backend with Java EE and JavaMail
For the backend, I'm using a straightforward Java EE 8 Maven project:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <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>de.rieckpil.blog</groupId> <artifactId>java-ee-sending-mails</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.microprofile</groupId> <artifactId>microprofile</artifactId> <version>3.2</version> <type>pom</type> <scope>provided</scope> </dependency> <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>2.23.0</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>java-ee-sending-mails</finalName> </build> </project> |
To trigger the email transport, I'm using a JAX-RS endpoint (definitely no best practice, but good enough for this example):
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Path("mails") public class MailingResource { @Inject private MailingService mailingService; @GET public Response sendSimpleMessage() { mailingService.sendSimpleMail(); return Response.ok("Mail was successfully delivered").build(); } } |
The actual logic for creating and sending the email is inside the MailingService
EJB which is part of the JAX-RS class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | @Stateless public class MailingService { @Inject @ConfigProperty(name = "email") private String emailAddress; @Resource(name = "mail/localsmtp") private Session mailSession; public void sendSimpleMail() { Message simpleMail = new MimeMessage(mailSession); try { simpleMail.setSubject("Hello World from Java EE!"); simpleMail.setRecipient(Message.RecipientType.TO, new InternetAddress(emailAddress)); MimeMultipart mailContent = new MimeMultipart(); MimeBodyPart mailMessage = new MimeBodyPart(); mailMessage.setContent("<p>Take a look at the <b>scecretMessage.txt</b> file</p>", "text/html; charset=utf-8"); mailContent.addBodyPart(mailMessage); MimeBodyPart mailAttachment = new MimeBodyPart(); DataSource source = new ByteArrayDataSource("This is a secret message".getBytes(), "text/plain"); mailAttachment.setDataHandler(new DataHandler(source)); mailAttachment.setFileName("secretMessage.txt"); mailContent.addBodyPart(mailAttachment); simpleMail.setContent(mailContent); Transport.send(simpleMail); System.out.println("Message successfully send to: " + emailAddress); } catch (MessagingException e) { e.printStackTrace(); } } } |
First, the EJB requires an instance of the javax.mail.Session
class which is injected with @Resource
and found via its unique JNDI name. However, you can set up the connection for this email session with either the Payara admin web page or using asadmin
as you'll see in the next section.
The Session
object is then used to create a MimeMessage
instance which represents the actual email. Setting the email recipient and the subject of the email is pretty straightforward. In this example, I'm injecting the recipient's email address via the MicroProfile Config API with a microprofile-config.properties
file:
For both the attachment and for the email body I'm using a separate MimeBodyPart
instance and add both to the MimeMultipart
object. Finally, the email is sent via the static Transport.send(Message msg)
method via SMTP.
Providing an SMTP server and configure JavaMail
Next, for your real-world example, you would connect to your company's internal SMTP server to send the emails to e.g. your customers. To provide you a running example without using an external SMTP server I'm using a Docker container to start a local SMTP server. Therefore I combine the whole infrastructure (SMTP server and Java EE backend with JavaMail) in a simple docker-compose.yml
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 | version: '3' services: app: build: ./ ports: - "8080:8080" - "4848:4848" links: - smtp smtp: image: namshi/smtp ports: - "25:25" |
After that, the Docker container with the Payara application server can reach the SMTP server via its name smtp
and I don't need to hardcode any IP address.
Next, I configure the email session (connection settings and JNDI name) in Payara with a post-boot asadmin
script:
1 2 3 4 5 6 | # Connecting to the SMTP server within the Docker Compose environment create-javamail-resource --mailhost smtp --mailuser duke --fromaddress [email protected] mail/localsmtp # For a connecting to e.g. Gmail's SMTP you have to specify further parameters (check e.g. https://medium.com/@swhp/sending-email-with-payara-and-gmail-56b0b5d56882) deploy /opt/payara/deployments/java-ee-sending-mails.war |
For the sake of completeness, this is the Dockerfile for the backend:
1 2 3 4 | FROM payara/server-full:5.201 COPY create-mail-session.asadmin $CONFIG_DIR COPY target/java-ee-sending-mails.war $DEPLOY_DIR ENV POSTBOOT_COMMANDS $CONFIG_DIR/create-mail-session.asadmin |
You can find the source code alongside a docker-compose.yml
file to bootstrap the application and an SMTP server for local development on GitHub.
For more tutorials using the Payara application server and Java EE, have a look at the corresponding Java EE category.
Have fun sending emails with Java EE and the JavaMail spec,
Phil