Using the JavaMail API
You've seen how to work with the core parts of the JavaMail API. In the following sections you'll find a how-to approach for connecting the pieces to do specific tasks.
Sending Messages
Sending an email message involves getting a session, creating and filling a message, and sending it. You can specify your SMTP server by setting the mail.smtp.host property for the Properties object passed when getting the Session:
| String host = ...; String from = ...; String to = ...; // Get system properties Properties props = System.getProperties(); // Setup mail server props.put("mail.smtp.host", host); // Get session Session session = Session.getDefaultInstance(props, null); // Define message MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); message.setSubject("Hello JavaMail"); message.setText("Welcome to JavaMail"); // Send message Transport.send(message); |
You should place the code in a try-catch block, as setting up the message and sending it can throw exceptions.
Fetching Messages
For reading mail, you get a session, get and connect to an appropriate store for your mailbox, open the appropriate folder, and get your message(s). Also, don't forget to close the connection when done.
| String host = ...; String username = ...; String password = ...; // Create empty properties Properties props = new Properties(); // Get session Session session = Session.getDefaultInstance(props, null); // Get the store Store store = session.getStore("pop3"); store.connect(host, username, password); // Get folder Folder folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); // Get directory Message message[] = folder.getMessages(); for (int i=0, n=message.length; i System.out.println(i + ": " + message[i].getFrom()[0] + "\t" + message[i].getSubject()); } // Close connection folder.close(false); store.close(); |
What you do with each message is up to you. The above code block just displays who the message is from and the subject. Technically speaking, the list of from addresses could be empty and the getFrom()[0] call could throw an exception.
To display the whole message, you can prompt the user after seeing the from and subject fields, and then call the message's writeTo() method if they want to see it.
| BufferedReader reader = new BufferedReader ( new InputStreamReader(System.in)); // Get directory Message message[] = folder.getMessages(); for (int i=0, n=message.length; i System.out.println(i + ": " + message[i].getFrom()[0] + "\t" + message[i].getSubject()); System.out.println("Do you want to read message? " + "[YES to read/QUIT to end]"); String line = reader.readLine(); if ("YES".equals(line)) { message[i].writeTo(System.out); } else if ("QUIT".equals(line)) { break; } } |
Deleting Messages and Flags
Deleting messages involves working with the Flags associated with the messages. There are different flags for different states, some system-defined and some user-defined. The predefined flags are defined in the inner class Flags.Flag and are listed below:
· Flags.Flag.ANSWERED
· Flags.Flag.DELETED
· Flags.Flag.DRAFT
· Flags.Flag.FLAGGED
· Flags.Flag.RECENT
· Flags.Flag.SEEN
· Flags.Flag.USER
Just because a flag exists doesn't mean the flag is supported by all mail servers/providers. For instance, besides deleting messages, the POP protocol supports none of them. Checking for new mail is not a POP task but one built into mail clients. To find out what flags are supported, ask the folder with getPermanentFlags().
To delete messages, you set the message's DELETED flag:
message.setFlag(Flags.Flag.DELETED, true);
Open up the folder in READ_WRITE mode first though:
folder.open(Folder.READ_WRITE);
Then, when you are done processing all messages, close the folder, passing in a true value to expunge the deleted messages.
folder.close(true);
There is an expunge() method of Folder that can be used to delete the messages. However, it doesn't work for Sun's POP3 provider. Other providers may or may not implement the capabilities. It will more than likely be implemented for IMAP providers. Because POP only supports single access to the mailbox, you have to close the folder to delete the messages with Sun's provider.
To unset a flag, just pass false to the setFlag() method. To see if a flag is set, check with isSet().
Authenticating Yourself
You previously learned that you can use an Authenticator to prompt for username and password when needed, instead of passing them in as strings. Here you'll actually see how to more fully use authentication.
Instead of connecting to the Store with the host, username, and password, you configure the Properties to have the host, and tell the Session about your custom Authenticator instance, as shown here:
| // Setup properties Properties props = System.getProperties(); props.put("mail.pop3.host", host); // Setup authentication, get session Authenticator auth = new PopupAuthenticator(); Session session = Session.getDefaultInstance(props, auth); // Get the store Store store = session.getStore("pop3"); store.connect(); |
You then subclass Authenticator and return a PasswordAuthentication object from the getPasswordAuthentication() method. The following is one such implementation, with a single field for both. As this isn't a Project Swing tutorial, just enter the two parts in the one field, separated by a comma.
| import javax.mail.*; import javax.swing.*; import java.util.*; public class PopupAuthenticator extends Authenticator { public PasswordAuthentication getPasswordAuthentication() { String username, password; String result = JOptionPane.showInputDialog( "Enter 'username,password'"); StringTokenizer st = new StringTokenizer(result, ","); username = st.nextToken(); password = st.nextToken(); return new PasswordAuthentication(username, password); } } |
Since the PopupAuthenticator relies on Swing, it will start up the event-handling thread for AWT. This basically requires you to add a call to System.exit() in your code to stop the program.
Replying to Messages
The Message class includes a reply() method to configure a new Message with the proper recipient and subject, adding "Re: " if not already there. This does not add any content to the message, only copying the from or reply-to header to the new recipient. The method takes a boolean parameter indicating whether to reply to only the sender (false) or reply to all (true).
MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("president@whitehouse.gov"));
reply.setText("Thanks");
Transport.send(reply);
To configure the reply-to address when sending a message, use the setReplyTo() method.
Forwarding Messages
Forwarding messages is a little more involved. There is no single method to call, and you build up the message to forward by working with the parts that make up a message.
A mail message can be made up of multiple parts. Each part is a BodyPart, or more specifically, a MimeBodyPart when working with MIME messages. The different body parts get combined into a container called Multipart or, again, more specifically a MimeMultipart. To forward a message, you create one part for the text of your message and a second part with the message to forward, and combine the two into a multipart. Then you add the multipart to a properly addressed message and send it.
That's essentially it. To copy the content from one message to another, just copy over its DataHandler, a class from the JavaBeans Activation Framework.
| // Create the message to forward Message forward = new MimeMessage(session); // Fill in header forward.setSubject("Fwd: " + message.getSubject()); forward.setFrom(new InternetAddress(from)); forward.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Create your new message part BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setText( "Here you go with the original message:\n\n"); // Create a multi-part to combine the parts Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart); // Create and fill part for the forwarded content messageBodyPart = new MimeBodyPart(); messageBodyPart.setDataHandler(message.getDataHandler()); // Add part to multi part multipart.addBodyPart(messageBodyPart); // Associate multi-part with message forward.setContent(multipart); // Send message Transport.send(forward); |
Working with Attachments
Attachments are resources associated with a mail message, usually kept outside of the message like a text file, spreadsheet, or image. As with common mail programs like Eudora and pine, you can attach resources to your mail message with the JavaMail API and get those attachments when you receive the message.
Sending Attachments
Sending attachments is quite like forwarding messages. You build up the parts to make the complete message. After the first part, your message text, you add other parts where the DataHandler for each is your attachment, instead of the shared handler in the case of a forwarded message. If you are reading the attachment from a file, your attachment data source is a FileataSource. Reading from a URL, it is a URLDataSource. Once you have your DataSource, just pass it on to the DataHandler constructor, before finally attaching it to the BodyPart with setDataHandler(). Assuming you want to retain the original filename for the attachment, the last thing to do is to set the filename associated with the attachment with the setFileName() method of BodyPart. All this is shown here:
| // Define message Message message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); message.setSubject("Hello JavaMail Attachment"); // Create the message part BodyPart messageBodyPart = new MimeBodyPart(); // Fill the message messageBodyPart.setText("Pardon Ideas"); Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart); // Part two is attachment messageBodyPart = new MimeBodyPart(); DataSource source = new FileDataSource(filename); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(filename); multipart.addBodyPart(messageBodyPart); // Put parts in message message.setContent(multipart); // Send the message Transport.send(message); |
When including attachments with your messages, if your program is a servlet, your users must upload the attachment besides tell you where to send the message. Uploading each file can be handled with a form encoding type of multipart/form-data.
|
method=post action="/myservlet"> |
| Note: Message size is limited by your SMTP server, not the JavaMail API. If you run into problems, consider increasing the Java heap size by setting the ms and mx parameters. |
Getting Attachments
Getting attachments out of your messages is a little more involved then sending them, as MIME has no simple notion of attachments. The content of your message is a Multipart object when it has attachments. You then need to process each Part, to get the main content and the attachment(s). Parts marked with a disposition of Part.ATTACHMENT from part.getDisposition() are clearly attachments. However, attachments can also come across with no disposition (and a non-text MIME type) or a disposition of Part.INLINE. When the disposition is either Part.ATTACHMENT or Part.INLINE, you can save off the content for that message part. Just get the original filename with getFileName() and the input stream with getInputStream().
| Multipart mp = (Multipart)message.getContent(); for (int i=0, n=multipart.getCount(); i Part part = multipart.getBodyPart(i)); String disposition = part.getDisposition(); if ((disposition != null) && ((disposition.equals(Part.ATTACHMENT) || (disposition.equals(Part.INLINE))) { saveFile(part.getFileName(), part.getInputStream()); } } |
The saveFile() method just creates a File from the filename, reads the bytes from the input stream, and writes them off to the file. In case the file already exists, a number is added to the end of the filename until one is found that doesn't exist.
| // from saveFile() File file = new File(filename); for (int i=0; file.exists(); i++) { file = new File(filename+i); } |
The code above covers the simplest case where message parts are flagged appropriately. To cover all cases, handle when the disposition is null and get the MIME type of the part to handle accordingly.
| if (disposition == null) { // Check if plain MimeBodyPart mbp = (MimeBodyPart)part; if (mbp.isMimeType("text/plain")) { // Handle plain } else { // Special non-attachment cases here of // image/gif, text/html, ... } ... } |
Processing HTML Messages
Sending HTML-based messages can be a little more work than sending plain text messages, though it doesn't have to be that much more work. It all depends on your specific requirements.
Sending HTML Messages
If all you need to do is send the equivalent of an HTML file as the message and let the mail reader worry about fetching any embedded images or related pieces, use the setContent() method of Message, passing along the content as a String and setting the content type to text/html.
String htmlText = "Hello
" +
"";
message.setContent(htmlText, "text/html"));
On the receiving end, if you fetch the message with the JavaMail API, there is nothing built into the API to display the message as HTML. The JavaMail API only sees it as a stream of bytes. To display the message as HTML, you must either use the Swing JEditorPane or some third-party HTML viewer component.
| if (message.getContentType().equals("text/html")) { String content = (String)message.getContent(); JFrame frame = new JFrame(); JEditorPane text = new JEditorPane("text/html", content); text.setEditable(false); JScrollPane pane = new JScrollPane(text); frame.getContentPane().add(pane); frame.setSize(300, 300); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.show(); } |
Including Images with Your Messages
On the other hand, if you want your HTML content message to be complete, with embedded images included as part of the message, you must treat the image as an attachment and reference the image with a special cid URL, where the cid is a reference to the Content-ID header of the image attachment.
The process of embedding an image is quite similar to attaching a file to a message, the only difference is that you have to tell the MimeMultipart that the parts are related by setting its subtype in the constructor (or with setSubType()) and set the Content-ID header for the image to a random string which is used as the src for the image in the img tag. The following demonstrates this completely.
| String file = ...; // Create the message Message message = new MimeMessage(session); // Fill its headers message.setSubject("Embedded Image"); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Create your new message part BodyPart messageBodyPart = new MimeBodyPart(); String htmlText = " " messageBodyPart.setContent(htmlText, "text/html"); // Create a related multi-part to combine the parts MimeMultipart multipart = new MimeMultipart("related"); multipart.addBodyPart(messageBodyPart); // Create part for the image messageBodyPart = new MimeBodyPart(); // Fetch the image and associate to part DataSource fds = new FileDataSource(file); messageBodyPart.setDataHandler(new DataHandler(fds)); messageBodyPart.setHeader("Content-ID"," // Add part to multi-part multipart.addBodyPart(messageBodyPart); // Associate multi-part with message message.setContent(multipart); |
Searching with SearchTerm
The JavaMail API includes a filtering mechanism found in the javax.mail.search package to build up a SearchTerm. Once built, you then ask a Folder what messages match, retrieving an array of Message objects:
SearchTerm st = ...;
Message[] msgs = folder.search(st);
There are 22 different classes available to help you build a search term.
· AND terms (class AndTerm)
· OR terms (class OrTerm)
· NOT terms (class NotTerm)
· SENT DATE terms (class SentDateTerm)
· CONTENT terms (class BodyTerm)
· HEADER terms (FromTerm / FromStringTerm, RecipientTerm / RecipientStringTerm, SubjectTerm, etc.)
Essentially, you build up a logical expression for matching messages, then search. For instance the following term searches for messages with a (partial) subject string of ADV or a from field of friend@public.com. You might consider periodically running this query and automatically deleting any messages returned.
| SearchTerm st = new OrTerm( new SubjectTerm("ADV:"), new FromStringTerm("friend@public.com")); Message[] msgs = folder.search(st); |
**************************************************************
**************************************************************
No comments:
Post a Comment