All of the development for the Aus-e-Stage project, part of the larger AusStage system, is in Java using Apache Tomcat. Recently I needed to be able to send emails from one of my servlets. This post is a bunch of notes based on my experiences.
I decided to use the JavaMail API as this seemed to be the most popular solution. The email server I needed to use supports a number of different connection methods including TLS and SSL and required authentication. Also, the code that I developed is released as open source and so I wanted a solution that was as portable as possible.
There are three main components to the system:
- Configuration options in the web.xml file
- Prepare and send the email
- A class to manage authentication
Configuration Options
The first component is the storage of configuration options in the web.xml file for the application. The options are as follows
<!-- email server details --> <!-- server to connect to --> <context-param> <param-name>mailHost</param-name> <param-value></param-value> </context-param> <!-- username and password for authentication --> <context-param> <param-name>mailUser</param-name> <param-value></param-value> </context-param> <context-param> <param-name>mailPassword</param-name> <param-value></param-value> </context-param> <!-- set to 'yes' to enable the use of TLS --> <context-param> <param-name>mailTls</param-name> <param-value></param-value> </context-param> <!-- set to 'yes' to enable the use of SSL --> <context-param> <param-name>mailSsl</param-name> <param-value></param-value> </context-param> <!-- port to use to connect to the server, if different from the default --> <context-param> <param-name>mailPort</param-name> <param-value></param-value> </context-param> <!-- set to 'yes' to have debug info printed to catalina.out --> <context-param> <param-name>mailDebug</param-name> <param-value></param-value> </context-param> <!-- address to send email from --> <context-param> <param-name>mailFrom</param-name> <param-value></param-value> </context-param> <!-- address to send email to --> <context-param> <param-name>mailTo</param-name> <param-value></param-value> </context-param>
Prepare and send the email
The following is an extract from the class that I use to construct the email message and send it. The comments in the code cover everything that it does.
// get the mail configuration options
String mailHost = servletConfig.getServletContext().getInitParameter("mailHost");
String mailTo = servletConfig.getServletContext().getInitParameter("mailTo");
String mailFrom = servletConfig.getServletContext().getInitParameter("mailFrom");
String mailUser = servletConfig.getServletContext().getInitParameter("mailUser");
String mailPassword = servletConfig.getServletContext().getInitParameter("mailPassword");
String mailTls = servletConfig.getServletContext().getInitParameter("mailTls");
String mailSsl = servletConfig.getServletContext().getInitParameter("mailSsl");
String mailPort = servletConfig.getServletContext().getInitParameter("mailPort");
String mailDebug = servletConfig.getServletContext().getInitParameter("mailDebug");
// check on the parameters
if(mailHost == null || mailTo == null || mailFrom == null) {
throw new ServletException("Unable to get mail init parameters");
}
// Build the message
StringBuilder message = new StringBuilder("Start messagen");
// add the rest of the message
message.apppend("line by line construct the message");
// set the base properties for sending email
Properties mailProperties = System.getProperties();
mailProperties.put("mail.transport.protocol", "smtp");
mailProperties.put("mail.smtp.host", mailHost);
// check to see if we need to use a non standard port
if(mailPort != null) {
mailProperties.put("mail.smtp.port", mailPort);
}
// check to see if we need to use SSL
if(mailSsl != null) {
mailProperties.put("mail.smtp.ssl.enable", "true");
mailProperties.put("mail.smtp.ssl.trust", "*");
if(mailPort != null) {
mailProperties.put("mail.smtp.socketFactory.port", mailPort);
} else {
// assume default standard port for SSL SMTP
mailProperties.put("mail.smtp.socketFactory.port", "465");
}
mailProperties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
mailProperties.put("mail.smtp.socketFactory.fallback", "false");
}
// check to see if we need to output debug messages
if(mailDebug != null && mailDebug.equals("yes")) {
mailProperties.put("mail.debug", "true");
}
// do we need to use TLS?
if(mailTls != null && mailTls.equals("yes")) {
mailProperties.put("mail.smtp.starttls.enable","true");
}
// start the email session
Session mailSession;
// do we need to do authentication
if(mailUser != null) {
mailProperties.put("mail.smtp.auth", true);
mailSession = Session.getInstance(mailProperties, new SMTPAuthenticator(mailUser, mailPassword));
} else {
mailSession = Session.getInstance(mailProperties, null);
}
try {
// construct the message
Message mailMessage = new MimeMessage(mailSession); // base object
mailMessage.setFrom(new InternetAddress(mailFrom)); // set from address
mailMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mailTo, false)); // set to address
mailMessage.setSubject("Email subject"); // subject
mailMessage.setText(message.toString()); // body of the message
// add additional headers
mailMessage.setHeader("X-Mailer", "JavaMail");
mailMessage.setSentDate(new Date());
// send the message
Transport.send(mailMessage);
} catch(javax.mail.internet.AddressException ex) {
throw new ServletException("Unable to prepare mail message", ex);
} catch(javax.mail.MessagingException ex) {
throw new ServletException("Unable to send mail message", ex);
}
Managing Authentication
The last component of the system is the SMTPAuthenticator class which is used by the JavaMail library to respond to authentication requests. The class looks like this:
/**
* A private class to help with authentication of SMTP session
*/
private class SMTPAuthenticator extends javax.mail.Authenticator {
// declare private variables
private String username;
private String password;
/**
* Constructor for this class
*
* @param user username to use for authentication
* @param pass password to use for authentication
*/
public SMTPAuthenticator(String user, String pass) {
super();
username = user;
password = pass;
}
/**
* Method used to provide authentication information to JavaMail classes
*/
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
} // end class definition
Lessons Learnt
Some lessons learnt while putting this code together are:
- The debug option is invaluable when diagnosing issues such as messages not being sent
- Using telnet to manually check the SMTP connection can be helpful
- If you’re trying to manually check SMTP connection using SSL the following command can be helpful using a Linux / Unix system
openssl s_client -host XXX -port XXX
Check the OpenSSL website for more information about what this command does
The photo “air mail” was uploaded to Flickr by ‘smil and used under the terms of a Creative Commons License.
