Thoughts by Techxplorer

Thoughts on my experiences with technology

Sending email in Java

By: 'smil

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:

  1. Configuration options in the web.xml file
  2. Prepare and send the email
  3. 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:

  1. The debug option is invaluable when diagnosing issues such as messages not being sent
  2. Using telnet to manually check the SMTP connection can be helpful
  3. 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.

Category: Thoughts
Tag: