Send emails with Oracle Cloud Infrastructure (OCI) Email Delivery using the Micronaut framework
About this workshop
This workshop is for Java developers looking to build portable, cloud-native Java applications using Micronaut, GraalVM Native Image, and Email Delivery service in Oracle Cloud Infrastructure (OCI). In this lab, you'll learn how to send emails using OCI Email Delivery and Micronaut.
Lab Contents
In this lab, you will:
- Connect VS Code to a remote development environment in an OCI Compute Instance
- Review the application source code developed using the Micronaut framework
- Configure OCI Email Delivery:
- generate SMTP credentials
- add an Approved Sender
- use the SMTP Endpoint for your OCI region
- Build a native executable for the application using GraalVM Native Image
- Send emails from your application using OCI Email Delivery
Estimated lab time: 30-45 minutes
NOTES:
Whenever you see the laptop icon you'll need to perform an action such as entering a command or editing a file.
# The box under the icon will tell you what to do.
To copy a command, hover over the field and then click the copy to clipboard icon.
To paste a copied command in a terminal window, right click and select the
Paste
option from the context menu. If you prefer keyboard shortcuts instead, useCTRL+SHIFT+V
.
Introduction
What is Micronaut?
Micronaut is a modern, JVM-based framework to build modular, easily testable microservice and serverless applications. By avoiding runtime reflection in favor of annotation processing, Micronaut improves the Java-based development experience by detecting errors at compile time instead of runtime, and improves Java-based application start time and memory footprint. Micronaut includes a persistence framework called Micronaut Data that precomputes your SQL queries at compilation time making it a great fit for working with databases like MySQL, Oracle Autonomous Database, etc.
What is GraalVM Native Image?
GraalVM is a high-performance JDK distribution that can accelerate any Java workload running on the HotSpot JVM. GraalVM Native Image ’s ahead-of-time compilation provides a whole new way to deploy Java applications ideal for containerization. At build time, GraalVM Native Image analyzes a Java application and its dependencies to identify just what classes, methods, and fields are absolutely necessary and generates optimized machine code for just those elements.
Micronaut uses GraalVM Native Image to build lightweight Java applications that use less memory and CPUs, are smaller and faster because of an advanced ahead-of-time compilation technology.
GraalVM Enterprise is available at no additional cost on Oracle Cloud Infrastructure (OCI)
STEP 1: Connect VS Code to a remote OCI Compute Instance
Your development environment is provided on a remote OCI Compute Instance. In this section, you will connect VS Code from the Luna Desktop environment to the remote machine by running a setup script.
Double-click the Luna Lab icon on the desktop to open the browser.
After the required OCI network and compute resources are provisioned (usually in 2-3 minutes), the animated gear beside Resources turns into a checkmark, and you can proceed with the lab.
Click Resources.
Scroll down to Configure and copy the text from the STEP 1.4 - Open VSCode and Connect text box. You many need to click on
View Details
. This script will setup the environment needed for the lab and launch VS Code. You can use the Copy to clipboard button on the far right that appears when you hover over the box.Click the Applications menu and open a Terminal Emulator.
Place your cursor in the Terminal window and paste the lab init script you copied (Shift+Ctrl+V). A dialog box will warn you that you're pasting multiple lines which looks suspicious, but click Paste to proceed.
A VS Code window will open and automatically connect to the remote VM instance that has been provisioned for you. Click Continue to accept the machine fingerprint.
The green box at the lower left corner of VS Code will display
SSH: <REMOTE VM IP ADDRESS>
to indicate you are connected over SSH.
Congratulations, your VS Code has successfully connected to a remote host in Oracle Cloud Infrastructure!
STEP 2: Review the sample application source code
In this section, we will review the sample Micronaut application code
used in this lab. The application source code and build scripts are
available in the Lab
folder in VS Code.
Let's go through the main components of the application.
2.1 Dependencies
We've added three dependencies to the build to add email support:
micronaut-email-javamail
is mandatorymicronaut-email-template
andmicronaut-views-thymeleaf
are optional. We've included these two libraries because we are using email templates in this example.
pom.xml
<dependency>
<groupId>io.micronaut.email</groupId>
<artifactId>micronaut-email-javamail</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.email</groupId>
<artifactId>micronaut-email-template</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.views</groupId>
<artifactId>micronaut-views-thymeleaf</artifactId>
<scope>compile</scope>
</dependency>
2.2 Email sender configuration
We've added the following configuration snippet to application.yml
. By using placeholder variables like FROM_EMAIL
and FROM_NAME
in application.yml
, we can externalize the values via environment variables and avoid hard-coding.
src/main/resources/application.yml
micronaut:
email:
from:
email: ${FROM_EMAIL:`default@test.com`} # <1>
name: ${FROM_NAME:'Default Sender'} # <2>
- Sender’s email
- Sender’s name
Note: This way property: ${<placeholder>:<default-value>}
of defining configuration enables us to externalize the configuration easily. For example, if a configuration placeholder (<placeholder>
) like FROM_NAME
is not defined, the Micronaut framework will use the default value (<default-value>
) specified. So if FROM_NAME
is not specified, the Micronaut framework sets the value of name
to Default Sender
.
2.3 SMTP configuration
We've added the following snippet to application.yml
to supply the SMTP credentials. This approach lets us externalize the values via environment variables or secure storage such as OCI Vault.
src/main/resources/application.yml
smtp:
password: ${SMTP_PASSWORD:'default-pass'} # <1>
user: ${SMTP_USER:'default-user'} # <2>
- the SMTP password
- the SMTP username
2.4 JavaMail properties configuration
We've added the following snippet to application.yml
to supply JavaMail properties:
src/main/resources/application.yml
javamail:
properties:
mail:
smtp:
port: 587
auth: true
starttls:
enable: true
host: ${SMTP_HOST:'default-smtp.com'} # <1>
- the SMTP server
2.5 Session Provider
Micronaut Email requires a bean of type SessionProvider
when using JavaMail to create a Session
. We've created the OciSessionProvider
class for this.
src/main/java/example/micronaut/OciSessionProvider.java
- Use
jakarta.inject.Singleton
to designate a class as a singleton.
- Annotate a constructor parameter with
@Property
to inject a configuration value. We've injected SMTP configuration via constructor paramters annotated with@Property
. We could have used a POJO annotated with@ConfigurationProperties
as well.
- Use the
user
andpassword
to create theSession
.
2.6 Controller
We've created a controller EmailController
that uses the Micronaut EmailSender
to send emails.
src/main/java/example/micronaut/EmailController.java
It is critical that any blocking I/O operations (such as fetching the data from the database) are offloaded to a separate thread pool that does not block the Event loop.
The class is defined as a controller with the
@Controller
annotation mapped to the path/email
.
- Use constructor injection to inject a bean of type
EmailSender
.
By default, a Micronaut response uses
application/json
asContent-Type
. We are returning a String, not a JSON object, so we set it totext/plain
.We're sending a plain-text email.
- To send an HTML email, we're leveraging Micronaut's template rendering capabilities.
A Micronaut controller action consumes
application/json
by default. Consuming other content types is supported with the@Consumes
annotation or theconsumes
member of any HTTP method annotation.We're sending an email with an attachment.
STEP 3: Configure OCI Email Delivery
In this section, you will configure OCI Email Delivery.
3.1 Create SMTP credentials
Go to the Luna Lab page opened in the browser, and click Resources.
Scroll down to Configure and copy the text from the STEP 3.1 - Create SMTP credentials text box. You many need to click on
View Details
. You can use the Copy to clipboard button on the far right that appears when you hover over the box.Open a new terminal in VS Code using the Terminal>New Terminal menu and paste (Shift+Ctrl+V) the copied script.
The response should look like this:
{ "data": { "description": "mn-email-user smtp credentials", "id": "ocid1.credential.oc1..aaaaaaaal...", "lifecycle-state": "ACTIVE", "password": "nB$O.......", "user-id": "ocid1.user.oc1..aaaaaaaaqx...", "username": "ocid1.user.oc1..aaaa....me.com" } }
Set the value of
password
from the response in the environment variableSMTP_PASSWORD
.Replace
nB$O.......
with the actual value. Enclose the value in single quotes' '
(instead of double quotes" "
) as shown below to handle special characters.export SMTP_PASSWORD='nB$O;.......'
Check the value set by running the following command:
echo $SMTP_PASSWORD
Set the value of
username
from the response in the environment variableSMTP_USER
.Replace
ocid1.user.oc1..aaaa....me.com
with the actual value. Enclose the value in single quotes' '
(instead of double quotes" "
) as shown below to handle special characters.export SMTP_USER='ocid1.user.oc1..aaaa....me.com'
Check the value set by running the following command:
echo $SMTP_USER
Note: In this example, we've used environment variables to store the SMTP user and password values for simplicity. Note that storing secrets in a vault provides greater security than you might achieve by storing secrets elsewhere, such as in configuration files or environment variables.
3.2 Create an approved sender
Go to the Luna Lab page opened in the browser, and click Resources.
Scroll down to Configure and copy the text from the STEP 3.2 - Create an approved sender text box. You many need to click on
View Details
. You can use the Copy to clipboard button on the far right that appears when you hover over the box.In the same terminal in VS Code, paste (Shift+Ctrl+V) the copied script.
This will create an approved email sender in your lab compartment.
3.3 Set email configuration environment variables
Go to the Luna Lab page opened in the browser, and click Resources.
Scroll down to Configure and copy the text from the STEP 3.3 - Set email configuration environment variables text box. You many need to click on
View Details
. You can use the Copy to clipboard button on the far right that appears when you hover over the box.In the same terminal in VS Code, paste (Shift+Ctrl+V) the copied script. A dialog box will ask you Are you sure you want to paste 4 lines of test in to the terminal?. Check the Do not ask me again checkbox and press Paste to proceed.
This will create three environment variables
FROM_EMAIL
,FROM_NAME
andSMTP_HOST
.
3.4 Change the "to" email address
In this step, you'll change the "to" email address to your personal email address so that you can verify the emails sent by the application in subsequent steps.
Go to
EmailController.java
and replacerecipient@domain.com
with your personal email address. Specify a valid personal email address where you can see the emails sent by this application in the next section.src/main/java/example/micronaut/EmailController.java
class EmailController { ... private final String toEmail = "recipient@domain.com"; ...
Save the file.
Congratulations! In this section, you configured the application to use OCI Email Delivery. In the next section, you'll learn how to build and run the application code and send emails.
STEP 4: Build and run the sample application code
In this section, you'll build and run the application code and send emails.
In the same terminal in VS Code, run the
./mvnw mn:run
command, which builds and starts the application on port 8080. Let's run it in the background by adding an&
../mvnw mn:run &
Send a simple plain-text email:
curl -X POST localhost:8080/email/basic
Check your email. You should see the basic email in your Inbox or Spam folder.
Send a templated email:
curl -X POST localhost:8080/email/template/test
Check your email. You should see the template email in your Inbox or Spam folder.
Send an email with an attachment.
curl -X POST \ -H "Content-Type: multipart/form-data" \ -F "file=@ README.md" \ localhost:8080/email/attachment
Check your email. You should see the attachment email in your Inbox or Spam folder.
Bring the running application in the foreground:
fg
Once the application is running in the foreground, press
CTRL+C
to stop it.
Congratulations! Your Micronaut application is now using OCI Email Delivery to send emails. In the next section, you'll learn how to generate a native executable and send emails using the native executable.
STEP 5: Generate a native executable for the application
In this section, you'll generate a native executable for the application and run the native executable to send emails.
For this, you will use GraalVM Native Image ’s ahead-of-time compilation to generate a native executable for our sample application.
At build time, GraalVM Native Image analyzes a Java application and its dependencies to identify just what classes, methods, and fields are absolutely necessary, and generates optimized machine code for just these elements.
Compiling applications ahead of time with GraalVM Native Image improves the startup time and reduces the memory footprint of JVM-based applications.
In the same terminal in VS Code, check the version of the GraalVM Enterprise native-image utility:
native-image --version
To generate a native executable using Maven, run:
./mvnw package -Dpackaging=native-image
It can take ~3-4 minutes to generate the native executable.
The native executable is created in the
target
directory and can be run withtarget/micronautguide
. Let's run it in the background by adding an&
.The native executable starts in just ~15 ms.
target/micronautguide &
Send a simple plain-text email:
curl -X POST localhost:8080/email/basic
Check your email. You should see the basic email in your Inbox or Spam folder.
Send a templated email:
curl -X POST localhost:8080/email/template/test
Check your email. You should see the template email in your Inbox or Spam folder.
Send an email with an attachment.
curl -X POST \ -H "Content-Type: multipart/form-data" \ -F "file=@ README.md" \ localhost:8080/email/attachment
Check your email. You should see the attachment email in your Inbox or Spam folder.
Bring the running application in the foreground:
fg
Once the application is running in the foreground, press
CTRL+C
to stop it.
Congratulations! Your Micronaut application native executable is now using the OCI Email Delivery to send emails.
Conclusion
In this workshop, you've seen how we can build a portable cloud-native Java application using Micronaut, GraalVM Native Image, and OCI Email Delivery to send emails.
Similarly, with Micronaut and GraalVM, you can easily send emails using AWS SES.