Java meets e-mail
In December 1997, I wrote an article called How to send an e-mail from Java. Sun had just announced the JavaMail API, but it was not publicly available, so I had to "roll my own" code. Let me offer a much-needed update.
Many applications, applets, and servlets can benefit from e-mail support. It's a great push technology; it's ideal to send alerts and offers flexible inter-application communication. However, with so many protocols (MIME, SMTP, POP, and so forth) to support, e-mail is not trivial. In this respect, the December code was helpful but limited. Now, Sun has released JavaMail 1.1 and suddenly e-mailing is easier.
JavaMail offers a clean, object-oriented API to messaging systems. JavaMail, the API, is strictly protocol-independent, and it is up to service providers to implement specific protocols. In fact, JavaMail service providers play a role similar to JDBC drivers: JDBC supports any SQL database for which a driver is available. Likewise, JavaMail supports any messaging system for which a service provider exists.
In practice, Sun's implementation is limited to SMTP and IMAP. The latter is a powerful protocol for reading e-mail from a server. Unfortunately, it is new and not as widely deployed as POP3. Fortunately, third parties have written POP3 service providers that plug into the JavaMail API. NNTP, the Usenet protocol, and Unix mbox are also available from third parties. While downloading JavaMail from Sun, check the list of service providers and grab a copy of POP3.
FunnyMail
FunnyMail is a simple application that illustrates how to use JavaMail. FunnyMail monitors a mailbox; when it receives mail, it automatically replies by reversing the text! Admittedly, this is not very helpful, but you can use the same canvas for many e-mail-oriented applications: instead of reversing the text, lookup information in a database. Listing 1 is FunnyMail's constructor. First, we obtain a reference to a Session object through
. Sessions manage configuration options and authentication. Session.getDefaultSession()
The FunnyMail constructor accepts a URL to the messaging system as a parameter. These URLs are of the form protocol://username:password/hostname, e.g., pop3://funnymail:pwd@mail.psol.com is the "fun" mailbox on mail.psol.com, a POP3 server, and imap://javamail:pwd@imap.psol.com is the "javamail" mailbox on an IMAP server, imap.psol.com.
JavaMail stores URLs in
objects. URLName
is similar to URLName
; however, it does not manage connecting to a server. As the name implies, java.net.URL
only stores the URL name. URLName
Listing 1: constructor
import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.event.*;
import javax.mail.internet.*;
class FunnyMail
{
private Session session;
private URLName urlName;
public FunnyMail(String url)
{
session =
Session.getDefaultInstance(System.getProperties(),
null);
urlName = new URLName(url);
}
The
method in listing 2 illustrates how to access the mailbox: first, obtain a Store object from the URL and the Session. Store represents a mailbox. A Store can contain multiple folders, but be aware that POP3, unlike IMAP, supports only one folder. readAndReply()
>From the Store, we open the "INBOX" folder. Note that FunnyMail needs write access on the folder to delete mail.
Next, we loop through the messages and, for each message, call
, which creates the reply. After posting a reply, it flags the message as deleted. To physically delete the messages, we have to call the replyTo()
method. expunge()
Finally we close the in-box and the store to release the connection.
Listing 2: readAndReply()
private void readAndReply()
throws MessagingException, IOException
{
Store store = session.getStore(urlName);
store.connect();
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_WRITE);
int count = inbox.getMessageCount();
for(int i = 1;i <= count;i++)
{
Message m = inbox.getMessage(i);
replyTo(m);
m.setFlag(Flags.Flag.DELETED,true);
}
inbox.expunge();
inbox.close(true);
store.close();
}
So far, so good. Let's turn our attention to
in listing 3. This method illustrates how to create and manipulate messages. replyTo()
instantiates a blank MimeMessage object and sets various properties using replyTo()
methods. setXXX()
, setFrom()
, setRecipients()
, and setSubject()
are self-explanatory. To add a non-standard field in the header, it is possible to use setSendDate()
. setHeader()
extracts lots of information from the original message using replyTo()
methods. Again getXXX()
and getSubject()
are self-explanatory. getFrom()
, however, deserves more explanation. E-mail posts are not limited to text; they can contain images, HTML, or a desktop document. getContent()
returns an object of a type that matches the content. For example, a String is used for text and a stream for images. Attachments are returned with as a Multipart object. FunnyMail only works with plain text mail and uses getContent()
to check this condition. MimeType()
Note that for graphical applications, JavaMail supports the JavaBeans Activation Framework, which automatically selects the right viewer based on the content type.
FunnyMail returns an error message if the message is not plain text. Otherwise, it reverses the mail. Finally, a call to
delivers the mail. Transport.send()
Listing 3: replyTo()
private void replyTo(Message m)
throws MessagingException, IOException
{
System.out.print("replying to: ");
System.out.println(m.getSubject());
Message r = new MimeMessage(session);
r.setFrom(InternetAddress.getLocalAddress(session));
r.setRecipients(Message.RecipientType.TO,m.getFrom());
r.setSubject("FUN: " + m.getSubject());
r.setSentDate(new Date());
r.setHeader("X-Mailer","FunnyMail 1.0");
Object o = m.getContent();
if(m.isMimeType("text/plain") && o instanceof String)
r.setText(reverse((String)o));
else
r.setText("FunnyMail requires plain text mails!");
Transport.send(r);
}
