Finally!! Let’s look at some code. Here is a simple Gmail client object in its entirety
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 Propeties 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.