HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

JavaMail is a set of abstract classes that create a framework for sending, receiving and handling e-mail, along with implementations of those classes. The package Sun provides contains implementations of IMAP and SMTP, allowing you to get started immediately on sending and receiving mail. They also provide a separate POP3 implementation that I'll describe below. The framework makes it easy to create your own cross-platform mail application without an in-depth knowledge of e-mail. Methods and classes that allow you to access mail folders, download messages, send messages with attachments and filter mail are included.

JavaMail has a number of uses in personal and Enterprise-level programming. It can be used to create personal mail filters, simple mailing lists and customized personal mail applications, as well as to add full e-mail capabilities to an Enterprise application or create a full-fledged e-mail client. A number of products currently available are built for or around JavaMail; many are listed on the JavaMail Third Party Product page (http://java.sun.com/products/javamail/Third_Party.html). Several companies have written marketing applications that use JavaMail to send customized mail to groups of contacts, and many companies have written new e-mail clients using JavaMail and its extensions.

Background
Knowing a little about what e-mail is and how it is sent and received is helpful in creating e-mail applications. E-mail protocols are, in the most basic sense, a way of transferring data from one machine to another, possibly going through several other machines along the way. SMTP has existed, essentially in its current state, since the early 1980s, and POP and IMAP aren't much younger. The preceding is a very basic introduction to these protocols; see "Where to get more information" for more information.

SMTP - Simple Mail Transfer Protocol - allows two mail servers to communicate using a simple language, and provides a step-by-step protocol for exchanging information. To use the example that follows, you'll need an SMTP server. If you're using UNIX, you probably have one on your machine already. If you're not, or don't have a server installed, you can ask your system administrator for the name of your SMTP server.

IMAP - Internet Mail Access Protocol - and POP3 - Post Office Protocol Version 3 - are client/server mail protocols. SMTP delivers mail to a central location, where the user can either log in and read it directly or use a client/server mail protocol to read it remotely. IMAP is designed to keep mail on a remote server and let the user interact with it there, while POP3 is designed to forward a user's mail to a single machine, where the user can go offline and read it, if necessary. In general, people with slow connections (dial-up or otherwise) tend to use POP3 because they can connect and download their mail without having to keep the connection open afterwards. People with fast connections and multiple machines usually use IMAP so they can read mail from whichever machine they happen to be on without losing access to the mail they read elsewhere. In the following examples, you will need a POP3.

How Do I Get Started?
First, you'll need the JDK (I use 1.2 in this article), JavaMail package and the JavaBeans Activation Framework extension (JAF). JAF is a standard extension that provides a way to identify and correctly process unknown data. You can download each from the Java Web site. JDK 1.2 (also known as the Java 2 SDK) is at http://java. sun.com/products/jdk/1.2/, and JavaMail and JAF are at http:/ /java.sun.com/products/ javamail/index.html. Follow the installation instructions given on the download pages, and install the packages. JavaMail comes with a number of examples in the demo directory, many of which are easy to use immediately or can be modified for specific situations.

A simple example is msgsendsample.java. This sends a simple message (included in the example) to an e-mail address provided by the user. To use this, go to the demo directory and compile msgsendsample.java. Then, on the command line, type:

  java msgsendsample <your email address> <your email address> <your SMTP server> false
e.g.
  java msgsendsample [email protected] [email protected] smtp.silentq.com false

The command line arguments are <address to> <address from> <mail server> <debug>. You can actually specify any e-mail address for the from argument and the message will appear to be from that e-mail address, but a look at the full headers will show the real source. The false at the end indicates that you don't want to see the debug information; change that to true for a look at what the example is doing. The complete SMTP connection dialog will be printed out, as well as some debug information for the example.

A look at the source shows how this example works:

Properties props = new Properties();
props.put("mail.smtp.host", host);

JavaMail needs the mail host property set to determine where the SMTP server is located. If it's on your local machine, the send can happen locally. Otherwise it will automatically connect to the server machine and send from there.

Session session = Session.getDefaultInstance(props, null);

This gets a mail session (notice it uses the properties just created). The default mail session is created if it doesn't already exist, and permissions are created or checked. Since the Authenticator (security object - the second parameter listed) is null, there's no security on this object, and anyone can access it.

Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
  InternetAddress[] address = {new InternetAddress(args[0])};
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject("JavaMail APIs Test");
msg.setSentDate(new Date());
msg.setText(msgText);

This section creates the actual message object and fills in the to, from, subject, date and content. There are also options to set the reply to, content and content type, and other header information. Since this is a MIME - Multipurpose Internet Mail Extensions - message, it can have several parts, none of which have to be plain text. You may want to set a section to HTML or add an attachment. You can set a DataHandler (in the JDK) using setDataHandler() in MimeMessage to handle nontext parts. This is a simple one-part text message, so you can use the setText() method instead.

Transport.send(msg);

This actually sends the message, using SMTP. If the send fails, it will throw exceptions based on the problem with the send. For example, try sending to a nonexistent address for example, ("rachelfoo"). You'll see a SendFailedException, with some details about the address that failed.

With a few changes you can modify this example to send mail programmatically. Changing the <to>, <from>, <host>, <subject> and <content> values to method arguments and adding a constructor to set them will allow you to instantiate this object from another class and send mail automatically. In addition, you can add a hashtable of substitutions and include these in your message text to personalize the e-mail - see Listing 1. This listing creates the hashtable individually for each e-mail, but this could be customized to read from a database or file.

A More Complex Code Sample
The next example will read and optionally delete messages from a POP3 server. The first step is to download and install a POP3 provider. The example uses Sun's POP3 provider. Follow the directions from the JavaMail page at Sun (listed above) to download and unzip the package, and set your classpath correctly. Since you may want to use other providers later, you need to add this provider to your provider list. To do this, create a new directory, copy the mail.jar file into that directory and unjar it (jar xvf mail.jar).

You'll see several directories, including a META-INF directory, which generally contains information about the jar file that includes it, but in this case also contains information critical to JavaMail. This is important to remember. If you ever install JavaMail as part of a product, be sure to include this directory in your product package.

To add the POP3 provider, create a file called javamail.providers with the following line in it:

protocol=pop3; type=store; class=com.sun.mail.pop3.POP3Store;

Then jar the directories again (jar cvf mail.jar *), back up the old mail.jar and move the new mail.jar into place. You now have a POP3 provider added to your provider list.

The POP3 example is included here as Listing 2. Download and compile the program - you'll need a POP3 mail server to test it. The example takes parameters <username> <password> <server> <delete>. The last parameter specifies whether you want to delete any messages you read from the server. Test the program with:

  java MailPrinter <your POP3 username> <your POP3 password> <your POP3 mail server> false
e.g.   java MailPrinter rachel mypassword pop3.silentq.com false

You'll see a list of all your new messages with their headers. The thread will continue, downloading all messages every five minutes until you stop it. This could easily be changed to append to a file; with the delete option set to true, it would constantly update your local mail file. It could also be altered simply to check the number of messages and, with a small user interface, could be made into a mail notification system.

The source can be broken down as follows:

Properties lProperties = System.getProperties();

Session lSession = Session.getDefaultInstance(lProperties, null);

As in the last example, the session is started with no security. This time it's started with the system properties instead of an empty Properties object.

Store store = null;
Folder folder = null;
try {
   store = lSession.getStore(protocol); }

This part gets the store type for the given protocol (POP3). Store is an abstract class that models a message store. This allows connections to various types of actual message stores with no loss of generality.

store.connect(host, user, password);

Now it connects to the remote server (host) with the given username and password. Obviously, it would be unwise to do this part programmatically without a fair number of precautions - storing your password in plain text in the source is a security problem. It might be wise to store the password encrypted in your database or file system and retrieve and unencrypt it only when needed.

folder = store.getDefaultFolder();  ...  folder = folder.getFolder("INBOX");

This initializes the folder with the standard default mailbox, indicated by the key word "INBOX". In this context INBOX stands for "primary folder for this user on this server" - it will vary by protocol, server and user.

folder.open(Folder.READ_WRITE);  ...  Message[] messages = folder.getMessages();

Here the folder is opened and the messages are downloaded. This includes their headers and all parts of the message (in the case of MIME messages). This doesn't remove them from the server; it just copies them into memory.

if (delete)     messages[i].setFlag(Flags.Flag.DELETED, true);
...
folder.close(true);
store.close();

Flags indicate the status of the message in the folder. Here each downloaded message is marked deleted, and when the folder is closed the "true" flag indicates that all deleted messages should be removed.

 for (Enumeration e = pPart.getAllHeaders(); e.hasMoreElements(); )
{     Header header = (Header) e.nextElement();
   System.out.println(header.getName() + ": " + header.getValue());
}

This section gets each of the header lines from the message and prints them out. You could change this part to print out only the headers you're interested in using: getMatchingHeaders() instead of getAllHeaders().

The rest of the example recursively divides the message into parts and prints each part. This example could easily be expanded to filter out messages with selected subjects or messages from particular addresses. It could store the messages in a database or to a file. In all, JavaMail gives you powerful tools to filter and manipulate e-mail messages with very simple code.

For More Information
1. JavaMail home page: http://java.sun.com/products/javamail/index.html
2. RFC 821 (SMTP specification:http://info.internet.isi.edu/in-notes/rfc/files/rfc821.txt
3. IMAP, POP3: ftp://ftp.cac.washington.edu/mail/imap.vs.pop For anything not covered in these sources, a simple Net search will probably find what you need. E-mail is a frequently discussed topic on the Net, and there are FAQs and articles everywhere. Good luck!

Author Bio
Rachel Gollub, founder of SilentQ Software Company, learned Java as a JavaSoft engineer from 1995 to 1997, and has worked at several start-ups since then. She writes articles and gives presentations on Java and related subjects.
The author can be reached at [email protected]

	

Listing 1:

/*
* SendMessage.java
*/


import java.util.*;
import java.io.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;


/**
* This is copied almost directly from
* msgsendsample.java, by Max Spivak,
* with changes by Rachel Gollub
* ([email protected]).  More details
* are available in the accompanying
* article.
*/
public class SendMessage {


  public boolean send(MessageObject message) {
    //create some properties and get
    //the default Session
    Properties props = new Proper-
    ties();
    props.put("mail.smtp.host", mes-
    sage.host);
    // Uncomment this to debug
    //props.put("mail.debug", args[3]);


    Session session =
      Session.getDefaultInstance(props,
      null);
    session.setDebug(false); // true to
    debug


    try {
      // create a message
      Message msg = new MimeMessage(ses-
      sion);
      msg.setFrom
        (new InternetAddress(mes-
         sage.from));
      InternetAddress[] address =
        {new InternetAddress(mes-
         sage.to)};
      msg.setRecipients
        (Message.RecipientType.TO,
         address);
      msg.setSubject(message.subject);
      msg.setSentDate(new Date());
      msg.setText(replace
        (message.custom, message.text));


      Transport.send(msg);
    } catch (MessagingException mex) {
      System.out.println
        ("\n--Exception handling in " +
         "msgsendsample.java");


      mex.printStackTrace();
      System.out.println();
      Exception ex = mex;
      do {
        if (ex instanceof
          SendFailedException) {
          SendFailedException sfex =
            (SendFailedException)ex;
          Address[] invalid =
            sfex.getInvalidAddresses();
          if (invalid != null) {
          System.out.println
            ("    ** Invalid Addresses");
          if (invalid != null) {
            for (int i = 0;
              i < invalid.length; i++)
              System.out.println
                ("      " + invalid[i]);
            }
          }
          Address[] validUnsent =
            sfex.getValidUnsentAddresses();
          if (validUnsent != null) {
            System.out.println
              ("    ** ValidUnsent Addresses");
            if (validUnsent != null) {
              for (int i = 0;
                i < validUnsent.length; i++)
                System.out.println
                  ("         "+validUnsent[i]);
              }
            }
          Address[] validSent =
            sfex.getValidSentAddresses();
          if (validSent != null) {
            System.out.println
              ("    ** ValidSent Addresses");
            if (validSent != null) {
              for (int i = 0;
                i < validSent.length; i++)
                System.out.println
                  ("         "+validSent[i]);
              }
            }
          }
        System.out.println();
        } while ((ex = ((MessagingException)ex)
          .getNextException()) != null);
      return false;
    }
      return true;
    }


  /**
   * This is a simple replace method.  It
   * searches for the keywords, and uses
   * a StringBuffer to replace them.
   *
   * @param macros The keywords and values.
   * @param message The message to change.
   *
   * @return The changed message.
   */
  public String replace (Hashtable macros,
                         String message) {
    for (Enumeration e = macros.keys();
      e.hasMoreElements(); ) {
      int index = 0;
      String key = (String) e.nextElement();
      String value = (String) macros.get(key);
      while ((index =
        message.indexOf(key, index)) !=-1) {
        StringBuffer text =
          new StringBuffer(message);
        message = text.replace(index,
          index + key.length(), value)
          .toString();
        index += value.length();
      }
    }
    return message;
  }
}



/*
* MakeMessage.java
*/


import java.util.Hashtable;


/**
* This is a test class that creates some
* MessageObjects to send with SendMessage.
*
* @author Rachel Gollub ([email protected])
*/
public class MakeMessage {


  /**
   * The main method initializes the objects
   * and sends them.
   */
  public static void main(String args[]) {


    SendMessage message = new SendMessage();
    MessageObject object =
      makeObject("[email protected]",
                 "Rachel", "chocolate");
    message.send(object);
    object = makeObject("[email protected]",
                        "Jeremy", "chicken");
    message.send(object);
    object = makeObject("[email protected]",
                        "Miriam", "milk");
    message.send(object);
  }


  /**
   * This creates the actual MessageObject
   * for each recipient, filling it with
   * standard and custom values.
   */
  public static MessageObject makeObject
    (String to, String name, String food) {
    MessageObject object = new MessageObject();
    Hashtable macros = new Hashtable();
    macros.put("$firstname", name);
    macros.put("$favoritefood", food);


    object.to = to;
    object.custom = macros;
    object.text =
      "Dear $firstname,\n" +
      "\n" +
      "We'd like you to know that " +
      "$favoritefood is on sale this week!\n" +
      "\n" +
      "Sincerely,\n" +
      "The Management";


    object.subject = "SALE!";
    object.host = "pop.idiom.com";
    object.from = "[email protected]";


    return object;
  }
}



/*
* MessageObject.java
*/


import java.util.Hashtable;


/**
* This class holds message information.
*
* @author Rachel Gollub ([email protected])
*/
public class MessageObject {
  String to;
  String from;
  String host;
  String subject;
  String text;
  Hashtable custom;
}


Listing 2

/*
* MailPrinter.java
*/


import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;


import java.util.Enumeration;
import java.util.Properties;


import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import javax.mail.Folder;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;


/**
* This class monitors a given mailbox every 5
* minutes, and prints the contents to stdout.
* It will also optionally delete messages it
* reads.  More detailed comments are provided in
* the accompanying article.
*
* @author Rachel Gollub ([email protected])
*/
public class MailPrinter extends Thread {


  public static void main(String args[]) {
    // These args are <user>, <password>, <host>,
    // <delete?true:false>
    MailPrinter popThread =
      new MailPrinter(args[0], args[1],
                      args[2], args[3]);
    popThread.start();
  }


  String protocol = "pop3";
  String host;
  String user;
  String password;
  boolean delete = false;


  /**
   * This just initializes the global variables.
   */
  public MailPrinter(String pUser,
    String pPassword, String pHost,
    String pDelete) {
    user = pUser;
    password = pPassword;
    host = pHost;
    if (pDelete.toLowerCase().equals("true"))
      delete = true;
  }


  /**
   * The run() method makes a mailbox connection
   * every five minutes to check for mail.
   */
  public void run() {
    while (true) {
    doStore();
      try {
    Thread.sleep(300000);
      } catch (Exception e) {
    e.printStackTrace();
      }
    }
  }


  public void doStore() {
    Properties lProperties =
      System.getProperties();


    Session lSession =
      Session.getDefaultInstance
        (lProperties, null);
    // Set debug here
    lSession.setDebug(false);


    Store store = null;
    Folder folder = null;
    try {
      store = lSession.getStore(protocol);
    } catch (Exception e) {
      System.out.println
        ("Couldn't get store from protocol.");
      e.printStackTrace();
    }
    // Connect
    try {
      if (host != null || user != null
      || password != null)
    store.connect
      (host, user, password);
      else
    store.connect();
    } catch (Exception e) {
      System.out.println("Unable to connect.");
      e.printStackTrace();
    }


    // Open the Folder
    if (folder == null)
      try {
    folder = store.getDefaultFolder();
      } catch (Exception e) {
    System.out.println
      ("Couldn't get folder.");
    e.printStackTrace();
      }
    if (folder == null) {
      System.out.println("No default folder");
    }


    // Get the default folder
    try {
      folder = folder.getFolder("INBOX");
      if (folder == null) {
    System.out.println("Invalid folder");
      }


      if (folder != null) {
    folder.open(Folder.READ_WRITE);
    int lTotalMessages =
      folder.getMessageCount();


    if (lTotalMessages == 0) {
      System.out.println("Empty folder");
      folder.close(false);
      folder = null;
      store.close();
    }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }


    // Get and process messages
    if (folder != null) {
      try {
    Message[] messages =
      folder.getMessages();


    for (int i = 0;
      i < messages.length; i++) {
      if (filter(messages[i])) {
        if (delete)
          messages[i].setFlag
            (Flags.Flag.DELETED, true);
      }
    }
    folder.close(true);
    store.close();
      } catch (Exception e) {
    e.printStackTrace();
      }
    }
  }


  /**
   * The filter() method tries to figure out the
   * message type, and recursively sends
   * multi-part parts through the filter.
   *
   * @param part The message part.
   *
   * @return True iff successful.
   */
  public boolean filter(Part part) {


    Object o = null;


    try {
      for (Enumeration e = part.getAllHeaders();
        e.hasMoreElements(); ) {
        Header header =
          (Header) e.nextElement();
        System.out.println(header.getName() +
          ": " + header.getValue());
      }
      System.out.println("\n");
      o = part.getContent();
    } catch (Exception e) {
      System.out.println
        ("Couldn't get content.");
      return false;
    }
    if (o instanceof String) {
      String lMessage = (String) o;
      System.out.println(lMessage);
      return true;
    } else if (o instanceof Multipart) {
      Multipart lPart = (Multipart)o;
      try {
    int count = lPart.getCount();
    for (int i = 0; i < count; i++)
      return filter(lPart.getBodyPart(i));
      } catch (Exception e) {
    System.out.println
      ("Couldn't access parts.");
    e.printStackTrace();
      }
    } else if (o instanceof Message) {
      return filter((Part)o);
    } else if (o instanceof InputStream) {
      BufferedReader lReader = new BufferedReader
    (new InputStreamReader((InputStream) o));
      int c;
      StringBuffer lBuffer = new StringBuffer();
      try {
    while ((c = lReader.read()) != -1)
      lBuffer.append((char) c);
    lReader.close();
      } catch (Exception e) {
    System.out.println("Read error.");
    e.printStackTrace();
      }
      System.out.println(lBuffer.toString());
      return true;
    }
    return false;
  }


}

      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.