15.1. Email and JavaMailThe vast majority of JavaMail applications use the system to interact with Internet email. While it's safe to assume that anyone reading this book has some conceptual familiarity with electronic mail, a quick overview of Internet-based email from a technical perspective is in order. If you would like more details about email services, see Programming Internet Email by David Wood (O'Reilly). The primary mechanism for email transport over the Internet is the Simple Mail Transfer Protocol (SMTP ), as defined in RFC 822. SMTP clients, such as desktop email programs, send messages by connecting to another SMTP server and transmitting a series of headers followed by a message body. Once the SMTP server receives a message, it is either stored for access by local users or forwarded to another SMTP server nearer to its destination. Most ISP and corporate mail servers will accept messages only for delivery to local users or from local users for delivery elsewhere. In the latter case, the SMTP server is responsible for determining the appropriate destination for the message. If the SMTP server determines that a message is for local delivery (for instance, mail.oreilly.com handles incoming email for O'Reilly Media), it handles the message in a number of ways. Early SMTP-based email systems just stored the incoming messages in the Unix mailbox format for retrieval off the local disk by mail reader software. However, as systems grew and mail services became decoupled from other systems, a need arose for a standardized way to access centralized "post office" systems that pooled the incoming mail for an entire domain (or, in the case of many ISPs, multiple domains). At a minimum, this involved providing access to Unix mailboxes over the Internet. POP, which stands for Post Office Protocol, was the first successful attempt to do this. A mail client (such as Outlook, PINE, or Eudora) can use POP to connect to a POP server and download the messages waiting for a particular user. POP has subsequently been replaced with POP-3 (which is universally supported) and IMAP, a more flexible successor that has rapidly gained acceptance since Version 4 was introduced in 1994. Figure 15-1 shows the path of a typical email message from the sender's email client to the recipient's email client. Note that this is a very simple case, with two SMTP servers relaying mail over the long haul (like AT&T long distance) and a or IMAP server at the receiving end (acting like a Baby Bell). Depending on server and client configurations, there may be only one SMTP server involved, or there may be several as messages are routed from ISP to ISP. Figure 15-1. Basic email routing15.1.1. JavaMail LayersAt this point, we'll give you a brief overview of the JavaMail layers . The abstract layer of the JavaMail APIs can be divided into three categories: sessions, services, and messages. Sessions contain information about the current user, mail hosts, and protocols. They serve as factories for various types of objects. Messages are exactly that: text sent through the mail system. Services allow you to do useful things with messages and are subdivided (as of JavaMail 1.2) into two categories: stores and transports. Stores act as containers for sets of messages, while transports route messages from one site to another. As we mentioned earlier, most of the JavaMail API consists of abstract classes. When necessary, however, implementations of those classes are provided by an implementation layer. Implementation layers may include SMTP, IMAP, NNTP, or POP-3 protocols or a message storage system. The implementation layer handles the actual message processing tasks. 15.1.2. Installing and Configuring JavaMailIf you're using J2EE 1.3 or 1.4, which include JavaMail 1.2 and 1.3 respectively, you need not download JavaMail. Otherwise, you can download the standalone JavaMail 1.3 distribution, suitable for inclusion in any application, from http://java.sun.com/products/javamail. You will also need the JavaBeans Activation Framework, which contains the javax.activation classes used to handle certain content formatting tasks. JAF is available from http://java.sun.com/products/javabeans/jaf. If you're not in a full J2EE configuration, install JavaMail by adding JAF's activation.jar and JavaMail's mail.jar to your system CLASSPATH or your JDK's lib directory. If you look at the JavaMail archive, you'll notice some additional jars: mailapi.jar, imap.jar, smtp.jar, and pop3.jar. The mail.jar file contains the contents of all four files, but you can use the smaller jars to selectively include service implementations in your environment. Using the smaller jar files helps reduce loading time for applets. Note that some J2EE application servers may package the JavaMail and JAF classes differently. Consult your server's documentation for details. By simply adding these jar files to the CLASSPATH, most JavaMail programs should compile and execute. However, some additional configuration options are available. JavaMail allows you to set a number of system properties to provide email-related system defaults. In addition to the eight properties listed in Table 15-1, each service has its own set of specific properties. Note that you don't need to set any of these to use the APIs.
15.1.2.1. Provider registriesJavaMail is designed to allow third parties to provide service implementations. JavaMail identifies the installed providers via the javamail.providers and javamail.address.map registry files. When starting up a JavaMail session, the system searches for javamail.providers files in the following locations:[*]
The first entries found provide the defaults unless specifically changed by the application; if two POP3 providers are found, the first one identified becomes the default. The javamail.providers file is formatted like this: protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=SunMicrosystems,Inc; version=1.2; The semicolon is the field delimiter. The javamail.address.map file is located in the same fashion as javamail.providers (including the javamail.default.address.map file in mail.jar) and contains mappings of address types to transport services. For example, messages to RFC 822-formatted addresses are transported via the SMTP protocol, and this relationship is indicated by a line in the javamail.address.map file like this one: rfc822=smtp Unless you are developing your own service providers, you probably won't need to alter any of these files. 15.1.3. The Mail SessionAs we mentioned earlier, the JavaMail API provides a Session class that fills two roles. First, it stores messaging-related properties, both global and user-specific, and provides this information to most new JavaMail objects via their constructors. Second, it provides factory methods for Store and transport objects. The JavaMail API provides two types of Session objects: the default session and local sessions . The default session is created only once using the static Session.getDefaultInstance( ) method. There are two overloaded versions of the getdefaultInstance( ) method: one accepts a java.util.Properties object, and one accepts a Properties object and a javax.mail.Authenticator. The Properties object can be use to set JavaMail system properties.[*] The Authenticator can also be used to limit access to the default session; if the first call to getdefaultInstance( ) includes an Authenticator, all subsequent calls must pass either the same instance of the Authenticator object or an instance from the same class loader as the original Authenticator object. Note that no Authenticator implementations are provided with the basic JavaMail distributionyou have to create your own by subclassing the abstract javax.mail.Authenticator class provided by the API.
The Session.getInstance( ) method behaves the same as geTDefaultInstance( ), but also provides a local instance of the Session object that is not shared across the JVM. With this approach, the Authenticator is used only to determine usernames and passwords for services that require them. The following code sample retrieves a default session, using the JVM system properties to define JavaMail properties and using an Authenticator implementation called myAuthenticator. It then tries to retrieve a second reference to the default session without using an authenticator. If this fails (which, in this case, it will), the code gets a local instance instead. Session session = Session.getDefaultInstance(System.getProperties( ), new MyAuthenticator( )); // Try to get it again, without the authenticator; get local // instance on failure Session sess2 = null; try { sess2 = Session.getDefaultInstance(System.getProperties( ), null); } catch(SecurityException se) { sess2 = Session.getInstance(System.getProperties( ), null); } 15.1.3.1. AuthenticatorsBesides controlling access to the default session, javax.mail.Authenticator objects are also used to provide usernames and passwords to messaging services such as mail servers. This is accomplished by extending the abstract Authenticator class and overriding the getPasswordAuthentication( ) method, which returns a JavaMail PasswordAuthentication object: class BasicAuthenticator extends Authenticator { protected PasswordAuthentication getPasswordAuthentication( ) { return new PasswordAuthentication("scott", "tw7182"); } The PasswordAuthentication object is simple: it just serves as a container for the two strings. In most applications, these would be entered by the user via a dialog box or retrieved from some centralized directory service. Since Authenticator objects are global to the session, the base Authenticator class contains a number of final methods, which can be used to provide information about the particular authentication requestfor instance, using getrequestingProtocol( ) and getrequestingSite( ), the getPasswordAuthentication( ) code can determine whether to provide the IMAP login information for mailserver.myco.com or the POP3 login information for pop.prep.edu. 15.1.3.2. ProvidersA Session itself doesn't provide any mail-handling capabilitiesit simply provides the means to access other messaging services. Generally, you will know what sort of messaging protocols and services are available before sitting down to code your application and won't need to determine them on the fly. However, the javax.mail.Provider object provides a convenient view of the implementation details for particular services: the protocol (such as SMTP), the service type (message storage or message transport), vendor, version, and implementing class name. For most applications, you don't actually need to worry about the Provider object, because you don't need access to the object itself to access the features it provides. However, if you do need one, the getProviders( ) method of Session will allow you to interrogate the current session to determine which service providers are installed. This method returns an array of Provider objects describing the different protocol implementations available to the session. To retrieve the Provider object for a particular protocol, call the getProvider(String) method and pass it the protocol name, as in the following: mySession.getProvider("smtp"); By default, provider information is specified in the javamail.providers and javamail.default.providers resource files. This means that if you are using the providers in the Sun JavaMail distribution, you don't have to do anything to obtain access to the standard services. If you want to programmatically install your own implementation of a particular protocol, you can pass a vendor-supplied Provider object to the setProvider( ) method of Session. 15.1.3.3. URL namesJavaMail provides a number of mechanisms for identifying services, including the system properties discussed earlier in this chapter. JavaMail services can also be identified via URLs in the form protocol://server. An IMAP URL, for example, might look like imap://mailserver.cs.edu. The javax.mail.URLName object is used to encapsulate information about a connection to a mail service. This can be quite convenient, since it allows all necessary information about a connection to be assembled in a centralized location. Since service URLs are protocol-specific, this can include an arbitrary range of precision IMAP URLs, for instance, allow the specification of particular messages and folders within the IMAP message store.[*]
A URLName object is only a container for connection informationhence the "Name." It doesn't actually connect to the service it identifies. Instead, it can be passed to various methods of Session, including getFolder( ), getStore( ), and gettransport( ). URLName objects can be retrieved from existing service connections via the getURLName( ) method of Service (we'll look at specific services later in this chapter). They can also be created directly via three constructors: URLName(Stringurl) URLName(java.net.URLurl) URLName(java.lang.String protocol, java.lang.String host, int port, java.lang.String file, java.lang.String username, java.lang.String password) The first two are fairly straightforward, accepting a raw URL in a String format or encapsulated as a java.net.URL object. The third optionthe detailed constructorallows you to build up the URL from its individual components, including some components (i.e., password) that might not be included in the actual URL at all. Not all options apply to all protocols, so to keep the default value for each field, pass in null. The obvious exception is the port parameter, which accepts -1 as its default value indicator. The other way to associate login information with a URLName (particularly if the URLName was created by some mechanism other than the detailed constructor) is to associate a PasswordAuthentication object with the URL. This is done at the session level by using Session.setPasswordAuthentication(URLName, PasswordAuthentication) and Session.getPasswordAuthentication(URLName). Once a PasswordAuthentication object has been associated with a URLName, it will be used to handle authentication for future connections to that URL. |