JavaMail quick start -- Send and receive email with the JavaMail APIs (转-来自JavaWorld)

In JavaMail you'll find APIs and provider implementations allowing you to develop fully functional email client applications. "Email client applications" invokes thoughts of Microsoft Outlook; and, yes, you could write your own Outlook replacement. But an email client need not reside on a client machine at all. Indeed, it could be a servlet or an EJB running on a remote server, providing end-user access to email via a Web browser. Think of Hotmail (yes, you could write your own version of Hotmail too). Or you could avoid a user interface altogether. How about an auto-responder that reads incoming messages and sends replies, customized according to the original sender?

In my own pet project, a talking email client reads -- that is, speaks -- incoming messages. It's based on a refinement of an idea I introduced in "Talking Java!" I'll tell you more about it later.

For now, start by installing and configuring the JavaMail software.

Setup
If you use Java 2 Platform, Enterprise Edition (J2EE) 1.3, you're in luck: it includes JavaMail, so no additional setup is required. If, however, you're running Java 2 Platform, Standard Edition (J2SE) 1.1.7 and upwards, and you want email capability for your applications, download and install the following:

To install, simply unzip the downloaded files and add the contained jar files to your classpath. As an example, here's my classpath for this project:

.;C:/Apps/Java/javamail-1.2/mail.jar;C:/Apps/Java
/javamail-1.2/mailapi.jar;C:/Apps/Java/javamail-1.2
/pop3.jar;C:/Apps/Java/javamail-1.2/smtp.jar;C:/Apps
/Java/jaf-1.0.1/activation.jar

.;C:/Apps/Java/javamail-1.2/mail.jar;C:/Apps/Java
/javamail-1.2/mailapi.jar;C:/Apps/Java/javamail-1.2
/pop3.jar;C:/Apps/Java/javamail-1.2/smtp.jar;C:/Apps
/Java/jaf-1.0.1/activation.jar

