JavaMail and Gmail Simplified Part 5: Epilogue
I know that this is way too long as it is, but in going through my folders I found a folder where I was keeping example code found out in cyberland and I wanted to share part of the code that seems to make it into a lot of example Gmail code out there. This shows where people are populating configuration properties for which there are no reasons. If the protocol is specified properly, all of the things listed here are unnecessary.
Properties props = new Properties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.host", "smtp.gmail.com");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
props.put("mail.smtp.quitwait", "false");
Session session = Session.getDefaultInstance(props, null);
Posted at 06:44PM Jul 01, 2009 by Christian in Java | Comments[0]
JavaMail and Gmail Simplified Part 4: The Code, Sending and Receiving
OK, back to the task at hand. If we are successful at connecting, we can do some real work with our Gmail account. Sending and receiving messages with attachments, multipart messages, HTML messages, etc. is beyond the scope of these posts since this is just a basic intro that will cover only the basic concepts of JavaMail and connecting to a hosted email account. We will just focus on plain text.
First, let's look at sending email. As stated, the Transport object handles this. Keep in mind that we are only scratching the surface of what the JavaMail API can do. Once you are familiar with the basics, you can do just about anything fairly easily.
/**
* Sends a plain text email
* @param to
* @param subject
* @param text
* @throws SimpleGmailClientException
*/
public void sendMessage(String to, String subject, String text)
throws SimpleGmailClientException {
Message message = new MimeMessage(session);
try {
message.setFrom(new InternetAddress("test@gmail.com"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject(subject);
message.setText(text);
} catch (AddressException ae) {
throw new SimpleGmailClientException("Invalid internet address", ae);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error creating message", me);
}
try {
transport.sendMessage(message, message.getAllRecipients());
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error sending message", me);
}
}
As you can see, the code is pretty basic and straight forward. The method takes Strings for the "to" address, the subject, and the message text. One of the more complicated things is handling the exceptions that JavaMail will throw. In this case, I am wrapping all the exceptions in my own exception to add a friendlier message about where an error occurred. You can, of course, decide to throw the exceptions out to make the code a little cleaner and apply your own exception handling scheme if you wish. When learning from a newbie's perspective, though, I think that using the code the way it is will help point errors back to a recognizable point.
Next, let's look at reading email. In this case, I am simply going to take a String for a message subject to search for and read. When the message with the subject is found, a simple String representation of it is returned. Otherwise the method will return null.
/**
* Reads an email message with a given subject.
* Returns null if the subject is not found.
* @param subject
* @return A simple String representation of an email message,
* null if not found
* @throws SimpleGmailClientException
*/
public String readMessage(String subject) throws SimpleGmailClientException {
String messageString = null;
try {
for (Message message : Arrays.asList(inbox.getMessages())) {
if (message.getSubject().equals(subject)) {
messageString = "To: " + Arrays.asList(message.getAllRecipients()) + "n" +
"From: " + Arrays.asList(message.getFrom()) + "n" +
"Sent: " + message.getSentDate() + "n" +
"Subject: " + message.getSubject() + "n" +
"Text: " + message.getContent();
break;
}
}
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error reading Gmail Inbox", me);
} catch (IOException e) {/* Not using streams, only plain text */}
return messageString;
}
One other aside here, this is where a lot of confusion comes from as well. The user interface for most email clients place "deleted" messages in a "Deleted Messages" or "Trash" folder, but flagging and expunging in JavaMail does not. Messages that are expunged from folders with this technique are deleted forever! To emulate the way the UI works, you would have to explicitly move the message to the "Deleted Messages" or "Trash" folder with that folder's appendMessages() method.
All that's left is to create a main() method to use the client. To demonstrate the send and receive capabilities, we will login, send a message to ourselves, give it some time to arrive, read it, delete it, and logout. All you need to know is your Gmail username and password. (Click here to view full screen)
package com.e_gineering.simplegmailclient;
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
SimpleGmailClient client = new SimpleGmailClient();
String subject = "This is a Test";
try {
String username = "{INSERT YOUR USERNAME HERE}";
String password = "{INSERT YOUR PASSWORD HERE}";
client.login(username, password);
client.sendMessage(username + "@gmail.com", subject, "This is some test text");
// Give the message some time to arrive...
try {Thread.sleep(10000l);} catch (InterruptedException e) {}
String message = client.readMessage(subject);
System.out.println(message);
client.deleteMessage(subject);
client.logout();
} catch (SimpleGmailClientException sgce) {
sgce.printStackTrace();
}
}
}
Oh, and here's the SimpleGmailClientException: (Click here to view full screen)
package com.e_gineering.simplegmailclient;
/**
* Wrapper Exception for exceptions thrown inside SimpleGmailClient
* @author christian.desserich
*/
@SuppressWarnings("serial")
public class SimpleGmailClientException extends Exception {
/**
* @param message
* @param cause
*/
public SimpleGmailClientException(String message, Throwable cause) {
super(message, cause);
}
}
I hope that this helps people get started using JavaMail and gets people to stop putting SMTP socket factory, SMTP port, and various other extraneous properties in their code. It is not necessary! I also hope this will put to bed all the questions about how to interface with Gmail via Java. There is, of course, so much more you can do with the JavaMail API. There are hooks to use it with a GUI front end, for example the Session factory method will take an Authenticator argument. The Message classes enable you to create complicated emails with attachments, multi-part messages, HTML messages, etc. There are folder listeners that enable you to monitor the status of the messages contained within. The list goes on and on
Sources:
http://java.sun.com/products/javamail/JavaMail-1.4.pdf
http://java.sun.com/products/javamail/javadocs/index.html
http://java.sun.com/products/javamail/FAQ.html
Posted at 10:43AM Jun 30, 2009 by Christian in Java | Comments[0]
JavaMail and Gmail Simplified Part 3: The Code, Logging In
Finally!! Let's look at some code. Here is a simple Gmail client object in its entirety (Click here to see SimpleGmailClient full-page):
package com.e_gineering.simplegmailclient;
import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
/**
* A simple email client that will send and receive email with a hosted Gmail account
* @author christian.desserich
*/
public class SimpleGmailClient {
private Session session;
private Transport transport;
private Store store;
private Folder inbox;
private static final String INBOX = "INBOX";
/**
* Logs in to a Gmail account
* @param username
* @param password
* @throws SimpleGmailClientException
*/
public void login(String username, String password) throws SimpleGmailClientException {
session = Session.getInstance(new Properties());
try {
transport = session.getTransport("smtps");
transport.connect("smtp.gmail.com", username, password);
} catch (NoSuchProviderException nspe) {
throw new SimpleGmailClientException("Error getting Gmail Transport", nspe);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error connecting to Gmail Transport", me);
}
try {
store = session.getStore("imaps");
store.connect("imap.gmail.com", username, password);
} catch (NoSuchProviderException nspe) {
throw new SimpleGmailClientException("Error getting Gmail Store", nspe);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error connecting to Gmail Store", me);
}
try {
inbox = store.getFolder(INBOX);
inbox.open(Folder.READ_WRITE);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error opening Gmail Inbox", me);
}
}
/**
* Sends a plain text email
* @param to
* @param subject
* @param text
* @throws SimpleGmailClientException
*/
public void sendMessage(String to, String subject, String text)
throws SimpleGmailClientException {
Message message = new MimeMessage(session);
try {
message.setFrom(new InternetAddress("test@gmail.com"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject(subject);
message.setText(text);
} catch (AddressException ae) {
throw new SimpleGmailClientException("Invalid internet address", ae);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error creating message", me);
}
try {
transport.sendMessage(message, message.getAllRecipients());
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error sending message", me);
}
}
/**
* Reads an email message with a given subject.
* Returns null if the subject is not found.
* @param subject
* @return A simple String representation of an email message,
* null if not found
* @throws SimpleGmailClientException
*/
public String readMessage(String subject) throws SimpleGmailClientException {
String messageString = null;
try {
for (Message message : Arrays.asList(inbox.getMessages())) {
if (message.getSubject().equals(subject)) {
messageString = "To: " + Arrays.asList(message.getAllRecipients()) + "n" +
"From: " + Arrays.asList(message.getFrom()) + "n" +
"Sent: " + message.getSentDate() + "n" +
"Subject: " + message.getSubject() + "n" +
"Text: " + message.getContent();
break;
}
}
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error reading Gmail Inbox", me);
} catch (IOException e) {/* Not using streams, only plain text */}
return messageString;
}
/**
* Deletes (permanently) an email message with the given subject
* @param subject
* @throws SimpleGmailClientException
*/
public void deleteMessage(String subject) throws SimpleGmailClientException {
try {
for (Message message : Arrays.asList(inbox.getMessages())) {
if (message.getSubject().equals(subject)) {
message.setFlag(Flags.Flag.DELETED, true);
break;
}
}
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error reading Gmail Inbox", me);
}
try {
inbox.expunge();
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error deleting messages from Gmail Inbox", me);
}
}
/**
* Logs out of Gmail
*/
public void logout() {
if (transport != null) {
try {transport.close();} catch (MessagingException me) {/* We could log this error */}
}
if (store != null) {
try {store.close();} catch (MessagingException me) {/* We could log this error */}
}
}
}
Fist let's dissect each piece. This is the setup and connection code:
/**
* Logs in to a Gmail account
* @param username
* @param password
* @throws SimpleGmailClientException
*/
public void login(String username, String password) throws SimpleGmailClientException {
session = Session.getInstance(new Properties());
try {
transport = session.getTransport("smtps");
transport.connect("smtp.gmail.com", username, password);
} catch (NoSuchProviderException nspe) {
throw new SimpleGmailClientException("Error getting Gmail Transport", nspe);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error connecting to Gmail Transport", me);
}
try {
store = session.getStore("imaps");
store.connect("imap.gmail.com", username, password);
} catch (NoSuchProviderException nspe) {
throw new SimpleGmailClientException("Error getting Gmail Store", nspe);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error connecting to Gmail Store", me);
}
try {
inbox = store.getFolder(INBOX);
inbox.open(Folder.READ_WRITE);
} catch (MessagingException me) {
throw new SimpleGmailClientException("Error opening Gmail Inbox", me);
}
}
Notice the creation of the empty Properties object to hand to the factory method. This is where you could potentially read in a properties file with the load() methods that will take a Reader or InputStream. As stated (many, many times), we will hand in our info as we go, instead. In this case, I am connecting to both the SMTP and IMAP servers so we can have both send and receive capabilities. If you only need one or the other of course you can split out the code as you please.
The other interesting thing going on here is the opening of the "INBOX" folder. The Store object is represented as a folder hierarchy. There is the "default folder" that is the top level of the hierarchy. Servers can have the "INBOX" folder as the default folder, apparently, but in Gmail's case, the conceptual inbox is contained in the default folder along with another folder hierarchy that I will explain below. If you are going to apply this code to a different host besides Gmail, it is supposed to be standard that all mail servers have a folder, called "INBOX" that you can access, but don't count on it unless you know its there.
So to open the inbox, we have to explicitly get it by passing its name to the getFolder() method. JavaMail will automatically search down the hierarchy to find the folder name you pass into the method, so there is no need to loop through the hierarchy to find and get a certain folder. If you have the name correct, JavaMail will find it. The other thing going on is that you have to explicitly open a folder with read-only or read-write access. If you attempt to open a folder with read-write permissions, but are forbidden to do so, an exception is thrown.
Here's a quick overview of Gmail's IMAP folder hierarchy. Inside the default folder ("") there is the "INBOX" folder which we are already familiar with and the "[Gmail]" folder which we are not.
Inside the "[Gmail]" folder, there are the Gmail specific folders like the sent mail folder. These folders are where you could do things like purge sent mail, move messages to other folders, delete spam, etc.
Posted at 12:28AM Jun 26, 2009 by Christian in Java | Comments[0]
JavaMail and Gmail Simplified Part 2: The Objects
First let's explore the main objects that we are concerned with for simple interfacing of a Java application with a Gmail account using the Javamail API. The Session object is the main object holds the connection configuration information and factory methods for itself and the other two main objects, Transport and Store. Almost all configuration information needed for connecting to, authenticating with, and sending and receiving email can be handed to the Session object's Session.getInstance(Properties props) factory method via the Java Properties object argument, but at least in this case, we are not going to hand anything into the Session, so we can see where the info is coming from as we step through the code (Yes, there is code coming eventually!). The Session is not really a session like an active connection, just the holder for the settings and the factory methods for the Transport and Store objects.
Transport and Store represent send and receive capabilities respectively. This didn't make a whole lot of sense to me at first, but I realized that the two capabilities speak to two different hosts with two different protocols. Also, I would think that the send capability is used most often by developed applications (sending mass emails, email alerts, confirmation emails, etc.) so there is typically no need to have both capabilities simultaneously.
To get a Transport, we call the factory method on our Session object, session.getTransport(). This is where things can get confusing since there are several ways to configure the connection. This factory method will take no arguments (which will get all its configuration from the configuration stored in the Session object), an Address object (which will apparently provide a place to load the Transport object with the information it needs to configure itself), a Provider object (which will configure the returned Store object), a URLName object (not really sure the use of this one), or a String designating the protocol.
To get a Store, we call the factory method on our Session object, session.getStore(). This is the same place things can get confusing for the same reason as the Transport factory method. This factory method will take no arguments (which will get its configuration from the configuration stored in the Session object), a Provider object (which will configure the returned Store object), a URLName object (not really sure the use of this one), or a String designating the protocol.
For simplicity's sake in my example code, I did not hand the Session object ANY configuration via its factory method with the Java Properties object argument so that we can see and know for sure where the configuration info comes from. For Gmail specifically, for both the Transport and the Store, I used the respective factory methods that take the String for the protocol. For Gmail, send is "smtps", receive in my case is "imaps." Gmail allows POP3 as well, but I liked IMAP better since there were more methods on the Message object that were supported by IMAP (for example getReceivedDate()). The "s" on the end of the otherwise familiar protocols is for SSL.
Now! After all this, we need to actually connect to the Transport and Store. Both objects have the same connect methods that take similar arguments. This is another point of confusion, since the method will take usernames and passwords along with the host name. This is where I passed this information in instead of passing it into the Session object via a Java Properties object. I think this is better for learning since we know where all the values are coming from at development time.
As the code matures, things could be progressively moved to a "higher" configuration level so that all configuration efforts could be focused in one place. Every value used directly in the code (protocols, host names, usernames, and passwords) could be read in from a properties file using a Properties object and handed into the Session factory method enabling us to use the no-argument Transport and Store factory methods on the Session and the no-argument connect() methods on the Transport and Store. However, I think for demonstration purposes, this makes it easier to understand. You can figure out your own best way to do things, there are several choices with the JavaMail API.
Posted at 02:58PM Jun 25, 2009 by Christian in Java | Comments[0]
JavaMail and Gmail Simplified Part 1: Introduction
This post in its entirety turned out to be quite a bit longer than I originally thought it would be, so I am going to break it into parts. Caution: This is going to be very basic and is written for beginners. I felt that there was a need out there for this type of introductory tutorial based on a hosted email account, especially Gmail. So, if you're looking for an in-depth guide, the JavaMail documentation is a better place to start. If you wish to continue, here's part 1, stay tuned for parts 2, 3, and 4
In developing a simple email client for a customer that needed to programmatically (no UI) send, receive, and read email from a Gmail account, I had to do a lot more research into the JavaMail API than I really wanted to. I, just like a lot of people, was simply looking for a code snippet that would point me in the right direction for logging into a Gmail account and performing the most basic of operations. In searching for examples and trying to apply certain aspects of them to my application, I found that there is a lot of code out there that is over-coded and a lot that simply doesn't work.
It seems that many people find the example code packaged with the JavaMail API confusing. I can't say that I agree with this perception, nor can I say I don't disagree because many of the simple examples people are looking for are either not covered in a simple, "JavaMail-for-Dummies" manner or not covered specifically. Also, some examples make extensive use of command-line arguments for configuration. The following of the command-line arguments through the code and, of course, the configuration itself are very likely to confuse newbies.
The biggest problem I see with the code I found on the Internet is the over-coding. People have a tendency to code in tons of configuration properties that are totally unnecessary. This over-coding comes from a lack of understanding about what information is needed for what situation and how to communicate that to the API. There seems to be several ways to pass this configuration information to the API. These are listed in rough order that they will override the settings in each "higher-level" technique. Disclaimer: What follows is too simplified an explanation. The reasons for using the different methods of setting up the API exist for very different reasons for each application and I certainly don't profess to know all the ins and outs of the entire API. Also, for our purposes, we don't need to know all this, but you can refer to the JavaMail Specification or the API JavaDocs for more info.
1. The API can be initialized with "registry" files that can be placed in a location that the API searches for.
2. A Session object is created with the appropriate settings passed as an argument to the static Session.getInstance(Properties props) factory method as a Java Properties object (This is the most common way).
3. Settings can be passed into factory methods on Session that create the JavaMail objects, Transport and Store, and settings can be passed into their connect() methods.
4. There are setters for a few of the settings.
One of the things that frustrates beginners is that some of the properties that can be passed into the static Session.getInstance() method via a Java Properties object are not documented. This seems to be where a lot of the over-coding comes from. I see a ton of code out there that specifies ports, SSL socket factory classes, and other unnecessary information. If you use the correct methods and specify the correct protocol, JavaMail does the rest. All that extraneous information is needless for interfacing with a simple hosted email account and must be the result of cutting and pasting from more advanced code. The whole purpose of Javamail is to make the job of interfacing with different email server protocols and designs easier!
Posted at 10:15PM Jun 24, 2009 by Christian in Java | Comments[0]

