Using Javamail to Send Emails (Includes SMTP Auth and Attachment Support)
Last night I presented my article discussing how to use JavaMail to check a POP3 mail account and retrieve its messages. Tonight I am going to follow that up with a summary of my EmailDelivery class. This is a simple class I wrote to help me easily send emails from my Java applications. A few highlights of the class are that it:
- Supports relaying through SMTP servers that require authentication.
- Has convenience methods for easily adding file attachments.
- Has a method to set the message priority (high, normal, low).
- Allows you to easily set the to, from, cc, and bcc email addresses. Also supports passing in a comma separated list into any of these methods and have it parsed automatically for easy sending to multiple recipients.
- Allows you to easily add header name/value pairs in the message.
- Suppports easily setting the value stored in the Message-ID header tag of the message.
To use my classes you will first need to download the JavaBeans Activation Framework, and the Javamail libraries. Unzip the downloaded archives and put the appropriate jar file into your classpath. My example also uses Log4j to log its output. If you don’t want to configure log4j, then replace all of my log.info and log.debug calls with a call to System.out.println.
You also need Jakarta Commons Codec library from http://jakarta.apache.org/site/downloads/downloads_commons-codec.cgi. I downloaded the file labeled 1.3.zip Binary. Uncompress the zip file and put the commons codec jar file into your classpath. The name of the jar file in my example is commons-codec-1.3.jar. The Commons Codec jar file is what supports the Base 64 encoding of the file attachments.
Next, you will need to have my EmailMimeMessage class. Take the class below which is a simple extension of the standard JavaMail MimeMessage class. This class exists for the sole purpose of supporting a user defined Message-ID header tag in my examples.
package com.tima.edelivery.util;
import java.util.*;
import java.io.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import org.apache.log4j.*;
/**
* Extended MIME Message class with method to set the message ID
* header tag, and the updateHeaders method which ensures the
* Message ID gets set in the message.
*
* @author Tim Archer 09/30/2003
* @version $Revision: 1.1 $
*/
class EmailMimeMessage extends MimeMessage {
static Logger log = Logger.getLogger(EmailMimeMessage.class);
/** The value stored in the Message-ID header tag for the message.*/
protected String messageID = "";
/**
* Create a new EmailMimeMessage object.
* @param session The javax.mail.Session object the mail message is for.
*
*/
public EmailMimeMessage(javax.mail.Session session) {
super(session);
}
/**
* Set the value stored in the Message-ID header tag for the message.
* @param p_value The value of the Message-ID header tag.
*
*/
void setMessageID(String p_value) {
messageID = p_value;
}
/**
* Calls the super.updateHeaders() method, and also ensures
* that the Message-ID header tag
* gets set if the setMessageID method was called.
*
* @throws MessagingException If an error occurs.
*/
protected void updateHeaders() throws MessagingException {
super.updateHeaders();
if (messageID != null && messageID.length() > 0) {
setHeader("Message-ID", messageID);
}
}
}
Now that you have the EmailMimeMessage class, take the code below and create the EmailDelivery class. This class has the real functionality we’re after.
package com.tima.edelivery.util;
import java.util.*;
import java.io.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.*;
/**
* Create a multipart message with the second block of the
* message being the given file for an attachment.
*
* Has support for SMTP authentication to authenticate to an
* SMTP server to send the message.
* Utilizes the apache commons codec library to Base64 encode the file attachment.
*
* @author Tim Archer 09/30/2003
* @version $Revision: 1.1 $
*/
public class EmailDelivery {
static Logger log = Logger.getLogger(EmailDelivery.class);
/** Static variable representing a high priority message. */
public final static String HIGH_PRIORITY = "1";
/** Static variable representing a normal priority message. */
public final static String NORMAL_PRIORITY = "3";
/** Static variable representing a low priority message. */
public final static String LOW_PRIORITY = "5";
EmailMimeMessage msg = null;
Session session = null;
Properties props = null;
Multipart mp = null;
/** The SMTP host used to send the message. */
String smtp_host = new String("");
/** If using SMTP authentication, then this is the username to login to the SMTP server as. */
String smtp_username = null;
/** If using SMTP authentication, then this is the password for the username to login to the SMTP server as. */
String smtp_password = null;
/** The port the SMTP server is listening on. */
int smtp_port = 25;
/** If we are to use SMTP authentication when sending the message. */
boolean usingAuthentication = false;
/**
* Create a new object to send email messages.
*
*/
public EmailDelivery() {
props = System.getProperties();
session = Session.getDefaultInstance(props, null);
session.setDebug(false);
msg = new EmailMimeMessage(session);
mp = new MimeMultipart();
}
/**
* Set the SMTP host used for sending the mail message.
*
* @param p_host The SMTP host to use for sending the messgae.
* @throws MessagingException If an error occurs.
*
*/
public void setSMTPHost(String p_host) throws MessagingException {
this.setSMTPHost(p_host, null, null);
}
/**
* Set the SMTP host used for sending the mail message.
* This method allows a username and password to be specified for SMTP authentication.
*
* @param p_host The SMTP host to use for sending the messgae.
* @param p_username The username to log into the SMTP server with.
* @param p_password The password for the username to log into the SMTP server with.
* @throws MessagingException If an error occurs.
*
*/
public void setSMTPHost(String p_host, String p_username, String p_password) throws MessagingException {
log.debug("EmailDelivery in setSMTPHost Method. Host: "+p_host+" Username: "+p_username);
smtp_host = p_host;
smtp_username = p_username;
smtp_password = p_password;
props.put("mail.smtp.host", smtp_host);
//
//Determine if we are using authentication or not
//
if ((smtp_username != null && smtp_username.length() > 0) ||
(smtp_password != null && smtp_password.length() > 0)) {
props.put("mail.smtp.auth", "true");
usingAuthentication = true;
}
else {
usingAuthentication = false;
}
log.debug("EmailDelivery in setSMTPHost Method. UsingAuthenticaton: "+usingAuthentication);
}
/**
* Set the port that the SMTP server is listening on.
*
* @param p_port The port the SMTP server is listening on.
* @throws Exception If an error occurs.
*
*/
public void setSMTPPort(int p_port) throws Exception {
smtp_port = p_port;
props.put("mail.smtp.port", String.valueOf(smtp_port));
}
/**
* Set who the email message is to.
* The to email address may be a comma separated list of email addresses so the message will be sent to multiple recipients.
*
* @param p_to The email address of who the message is to. e.g. "jdoe@test.com"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setTo(String p_to) throws MessagingException, UnsupportedEncodingException {
this.setTo(p_to, null);
}
/**
* Set who the email message is to.
* The to email address may be a comma separated list of email addresses so the message will be sent to multiple recipients.
* If the message is sent to multiple recipients, then the name parameter of who to send to is ignored, and the
* displayed recipient name is the recipient email address.
*
* @param p_to The email address of who the message is to. e.g. "jdoe@test.com"
* @param p_to_name The textual name of who the message is to. e.g. "John Doe". This parameter is ignored if the to email address
* is a comma separated list of multiple email recipients.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setTo(String p_to, String p_to_name) throws MessagingException, UnsupportedEncodingException {
this.processRecipientList(Message.RecipientType.TO, p_to, p_to_name);
}
/**
* Set who is CC'ed on the email message.
* The to email address may be a comma separated list of email addresses who will be CC'ed on the email.
*
* @param p_cc The email address of who the message is to. e.g. "jdoe@test.com"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setCC(String p_cc) throws MessagingException, UnsupportedEncodingException {
this.setCC(p_cc, null);
}
/**
* Set who is CC'ed on the email message.
* The to email address may be a comma separated list of email addresses who will be CC'ed on the email.
* If the message is sent to multiple recipients, then the name parameter of who to send to is ignored, and the
* displayed recipient name is the recipient email address.
*
* @param p_cc The email address of who the message is to. e.g. "jdoe@test.com"
* @param p_cc_name The textual name of who the message is to. e.g. "John Doe". This parameter is ignored if the to email address
* is a comma separated list of multiple email recipients.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setCC(String p_cc, String p_cc_name) throws MessagingException, UnsupportedEncodingException {
this.processRecipientList(Message.RecipientType.CC, p_cc, p_cc_name);
}
/**
* Set who is BCC'ed on the email message.
* The to email address may be a comma separated list of email addresses who will be BCC'ed on the email.
*
* @param p_bcc The email address of who the message is to. e.g. "jdoe@test.com"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setBCC(String p_bcc) throws MessagingException, UnsupportedEncodingException {
this.setBCC(p_bcc, null);
}
/**
* Set who is BCC'ed on the email message.
* The to email address may be a comma separated list of email addresses who will be BCC'ed on the email.
* If the message is sent to multiple recipients, then the name parameter of who to send to is ignored, and the
* displayed recipient name is the recipient email address.
*
* @param p_bcc The email address of who the message is to. e.g. "jdoe@test.com"
* @param p_bcc_name The textual name of who the message is to. e.g. "John Doe". This parameter is ignored if the to email address
* is a comma separated list of multiple email recipients.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setBCC(String p_bcc, String p_bcc_name) throws MessagingException, UnsupportedEncodingException {
this.processRecipientList(Message.RecipientType.BCC, p_bcc, p_bcc_name);
}
/**
* Process a recipient list (To, CC, BCC) and setup the appropriate internet addresses to send to.
* This method will parse a list of recipients which may be separated by commas.
*
* @param recipientType The type of recipient the recipient list is being parsed for and sent to.
* @param p_to The email address of who the message is to. e.g. "jdoe@test.com"
* @param p_to_name The textual name of who the message is to. e.g. "John Doe". This parameter is ignored if the to email address
* is a comma separated list of multiple email recipients.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
private void processRecipientList (Message.RecipientType recipientType, String p_to, String p_to_name) throws MessagingException, UnsupportedEncodingException {
//
//If the to message address has a comma in it, then it must be a comma separated list of email recipients
//
StringTokenizer st = new StringTokenizer(p_to, ",");
int tokenCount = st.countTokens();
InternetAddress[] recipientList = new InternetAddress[tokenCount];
//
//Tokenize the recipient list, and create the Internet Address Array of Recipients
//
for (int i = 0; st.hasMoreTokens(); i++) {
//Get the next token
String msgTo = st.nextToken();
//Ensure the token received is a valid address
if (msgTo != null && msgTo.trim().length() > 0) {
//If we only have one email address then we can display the to name
if (tokenCount == 1 && p_to_name != null && p_to_name.length() > 0) {
recipientList[i] = new InternetAddress(msgTo, p_to_name);
}
//Otherwise just display the email address as the to name.
else {
recipientList[i] = new InternetAddress(msgTo);
}
}
}
msg.setRecipients(recipientType, recipientList);
}
/**
* Set who the email message is from.
*
* @param p_from The email address of who the message is from. e.g. "jdoe@test.com"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setFrom(String p_from) throws MessagingException, UnsupportedEncodingException {
this.setFrom(p_from, null);
}
/**
* Set who the email message is from.
*
* @param p_from The email address of who the message is from. e.g. "jdoe@test.com"
* @param p_from_name The textual name of who the message is from. e.g. "John Doe"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setFrom(String p_from, String p_from_name) throws MessagingException, UnsupportedEncodingException {
String msg_from = p_from;
String msg_from_name = p_from_name;
if (p_from_name != null && p_from_name.length() > 0) {
msg.setFrom(new InternetAddress(msg_from, msg_from_name));
}
else {
msg.setFrom(new InternetAddress(msg_from));
}
}
/**
* Set the reply to address for the email message.
*
* @param p_reply_to The email address for the reply to of the message. e.g. "jdoe@test.com"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setReplyTo(String p_reply_to) throws MessagingException, UnsupportedEncodingException {
this.setReplyTo(p_reply_to, null);
}
/**
* Set the reply to address for the email message.
*
* @param p_reply_to The email address for the reply to of the message. e.g. "jdoe@test.com"
* @param p_reply_to_name The textual name for the reply to of the message. e.g. "John Doe"
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void setReplyTo(String p_reply_to, String p_reply_to_name) throws MessagingException, UnsupportedEncodingException {
String msg_reply_to = p_reply_to;
String msg_reply_to_name = p_reply_to_name;
if (msg_reply_to_name != null && msg_reply_to_name.length() > 0) {
InternetAddress[] address = {new InternetAddress(msg_reply_to, msg_reply_to_name)};
msg.setReplyTo(address);
}
else {
InternetAddress[] address = {new InternetAddress(msg_reply_to)};
msg.setReplyTo(address);
}
}
/**
* Set the subject of the email message.
*
* @param p_subject The subject of the email message.
* @throws MessagingException If an error occurs.
*
*/
public void setSubject(String p_subject) throws MessagingException {
msg.setSubject(p_subject);
}
/**
* Set the body of the email message.
*
* @param p_body The body of the email message.
* @throws MessagingException If an error occurs.
*
*/
public void setBody(String p_body) throws MessagingException {
MimeBodyPart mbp_msgtext = new MimeBodyPart();
mbp_msgtext.setText(p_body);
mp.addBodyPart(mbp_msgtext);
}
/**
* Set a header tag in the email message. Replaces all existing header values
* with the same name with this new value. Note that RFC 822 headers must
* contain only US-ASCII characters, so a header that contains non
* US-ASCII characters must have been encoded by the caller as per
* the rules of RFC 2047.
*
* @param p_name The name of the header tag.
* @param p_value The value of the header tag.
* @throws MessagingException If an error occurs.
*
*/
public void setHeader(String p_name, String p_value) throws MessagingException {
msg.setHeader(p_name, p_value);
}
/**
* Set the priority of a message.
* 1 or 2 are high priority.
* 3 is normal priority.
* 4 or 5 are low priority.
* Most mail readers don't differentiate 1-2 and 4-5.
*
* @param priorityValue The priority code of the message.
* @throws Exception If an error occurs.
*
*/
public void setPriority(String priorityValue) throws Exception {
//if (priorityValue < 1 || priorityValue > 5) {
// throw new Exception ("An invalid priority value of " + priorityValue + " has been specified for the email.");
//}
if (priorityValue != null && priorityValue.trim().length() > 0) {
msg.setHeader("X-Priority", priorityValue);
}
}
/**
* Set the value stored in the Message-ID header tag for the message.
* @param p_value The value of the Message-ID header tag.
* @throws MessagingException If an error occurs.
*
*/
public void setMessageID(String p_value) throws MessagingException {
msg.setMessageID(p_value);
}
/**
* Take an original MimeMessage object and forward it to a new destination.
*
* @param p_msgFrom Email address of who the message is from. e.g. "jdoe@test.com"
* @param p_msgTo Email address of who the message is to. e.g. "jdoe@test.com"
* @param p_msgSubject Subject for the forwarded email message.
* @param p_msgBody Body of the forwarded email message.
* @param msgOrig The original MimeMessage that is being forwarded.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void forwardMessage(String p_msgFrom,
String p_msgTo,
String p_msgSubject,
String p_msgBody,
MimeMessage msgOrig) throws MessagingException, UnsupportedEncodingException {
forwardMessage(p_msgFrom, null, p_msgTo, null, p_msgSubject, p_msgBody, msgOrig);
}
/**
* Take an original MimeMessage object and forward it to a new destination.
*
* @param p_msgFrom Email address of who the message is from. e.g. "jdoe@test.com"
* @param p_msgFromText The textual name of who the message is from. e.g. "John Doe"
* @param p_msgTo Email address of who the message is to. e.g. "jdoe@test.com"
* @param p_msgToText The textual name of who the message is to. e.g. "John Doe"
* @param p_msgSubject Subject for the forwarded email message.
* @param p_msgBody Body of the forwarded email message.
* @param msgOrig The original MimeMessage that is being forwarded.
* @throws MessagingException If an error occurs.
* @throws UnsupportedEncodingException If an error occurs.
*
*/
public void forwardMessage(String p_msgFrom,
String p_msgFromText,
String p_msgTo,
String p_msgToText,
String p_msgSubject,
String p_msgBody,
MimeMessage msgOrig) throws MessagingException, UnsupportedEncodingException {
this.setFrom(p_msgFrom, p_msgFromText);
this.setTo(p_msgTo, p_msgToText);
this.setSubject(p_msgSubject);
this.setBody(p_msgBody);
MimeBodyPart part = new MimeBodyPart();
part.setDisposition(Part.ATTACHMENT);
part.setFileName(msgOrig.getSubject());
part.setContent(msgOrig, "message/rfc822");
mp.addBodyPart(part);
}
/**
* Add a file attachment to the email message.
*
* @param p_fileName The fully qualified name of the file to add to the message.
* @throws MessagingException If an error occurs.
*/
public void addFileAttachment(String p_fileName) throws MessagingException {
MimeBodyPart mbp_file = new MimeBodyPart();
//
//Attach the file to the message
//
FileDataSource fds = new FileDataSource(p_fileName);
mbp_file.setDataHandler(new DataHandler(fds));
mbp_file.setFileName(fds.getName());
mp.addBodyPart(mbp_file);
}
/**
* Add a file attachment to the email message from an input stream.
*
* @param p_fileName The filename for the attachment displayed in the the message.
* @param istrm The input stream containing the file attachment to add to the file.
* @param stream_len The length of the data in the input stream in bytes.
* @throws MessagingException If an error occurs.
* @throws IOException If an error occurs.
*/
public void addFileAttachmentFromStream(String p_fileName,
InputStream istrm,
int stream_len) throws MessagingException, IOException {
String logMsg = "";
byte b[] = new byte [stream_len];
byte enc_b[] = new byte [stream_len*3];
String encoded_str = new String("");
BufferedInputStream bistrm = new BufferedInputStream(istrm);
int bytes_read = 0;
bytes_read = bistrm.read(b, 0, stream_len);
logMsg = "Bytes Read From Stream: "+Integer.toString(bytes_read);
log.debug(logMsg);
/*
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Base64Encoder b64 = new Base64Encoder(new ByteArrayInputStream(b), bos);
b64.process();
enc_b = bos.toByteArray();
*/
enc_b = Base64.encodeBase64(b);
logMsg = "Encoded Byte Count: "+Integer.toString(enc_b.length);
log.debug(logMsg);
InternetHeaders hdr = new InternetHeaders();
hdr.addHeader("Content-Type", "application/octet-stream; name=\""+p_fileName+"\"");
hdr.addHeader("Content-Transfer-Encoding", "base64");
hdr.addHeader("Content-Disposition", "inline; filename=\""+p_fileName+"\"");
MimeBodyPart mbp_file = new MimeBodyPart(hdr, enc_b);
mp.addBodyPart(mbp_file);
}
/**
* Send the email message.
*
* @throws MessagingException If an error occurs.
*/
public void sendMsg() throws MessagingException {
//
//add the Multipart to the message
//
msg.setContent(mp);
//
// set the Date: header
//
msg.setSentDate(new Date());
msg.saveChanges();
//TEA 05/13/03, New method for sending so that we
//can support SMTP authentication
if (usingAuthentication) {
Transport transport = session.getTransport("smtp");
transport.connect (smtp_host, smtp_username, smtp_password);
transport.sendMessage(msg, msg.getAllRecipients());
transport.close();
}
else {
Transport.send(msg);
}
}
/**
* Main unit test method.
* @param args Command line arguments.
*
*/
public static void main(String args[]) {
try {
log.debug("Preparing to send email.");
//
//Prepare Our Mail Message
//
EmailDelivery ed = new EmailDelivery();
//Update the to/cc/bcc/from email addresseses. Note that the to, cc, and bcc
//methods can take a command separated list.
ed.setTo("tima@myemail.com, tima@hotmail.com");
ed.setCC("tima@test.com");
ed.setBCC("tima@anotheremailaddress.com");
ed.setFrom("somebody@test.com", "Joe Smith");
ed.setReplyTo("replytome@test.com", "Reply To Me");
ed.setSubject("Test Message Subject");
ed.setBody("Test message body.");
//Add the file attachment. Make sure this file exists for your testing.
ed.addFileAttachment("c:\\temp\\attachment.txt");
//Set the SMTP host to use to relay the message, and if it requires
//smtp authentication then also specify the username and password.
ed.setSMTPHost("192.168.1.50", "tima", "mySMTPpassword");
ed.sendMsg();
log.info("Test message sent.");
} catch (Exception e) {
log.error("Error:"+e.toString(), e);
}
}
}
Upon a close inspection you’ll see that the class isn’t really all that fancy. It’s basically a bunch of methods that make it easier to call the appropriate JavaMail APIs. Let me go over the example that I present in the main () method of the EmailDelivery class.
First off, you’ll need to construct a new EmailDelivery object.
EmailDelivery ed = new EmailDelivery();
Next, notice that I have convenience methods to set the To, CC, and BCC addresses. The call to set these looks like:
ed.setTo("tima@myemail.com, tima@hotmail.com");
ed.setCC("tima@test.com", "Tim Archer");
ed.setBCC("tima@anotheremailaddress.com", "Tim Archer");
These methods all take similar parameters. Either you can pass in a comma separated list of email addresses, which the method will then parse and send the message to all addresseses, or you can pass in a single email address paired with a second parameter of the plain text to display. For example, you may be CC’ing the message to tima@test.com, but you want any email clients to show in plain text that “Tim Archer” was cc’ed.
The setFrom() and setReplyTo() methods also work like the above, except they do not accept a command separated list.
ed.setFrom("somebody@test.com", "Joe Smith");
ed.setReplyTo("replytome@test.com", "Reply To Me");
You dont have to call the setReplyTo() method, you would only use that if, when the recipient replies to the message, you want the reply to go to a different address than who sent it. These two methods take the first parameter as being the actual email address that the email is from, or who to reply to, and the second optional parameter specifies the plain text name to be displayed in the recipients email client.
Setting the subject and email body is extremely easy, as demonstrated with the following two lines of code:
ed.setSubject("Test Message Subject");
ed.setBody("Test message body.");
And adding file attachments is simplified also. You can either add a file attachment from a file already on the filesystem just by passing in the file name:
ed.addFileAttachment("c:\\temp\\attachment.txt");
Call the above method for as many file attachments as you want to add. Or you can add a file attachment from a stream through the method which takes the filename to show as the email attachment, the stream of bytes that represents the file attachment, and the stream length.
addFileAttachmentFromStream(String p_fileName,
InputStream istrm,
int stream_len)
Lastly, you need to connect to an SMTP server and send the message. These two lines do that:
ed.setSMTPHost("192.168.1.50", "tima", "mySMTPpassword");
ed.sendMsg();
The setSMTPHost() method takes the host name of the mail server to use for sending the message. It can also optionally take two more parmameters which are the username and password to use to authenticate to the SMTP server with. Once you’ve made it this far the message is setup and ready to go. Simply call the sendMsg() method to send it on its way.
I hope this package helps you get over any stumbling blocks you may have when using JavaMail to send emails. I know it took me a little time to figure out how to get the attachments added, set the header tags, and to use an SMTP server that requires authentication. Feel free to grab and reuse any pieces of my classes.
Comments
Leave a comment Trackback