The mailapi.jar file contains the core API classes, while the pop3.jar and smtp.jar files contain the provider implementations for the respective mail protocols. (We won't use the imap.jar file in this article.) Think of the provider implementations as similar to JDBC (Java Database Connectivity) drivers, but for messaging systems rather than databases. As for the mail.jar file, it contains each of the above jar files, so you could restrict your classpath to just the mail.jar and activation.jar files.

The activation.jar file allows you to handle MIME (Multipurpose Internet Mail Extensions) types accessible via binary data streams. Look for the DataHandler class in the Not Just Plain Text section later.

For the record, the rest of this article does not offer comprehensive API coverage; rather, you will learn by doing. If it's in-depth API information you're looking for, then look at the PDF files and Javadocs included in the respective download bundles.

Once you've installed the software, you need to get email account details to run the examples that follow. You'll need your ISP's SMTP (Simple Mail Transfer Protocol) server name and POP (Post Office Protocol) server name, your email account login name, and your mailbox password. Figure 1 shows my details -- not the real ones, you understand -- as used by Microsoft Outlook.


Figure 1. Tony's Internet mail settings

Figure 1. Tony's Internet mail settings

Sending email via SMTP
The first example shows how to send a basic email message via SMTP. Below, you'll find the SimpleSender class, which takes your message's details from the command line and calls a separate method -- send(...) -- to send it:

package com.lotontech.mail;

import javax.mail.*;
import javax.mail.internet.*;

import java.util.*;

/**
  * A simple email sender class.
  */
public class SimpleSender
{

  /**
    * Main method to send a message given on the command line.
    */
  public static void main(String args[])
  {
    try
    {
      String smtpServer=args[0];
      String to=args[1];
      String from=args[2];
      String subject=args[3];
      String body=args[4];

      send(smtpServer, to, from, subject, body);
    }
    catch (Exception ex)
    {
      System.out.println("Usage: java com.lotontech.mail.SimpleSender"
       +" smtpServer toAddress fromAddress subjectText bodyText");
    }

    System.exit(0);
  }

package com.lotontech.mail;

import javax.mail.*;
import javax.mail.internet.*;

import java.util.*;

/**
  * A simple email sender class.
  */
public class SimpleSender
{

  /**
    * Main method to send a message given on the command line.
    */
  public static void main(String args[])
  {
    try
    {
      String smtpServer=args[0];
      String to=args[1];
      String from=args[2];
      String subject=args[3];
      String body=args[4];

      send(smtpServer, to, from, subject, body);
    }
    catch (Exception ex)
    {
      System.out.println("Usage: java com.lotontech.mail.SimpleSender"
       +" smtpServer toAddress fromAddress subjectText bodyText");
    }

    System.exit(0);
  }

Next, run SimpleSender as below. Replace smtp.myISP.net with your own SMTP server, as derived from your mail settings:

> java com.lotontech.mail.SimpleSender smtp.myISP.net bill@lotontech.com ben@lotontech.com "Hello" "Just to say Hello."

> java com.lotontech.mail.SimpleSender smtp.myISP.net bill@lotontech.com ben@lotontech.com "Hello" "Just to say Hello."

And, if it works, at the receiving end you'll see something like what's shown in Figure 2.


Figure 2. Message received from SimpleSender

Figure 2. Message received from SimpleSender

The send(...) method completes the SimpleSender class. I'll show the code first, then detail the theory:

  /**
    * "send" method to send the message.
    */
  public static void send(String smtpServer, String to, String from
   , String subject, String body)
  {
    try
    {
      Properties props = System.getProperties();

      // -- Attaching to default Session, or we could start a new one --

      props.put("mail.smtp.host", smtpServer);
      Session session = Session.getDefaultInstance(props, null);

      // -- Create a new message --
      Message msg = new MimeMessage(session);

      // -- Set the FROM and TO fields --
      msg.setFrom(new InternetAddress(from));
      msg.setRecipients(Message.RecipientType.TO,
        InternetAddress.parse(to, false));

      // -- We could include CC recipients too --
      // if (cc != null)
      // msg.setRecipients(Message.RecipientType.CC
      // ,InternetAddress.parse(cc, false));

      // -- Set the subject and body text --
      msg.setSubject(subject);
      msg.setText(body);

      // -- Set some other header information --
      msg.setHeader("X-Mailer", "LOTONtechEmail");
      msg.setSentDate(new Date());

      // -- Send the message --
      Transport.send(msg);

      System.out.println("Message sent OK.");
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

  /**
    * "send" method to send the message.
    */
  public static void send(String smtpServer, String to, String from
   , String subject, String body)
  {
    try
    {
      Properties props = System.getProperties();

      // -- Attaching to default Session, or we could start a new one --

      props.put("mail.smtp.host", smtpServer);
      Session session = Session.getDefaultInstance(props, null);

      // -- Create a new message --
      Message msg = new MimeMessage(session);

      // -- Set the FROM and TO fields --
      msg.setFrom(new InternetAddress(from));
      msg.setRecipients(Message.RecipientType.TO,
        InternetAddress.parse(to, false));

      // -- We could include CC recipients too --
      // if (cc != null)
      // msg.setRecipients(Message.RecipientType.CC
      // ,InternetAddress.parse(cc, false));

      // -- Set the subject and body text --
      msg.setSubject(subject);
      msg.setText(body);

      // -- Set some other header information --
      msg.setHeader("X-Mailer", "LOTONtechEmail");
      msg.setSentDate(new Date());

      // -- Send the message --
      Transport.send(msg);

      System.out.println("Message sent OK.");
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

First, notice that you're obtaining a mail session (java.mail.Session), without which you can do nothing. In this case you're calling Session.getDefaultInstance(...) to get a shared session, which other desktop applications may reuse; you can also set up an entirely new session -- via the Session.getInstance(...) method -- that would be unique to your application. The latter could prove important for email clients not isolated on a per-user basis, such as a Web-based email system implemented with servlets.

Establishing a session requires you to set certain properties; at minimum, you need the mail.smtp.host property if you're sending messages via SMTP. You'll find other properties described within the API documentation.

Once you have a session, create a message. In this example, you're setting the message's from and to email addresses, the subject, and the body text, all taken originally from the command line. You're also setting some header information, including the date, and you can specify cc recipients if you want.

Finally, you send the message via the javax.mail.Transport class. If you wonder how it knows about our mail session, look back at the message's constructor.

Not just plain text
The setText(...) convenience method in class javax.mail.Message (inherited from the javax.mail.Part interface) sets the message content to the supplied string and sets the MIME type to text/plain.

You're not limited to plain text, though: you can send other content types via the setDataHandler(...) method. In most cases you can take "other content types" to mean file attachments, such as Word documents, but for something a bit more interesting, check out this code for sending a Java serialized object:

ByteArrayOutputStream byteStream=new ByteArrayOutputStream();
ObjectOutputStream objectStream=new ObjectOutputStream(byteStream);
objectStream.writeObject(theObject);

msg.setDataHandler(new DataHandler( new ByteArrayDataSource( byteStream.toByteArray(), "lotontech/javaobject" )));

ByteArrayOutputStream byteStream=new ByteArrayOutputStream();
ObjectOutputStream objectStream=new ObjectOutputStream(byteStream);
objectStream.writeObject(theObject);

msg.setDataHandler(new DataHandler( new ByteArrayDataSource( byteStream.toByteArray(), "lotontech/javaobject" )));

You won't find the DataHandler class within the javax.mail.* package structure because it belongs to the JavaBeans Activation Framework (JAF) package javax.activation. Remember, you downloaded the JAF distribution as well as JavaMail. JAF provides a mechanism for handling typed data content, which for Internet content means MIME types.

And if you really do try the code above for sending a Java object by email, you'll have trouble locating the ByteArrayDataSource class, as neither mail.jar nor activation.jar include it. Try looking in the JavaMail demo directory!

As for those file attachments that you're more likely to be interested in initially, you would create a javax.activation.FileDataSource instance in the DataHandler's constructor. Of course, you're not likely to send a file alone; rather, it will probably be an attachment to a text message. For that you need to understand the concept of multipart messages, so I'll introduce that concept now, in the context of receiving email.

Receive email via POP3
Earlier, I introduced the javax.mail.Part interface implemented by javax.mail.Message. I'll now explain its message parts, which are important in this example. To start, take a look at Figure 3.

Figure 3 shows a Message as created in the previous example that is both a message and message part, because it implements the Part interface. For any part, you can get its content (any Java object), and, in the case of a simple text message, the content object may be a String. For a multipart message, the content will be of type Multipart, from which we can get hold of the individual body parts, which themselves implement the Part interface.


Figure 3. UML diagram for the mail.Part interface

Figure 3. UML diagram for the mail.Part interface

In practice, all will become apparent as you step through the code for a SimpleReceiver class, which I'll present in three sections: first, the class definition and the main(...) method that takes connection details from the command line; second, the receive(...) method that captures and steps through the incoming messages; and finally, the printMessage(...) method that prints the header information and content of each message.

Here's the first section:

package com.lotontech.mail;

import javax.mail.*;
import javax.mail.internet.*;

import java.util.*;
import java.io.*;

/**
  * A simple email receiver class.
  */
public class SimpleReceiver
{

  /**
    * Main method to receive messages from the mail server specified
    * as command line arguments.
    */
  public static void main(String args[])
  {
    try
    {
      String popServer=args[0];
      String popUser=args[1];
      String popPassword=args[2];

      receive(popServer, popUser, popPassword);
    }
    catch (Exception ex)
    {
      System.out.println("Usage: java com.lotontech.mail.SimpleReceiver"
       +" popServer popUser popPassword");
    }

    System.exit(0);
  }

package com.lotontech.mail;

import javax.mail.*;
import javax.mail.internet.*;

import java.util.*;
import java.io.*;

/**
  * A simple email receiver class.
  */
public class SimpleReceiver
{

  /**
    * Main method to receive messages from the mail server specified
    * as command line arguments.
    */
  public static void main(String args[])
  {
    try
    {
      String popServer=args[0];
      String popUser=args[1];
      String popPassword=args[2];

      receive(popServer, popUser, popPassword);
    }
    catch (Exception ex)
    {
      System.out.println("Usage: java com.lotontech.mail.SimpleReceiver"
       +" popServer popUser popPassword");
    }

    System.exit(0);
  }

I'll take you through a proper test drive later, but for now here is the command line to run it (remember to replace the command arguments with your mail settings):

> java com.lotontech.mail.SimpleReceiver pop.myIsp.net myUserName myPassword

> java com.lotontech.mail.SimpleReceiver pop.myIsp.net myUserName myPassword

The receive(...) method -- called from main(...) -- opens your POP3 INBOX and steps through the messages in turn, each time calling printMessage(...). Here is the code:

  /**
    * "receive" method to fetch messages and process them.
    */
  public static void receive(String popServer, String popUser
   , String popPassword)
  {

    Store store=null;
    Folder folder=null;

    try
    {
      // -- Get hold of the default session --
      Properties props = System.getProperties();
      Session session = Session.getDefaultInstance(props, null);

      // -- Get hold of a POP3 message store, and connect to it --
      store = session.getStore("pop3");
      store.connect(popServer, popUser, popPassword);
      
      // -- Try to get hold of the default folder --
      folder = store.getDefaultFolder();
      if (folder == null) throw new Exception("No default folder");

      // -- ...and its INBOX --
      folder = folder.getFolder("INBOX");
      if (folder == null) throw new Exception("No POP3 INBOX");

      // -- Open the folder for read only --
      folder.open(Folder.READ_ONLY);

      // -- Get the message wrappers and process them --
      Message[] msgs = folder.getMessages();
      for (int msgNum = 0; msgNum < msgs.length; msgNum++)
      {
        printMessage(msgs[msgNum]);
      }

    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    finally
    {
      // -- Close down nicely --
      try
      {
        if (folder!=null) folder.close(false);
        if (store!=null) store.close();
      }
      catch (Exception ex2) {ex2.printStackTrace();}
    }
  }

  /**
    * "receive" method to fetch messages and process them.
    */
  public static void receive(String popServer, String popUser
   , String popPassword)
  {

    Store store=null;
    Folder folder=null;

    try
    {
      // -- Get hold of the default session --
      Properties props = System.getProperties();
      Session session = Session.getDefaultInstance(props, null);

      // -- Get hold of a POP3 message store, and connect to it --
      store = session.getStore("pop3");
      store.connect(popServer, popUser, popPassword);
      
      // -- Try to get hold of the default folder --
      folder = store.getDefaultFolder();
      if (folder == null) throw new Exception("No default folder");

      // -- ...and its INBOX --
      folder = folder.getFolder("INBOX");
      if (folder == null) throw new Exception("No POP3 INBOX");

      // -- Open the folder for read only --
      folder.open(Folder.READ_ONLY);

      // -- Get the message wrappers and process them --
      Message[] msgs = folder.getMessages();
      for (int msgNum = 0; msgNum < msgs.length; msgNum++)
      {
        printMessage(msgs[msgNum]);
      }

    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    finally
    {
      // -- Close down nicely --
      try
      {
        if (folder!=null) folder.close(false);
        if (store!=null) store.close();
      }
      catch (Exception ex2) {ex2.printStackTrace();}
    }
  }

Notice that you're obtaining a POP3 message-store wrapper from the session, then connecting to it using the mail settings originally supplied on the command line.

Once connected, you get a handle on the default folder -- effectively the root of the folder tree -- and, from there, the INBOX folder that holds the inbound messages. You open the INBOX for read-only access; you get hold of the messages and step through them one by one.

As an aside, you might wonder if you would ever want to open the INBOX for write access. You would if you intended to mark the messages as received and/or remove them from the server. In our example, you're only looking at them.

Finally, in the code above you're taking care to close the folder and the message store when finished, which leaves only the printMessage(...) method to complete this class.

Print the messages
In this section, the earlier javax.mail.Part interface discussion becomes relevant.

In the code that follows you will see how to implicitly cast the message to its Part interface and assign it to the messagePart variable. For single-part messages you now have something to print.

If a call to messagePart.getContent() yields a Multipart instance, you know you're dealing with a multipart message; in that case, you're getting hold of the first multipart -- via getBodyPart(0) -- and printing it.

Whether you've got a hold of the message itself, or only the first body part, you're printing it only if the content is plain text or HTML, and you're doing so by reading from an InputStream:

  /**
    * "printMessage()" method to print a message.
    */
  public static void printMessage(Message message)
  {
    try
    {
      // Get the header information
      String from=((InternetAddress)message.getFrom()[0]).getPersonal();
      if (from==null) from=((InternetAddress)message.getFrom()[0])
       .getAddress();
      System.out.println("FROM: "+from);

      String subject=message.getSubject();
      System.out.println("SUBJECT: "+subject);

      // -- Get the message part (i.e. the message itself) --
      Part messagePart=message;
      Object content=messagePart.getContent();

      // -- or its first body part if it is a multipart message --
      if (content instanceof Multipart)
      {
        messagePart=((Multipart)content).getBodyPart(0);
        System.out.println("[ Multipart Message ]");
      }

      // -- Get the content type --
      String contentType=messagePart.getContentType();

      // -- If the content is plain text, we can print it --
      System.out.println("CONTENT:"+contentType);

      if (contentType.startsWith("text/plain")
       || contentType.startsWith("text/html"))
      {
        InputStream is = messagePart.getInputStream();

        BufferedReader reader
         =new BufferedReader(new InputStreamReader(is));
        String thisLine=reader.readLine();

        while (thisLine!=null)
        {
          System.out.println(thisLine);
          thisLine=reader.readLine();
        }
      }

      System.out.println("-----------------------------");
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

  /**
    * "printMessage()" method to print a message.
    */
  public static void printMessage(Message message)
  {
    try
    {
      // Get the header information
      String from=((InternetAddress)message.getFrom()[0]).getPersonal();
      if (from==null) from=((InternetAddress)message.getFrom()[0])
       .getAddress();
      System.out.println("FROM: "+from);

      String subject=message.getSubject();
      System.out.println("SUBJECT: "+subject);

      // -- Get the message part (i.e. the message itself) --
      Part messagePart=message;
      Object content=messagePart.getContent();

      // -- or its first body part if it is a multipart message --
      if (content instanceof Multipart)
      {
        messagePart=((Multipart)content).getBodyPart(0);
        System.out.println("[ Multipart Message ]");
      }

      // -- Get the content type --
      String contentType=messagePart.getContentType();

      // -- If the content is plain text, we can print it --
      System.out.println("CONTENT:"+contentType);

      if (contentType.startsWith("text/plain")
       || contentType.startsWith("text/html"))
      {
        InputStream is = messagePart.getInputStream();

        BufferedReader reader
         =new BufferedReader(new InputStreamReader(is));
        String thisLine=reader.readLine();

        while (thisLine!=null)
        {
          System.out.println(thisLine);
          thisLine=reader.readLine();
        }
      }

      System.out.println("-----------------------------");
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

For simplicity I've assumed that the message itself, or the first body part, is printable. For any real application, you would want to step through every body part in turn and for each one take some appropriate action -- print it or save it to disk, depending on the content type.

As you fetch each message from the message store, you're actually getting hold of a lightweight wrapper. The data content itself is fetched using a lazy approach, at the time you ask for it -- useful if you wish to download only the message headers initially.

SimpleReceiver test drive
Let's take the SimpleReceiver for a test drive. To give it something to receive, I sent out the message illustrated in Figure 4 (notice that the message comprises text and an attachment).


Figure 4. Test message for SimpleReceiver

Figure 4. Test message for SimpleReceiver

Upon reception it was identified as a multipart message. The text printed as:

FROM: Tony Loton
SUBJECT: Number 1
[ Multipart Message ]
CONTENT:text/plain;
        charset="iso-8859-1"
Attachment 1
from Tony Loton.
-----------------------------

Speak the messages
For a bit of fun, and to demonstrate a novel use of the JavaMail APIs, I now briefly turn to my talking email project. To try this out you'll need the lotontalk.jar file (supplied), and you'll need to include it in your classpath, as in:

set CLASSPATH=%CLASSPATH%;lotontalk.jar

set CLASSPATH=%CLASSPATH%;lotontalk.jar

You'll also need to make two code changes within the SimpleReceiver class. First, inside the receive(...) method, replace this code:

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
}

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
}

with:

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
  speakMessage(msgs[msgNum]);
}

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
  speakMessage(msgs[msgNum]);
}

Now add the following new method, speakMessage(...), which is similar to the original printMessage() method:

/**
  * "speakMessage", a talking version of printMessage().
  */
public static void speakMessage(Message message)
{
  String speech="";

  try
  {
    com.lotontech.talk.LOTONtalk speaker
     =new com.lotontech.talk.LOTONtalk();

    String from=((InternetAddress)message.getFrom()[0]).getPersonal();
    if (from==null) from=((InternetAddress)message.getFrom()[0])
     .getAddress();
    speech=speech+"from "+from+", ";

    String subject=message.getSubject();
    speech=speech+"subject "+subject+", ";

    // -- Get the message part (i.e., the message itself) --
    Part messagePart=message;
    Object content=messagePart.getContent();

    // -- ...or its first body part if it is a multipart message --
    if (content instanceof Multipart)
      messagePart=((Multipart)content).getBodyPart(0);

    String contentType=messagePart.getContentType();

    if (contentType.startsWith("text/plain")
     || contentType.startsWith("text/html"))
    {
      InputStream is = messagePart.getInputStream();

      BufferedReader reader
       =new BufferedReader(new InputStreamReader(is));
      String thisLine=reader.readLine();

      while (thisLine!=null)
      {
        speech=speech+thisLine+". ";
        thisLine=reader.readLine();
      }

      // -- SPEAK --
      speaker.speak(speech,true);
    }
  }
  catch (Exception ex)
  {
    ex.printStackTrace();
  }
}

/**
  * "speakMessage", a talking version of printMessage().
  */
public static void speakMessage(Message message)
{
  String speech="";

  try
  {
    com.lotontech.talk.LOTONtalk speaker
     =new com.lotontech.talk.LOTONtalk();

    String from=((InternetAddress)message.getFrom()[0]).getPersonal();
    if (from==null) from=((InternetAddress)message.getFrom()[0])
     .getAddress();
    speech=speech+"from "+from+", ";

    String subject=message.getSubject();
    speech=speech+"subject "+subject+", ";

    // -- Get the message part (i.e., the message itself) --
    Part messagePart=message;
    Object content=messagePart.getContent();

    // -- ...or its first body part if it is a multipart message --
    if (content instanceof Multipart)
      messagePart=((Multipart)content).getBodyPart(0);

    String contentType=messagePart.getContentType();

    if (contentType.startsWith("text/plain")
     || contentType.startsWith("text/html"))
    {
      InputStream is = messagePart.getInputStream();

      BufferedReader reader
       =new BufferedReader(new InputStreamReader(is));
      String thisLine=reader.readLine();

      while (thisLine!=null)
      {
        speech=speech+thisLine+". ";
        thisLine=reader.readLine();
      }

      // -- SPEAK --
      speaker.speak(speech,true);
    }
  }
  catch (Exception ex)
  {
    ex.printStackTrace();
  }
}

Because you're accumulating the whole message text in a string before speaking it, this solution may be suitable only for small messages. Alternatively, you could speak each line of text as you read it.

Of course, it's impossible for me to show the result visually, so you'll have to try it for yourself. You have everything you need, and, though it's not yet half as good as I'd like it to be, you'll get a feel for where it's heading.

With a little experimentation, possibly outside of this example, you will discover some interesting features of the speech synthesis: how it handles numbers, and how it assumes wholly capitalized words to be acronyms, thus S-P-E-L-L-I-N-G them out.

For further consideration
We've covered quite a bit of ground here, putting in place the basic building blocks for applications that send and receive email messages. As a starting point, it's not difficult to get messages into -- and out of -- your Java applications, is it?

Yet there is more to learn if you look deeper. Here is a rundown of what we've covered and what you might look at next.

Send messages
We looked at how to send single-part messages via the SMTP protocol, though I did hint at how you could send nontext content -- i.e., a Java serialized object -- as the single message part.

Later, we looked at the Part interface in the context of multipart messages. You might now like to apply that knowledge to sending messages. How about sending a multipart message comprising some plain text and a file attachment?

Receive messages
We looked at receiving multipart messages, but for viewing only. The messages we receive remain on the server, to be reread as many times as we like, until we remove them. How would we do that? To change the status of messages on the server, consider this line of code:

message.setFlag(Flags.Flag.ANSWERED,true);

message.setFlag(Flags.Flag.ANSWERED,true);

As an illustration for sending nontext content, I introduced the idea of writing a Java serialized object into a mail message. Could you complete the picture by now receiving such objects, as the basis of a kind of asynchronous Object Request Broker for disconnected applications? Believe it or not, I did something like that, and it's not as ridiculous as it sounds for field engineers who need to send batches of updates to client records whenever they get the chance to go online. Before you all start shouting, I know it's more fashionable these days to use XML -- rather than serialized objects -- for that! If you're interested, the receiving code looks like this:

InputStream is = (InputStream) message.getContent();
ObjectInputStream objectStream=new ObjectInputStream(is);
Object theObject=objectStream.readObject();

InputStream is = (InputStream) message.getContent();
ObjectInputStream objectStream=new ObjectInputStream(is);
Object theObject=objectStream.readObject();

Talking email
Talking email may interest you in terms of a desktop application that reads incoming messages, but I have another idea. Several ISPs -- in the UK at least -- provide a SpeechMail service that allows you to dial a telephone number from anywhere in the world and have your INBOX messages spoken to you. You can see the kind of service I mean at http://www.btinternet.com.

I don't think that this service is built with Java, but it is possible -- and the speech sounds distinctly allophonic, meaning that the BT Internet developers have taken more or less the same approach to speech synthesis as I have.

Finally
I hope I've provided a good starting point for your email-enabled Java applications, with a few ideas thrown in for where to take it next. I'll judge the usefulness of this article by the number of Java-based email clients and Webmail sites that spring up over the coming months!

About the author
Tony Loton works through his company -- LOTONtech Limited -- to provide software solutions, consultancy, training, and technical writing services. The writing bug seems to have bitten Tony this year, with his involvement in book projects for John Wiley & Sons and Wrox Press.


FROM: Tony Loton
SUBJECT: Number 1
[ Multipart Message ]
CONTENT:text/plain;
        charset="iso-8859-1"
Attachment 1
from Tony Loton.
-----------------------------

Speak the messages
For a bit of fun, and to demonstrate a novel use of the JavaMail APIs, I now briefly turn to my talking email project. To try this out you'll need the lotontalk.jar file (supplied), and you'll need to include it in your classpath, as in:

set CLASSPATH=%CLASSPATH%;lotontalk.jar

set CLASSPATH=%CLASSPATH%;lotontalk.jar

You'll also need to make two code changes within the SimpleReceiver class. First, inside the receive(...) method, replace this code:

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
}

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
}

with:

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
  speakMessage(msgs[msgNum]);
}

// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);
  speakMessage(msgs[msgNum]);
}

Now add the following new method, speakMessage(...), which is similar to the original printMessage() method:

/**
  * "speakMessage", a talking version of printMessage().
  */
public static void speakMessage(Message message)
{
  String speech="";

  try
  {
    com.lotontech.talk.LOTONtalk speaker
     =new com.lotontech.talk.LOTONtalk();

    String from=((InternetAddress)message.getFrom()[0]).getPersonal();
    if (from==null) from=((InternetAddress)message.getFrom()[0])
     .getAddress();
    speech=speech+"from "+from+", ";

    String subject=message.getSubject();
    speech=speech+"subject "+subject+", ";

    // -- Get the message part (i.e., the message itself) --
    Part messagePart=message;
    Object content=messagePart.getContent();

    // -- ...or its first body part if it is a multipart message --
    if (content instanceof Multipart)
      messagePart=((Multipart)content).getBodyPart(0);

    String contentType=messagePart.getContentType();

    if (contentType.startsWith("text/plain")
     || contentType.startsWith("text/html"))
    {
      InputStream is = messagePart.getInputStream();

      BufferedReader reader
       =new BufferedReader(new InputStreamReader(is));
      String thisLine=reader.readLine();

      while (thisLine!=null)
      {
        speech=speech+thisLine+". ";
        thisLine=reader.readLine();
      }

      // -- SPEAK --
      speaker.speak(speech,true);
    }
  }
  catch (Exception ex)
  {
    ex.printStackTrace();
  }
}

/**
  * "speakMessage", a talking version of printMessage().
  */
public static void speakMessage(Message message)
{
  String speech="";

  try
  {
    com.lotontech.talk.LOTONtalk speaker
     =new com.lotontech.talk.LOTONtalk();

    String from=((InternetAddress)message.getFrom()[0]).getPersonal();
    if (from==null) from=((InternetAddress)message.getFrom()[0])
     .getAddress();
    speech=speech+"from "+from+", ";

    String subject=message.getSubject();
    speech=speech+"subject "+subject+", ";

    // -- Get the message part (i.e., the message itself) --
    Part messagePart=message;
    Object content=messagePart.getContent();

    // -- ...or its first body part if it is a multipart message --
    if (content instanceof Multipart)
      messagePart=((Multipart)content).getBodyPart(0);

    String contentType=messagePart.getContentType();

    if (contentType.startsWith("text/plain")
     || contentType.startsWith("text/html"))
    {
      InputStream is = messagePart.getInputStream();

      BufferedReader reader
       =new BufferedReader(new InputStreamReader(is));
      String thisLine=reader.readLine();

      while (thisLine!=null)
      {
        speech=speech+thisLine+". ";
        thisLine=reader.readLine();
      }

      // -- SPEAK --
      speaker.speak(speech,true);
    }
  }
  catch (Exception ex)
  {
    ex.printStackTrace();
  }
}

Because you're accumulating the whole message text in a string before speaking it, this solution may be suitable only for small messages. Alternatively, you could speak each line of text as you read it.

Of course, it's impossible for me to show the result visually, so you'll have to try it for yourself. You have everything you need, and, though it's not yet half as good as I'd like it to be, you'll get a feel for where it's heading.

With a little experimentation, possibly outside of this example, you will discover some interesting features of the speech synthesis: how it handles numbers, and how it assumes wholly capitalized words to be acronyms, thus S-P-E-L-L-I-N-G them out.

For further consideration
We've covered quite a bit of ground here, putting in place the basic building blocks for applications that send and receive email messages. As a starting point, it's not difficult to get messages into -- and out of -- your Java applications, is it?

Yet there is more to learn if you look deeper. Here is a rundown of what we've covered and what you might look at next.

Send messages
We looked at how to send single-part messages via the SMTP protocol, though I did hint at how you could send nontext content -- i.e., a Java serialized object -- as the single message part.

Later, we looked at the Part interface in the context of multipart messages. You might now like to apply that knowledge to sending messages. How about sending a multipart message comprising some plain text and a file attachment?

Receive messages
We looked at receiving multipart messages, but for viewing only. The messages we receive remain on the server, to be reread as many times as we like, until we remove them. How would we do that? To change the status of messages on the server, consider this line of code:

message.setFlag(Flags.Flag.ANSWERED,true);

message.setFlag(Flags.Flag.ANSWERED,true);

As an illustration for sending nontext content, I introduced the idea of writing a Java serialized object into a mail message. Could you complete the picture by now receiving such objects, as the basis of a kind of asynchronous Object Request Broker for disconnected applications? Believe it or not, I did something like that, and it's not as ridiculous as it sounds for field engineers who need to send batches of updates to client records whenever they get the chance to go online. Before you all start shouting, I know it's more fashionable these days to use XML -- rather than serialized objects -- for that! If you're interested, the receiving code looks like this:

InputStream is = (InputStream) message.getContent();
ObjectInputStream objectStream=new ObjectInputStream(is);
Object theObject=objectStream.readObject();

InputStream is = (InputStream) message.getContent();
ObjectInputStream objectStream=new ObjectInputStream(is);
Object theObject=objectStream.readObject();

Talking email
Talking email may interest you in terms of a desktop application that reads incoming messages, but I have another idea. Several ISPs -- in the UK at least -- provide a SpeechMail service that allows you to dial a telephone number from anywhere in the world and have your INBOX messages spoken to you. You can see the kind of service I mean at http://www.btinternet.com.

I don't think that this service is built with Java, but it is possible -- and the speech sounds distinctly allophonic, meaning that the BT Internet developers have taken more or less the same approach to speech synthesis as I have.

Finally
I hope I've provided a good starting point for your email-enabled Java applications, with a few ideas thrown in for where to take it next. I'll judge the usefulness of this article by the number of Java-based email clients and Webmail sites that spring up over the coming months!

About the author
Tony Loton works through his company -- LOTONtech Limited -- to provide software solutions, consultancy, training, and technical writing services. The writing bug seems to have bitten Tony this year, with his involvement in book projects for John Wiley & Sons and Wrox Press.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值