Sending Email

The most basic email need of a Java program is to send messages. While email clients like Eudora and mailing list managers like listproc are the only common programs that receive messages, all sorts of programs send messages. For instance, web browsers can submit HTML forms via email. Security scanning tools like Satan can run in the background and email their results to the administrator when they’re done. When the Unix cron program detects a misconfigured crontab file, it emails the error to the owner. Books & Writers runs a service that’s very popular with authors to track the sales rank of their books on Amazon.com and notify them periodically via email. (See http://www.booksandwriters.com/rank.html.) A massively parallel computation like the SETI@home project can submit individual results via email. Some multiplayer games like chess can be played across the network by emailing the moves back and forth (though this scheme wouldn’t work for faster-moving games like Quake or even for speed chess). And these are just a few of the different kinds of programs that send email. In today’s wired world, by far the simplest way to notify a user of an event when he’s not currently sitting in front of the computer that the program is running on is to send him email.

The JavaMail API provides everything your programs need to send email. To send a message, a program just follows these eight steps:

  1. Set the mail.host property to point to the local mail server.

  2. Start a mail session with the Session.getInstance( ) method.

  3. Create a new Message object, probably by instantiating one of its concrete subclasses.

  4. Set the message’s From: address.

  5. Set the message’s To: address.

  6. Set the message’s Subject:.

  7. Set the content of the message.

  8. Send the message with the Transport.send( ) method.

The order of these steps is not especially rigid. For instance, steps 4 through 7 can be performed in any order. Furthermore, each of these steps is individually quite simple.

The first step is to set up the properties for the mail session. The only property you have to set in order to send mail is mail.host. This is configured as a java.util.Properties object rather than an environment variable. For example, this code fragment sets the mail.host property to mail.cloud9.net :

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

Your programs will of course have to set this property to the name of your own mail server. These properties are used to retrieve a Session object from the Session.getInstance( ) factory method like this:

Session mailConnection = Session.getInstance(props, null);

The Session object represents an ongoing communication between a program and one mail server. The second argument to the getInstance( ) method, null here, is a javax.mail.Authenticator that will ask the user for a password if one is requested by the server. We’ll discuss this more later in the section on password authentication. Most of the time, you do not need to provide a username and password to send email, only to receive it.

The Session object is used to construct a new Message object:

Message msg = new MimeMessage(mailConnection);

I specify the MimeMessage class in particular since I know I’m sending Internet email. However, this is the one place where I do explicitly choose a format for the email message. In some cases, this may not be necessary if I can copy the incoming message format instead.

Now that I have a Message object, I need to set up its fields and contents. The From: address and To: address will each be javax.mail.internet.InternetAddress objects. You can provide either an email address alone or an email address and a real name. For example:

Address bill = new InternetAddress("[email protected]", "Bill Gates");
Address elliotte = new InternetAddress("[email protected]");

The setFrom( ) message allows us to say who’s sending the message by setting the From: header. There’s no protection against forgery here. It’s quite easy for me to masquerade as Bill Gates at a (presumably) fictitious email address:

msg.setFrom(bill);

The setRecipient( ) method is slightly more complex. You not only have to specify the address that the message will be sent to, but how that address is used; that is, as a To: field, a Cc: field, or a Bcc: field. These are indicated by three mnemonic constants of the Message.RecipientType class:

Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC

For example:

msg.setRecipient(Message.RecipientType.TO, elliotte);

The subject is set as a simple string of text. For example:

msg.setSubject("You must comply.");

The body is also set as a single string of text. However, as well as that text you need to provide the MIME type of the text. The most common type is text/plain. For example:

msg.setContent("Resistance is futile. You will be assimilated!", 
 "text/plain");

Finally, the static Transport.send( ) method connects to the mail server specified by the mail.host property and sends the message on its way:

Transport.send(msg);

Example 19.1 puts all these steps together into a standalone program that sends the following message:

Date: Mon, 29 Nov 1999 15:55:42 -0500 (EST)
From: Bill Gates <[email protected]>
To: [email protected]
Subject: You must comply.

Resistance is futile. You will be assimilated!

I’ve shown this message here in standard RFC 822 format used for Internet email. However, that isn’t necessary. The main point is that you need to know the addressee ([email protected]), the sender ([email protected]), and the subject and body of the message.

Example 19-1. Sending a Very Simple Mail Message

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

public class Assimilator {

  public static void main(String[] args) {

    try {
      Properties props = new Properties(  );
      props.put("mail.host", "mail.cloud9.net");
       
      Session mailConnection = Session.getInstance(props, null);
      Message msg = new MimeMessage(mailConnection);

      Address bill = new InternetAddress("[email protected]", 
       "Bill Gates");
      Address elliotte = new InternetAddress("[email protected]");
    
      msg.setContent("Resistance is futile. You will be assimilated!", 
       "text/plain");
      msg.setFrom(bill);
      msg.setRecipient(Message.RecipientType.TO, elliotte);
      msg.setSubject("You must comply.");
      
      Transport.send(msg);
      
    }
    catch (Exception e) {
      e.printStackTrace(  ); 
    }
    
  }
}

Sending Email from an Application

Example 19.1 is a simple application that sends a fixed message to a known address with a specified subject. Once you see how to do this, it’s straightforward to replace the strings that give the message address, subject, and body with data read from the command line, a GUI, a database, or some other source. For instance, Example 19.2 is a very simple GUI for sending email. Figure 19.1 shows the program running. The mail code is all tied up in the actionPerformed( ) method and looks very similar to the main( ) method of Example 19.1. The big difference is that now the host, subject, from: address, to: address, and text of the message are all read from the GUI components at runtime rather than being hardcoded as string literals in the source code. The rest of code is related to setting up the GUI and has little to do with the JavaMail API.

Example 19-2. A Graphical SMTP Client

import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;


public class SMTPClient extends JFrame {

  private JButton     sendButton   = new JButton("Send Message"); 
  private JLabel      fromLabel    = new JLabel("From: "); 
  private JLabel      toLabel      = new JLabel("To: "); 
  private JLabel      hostLabel    = new JLabel("SMTP Server: "); 
  private JLabel      subjectLabel = new JLabel("Subject: "); 
  private JTextField  fromField    = new JTextField(40); 
  private JTextField  toField      = new JTextField(40); 
  private JTextField  hostField    = new JTextField(40); 
  private JTextField  subjectField = new JTextField(40); 
  private JTextArea   message      = new JTextArea(40, 72); 
  private JScrollPane jsp          = new JScrollPane(message);

  public SMTPClient(  ) {
    
    super("SMTP Client");
    Container contentPane = this.getContentPane(  );
    contentPane.setLayout(new BorderLayout(  ));  
    
    JPanel labels = new JPanel(  );
    labels.setLayout(new GridLayout(4, 1));
    labels.add(hostLabel);
    
    JPanel fields = new JPanel(  );
    fields.setLayout(new GridLayout(4, 1));
    String host = System.getProperty("mail.host", "");
    hostField.setText(host);
    fields.add(hostField);
    
    labels.add(toLabel);
    fields.add(toField);

    String from = System.getProperty("mail.from", "");
    fromField.setText(from);
    labels.add(fromLabel);
    fields.add(fromField);

    labels.add(subjectLabel);
    fields.add(subjectField);
    
    Box north = Box.createHorizontalBox(  );
    north.add(labels);
    north.add(fields);
    
    contentPane.add(north, BorderLayout.NORTH);
    
    message.setFont(new Font("Monospaced", Font.PLAIN, 12));
    contentPane.add(jsp, BorderLayout.CENTER);

    JPanel south = new JPanel(  );
    south.setLayout(new FlowLayout(FlowLayout.CENTER));
    south.add(sendButton);
    sendButton.addActionListener(new SendAction(  ));
    contentPane.add(south, BorderLayout.SOUTH);       
    
    this.pack(  ); 
    
  }

  class SendAction implements ActionListener {
   
    public void actionPerformed(ActionEvent evt) {
      
      try {
        Properties props = new Properties(  );
        props.put("mail.host", hostField.getText(  ));
         
        Session mailConnection = Session.getInstance(props, null);
        final Message msg = new MimeMessage(mailConnection);
  
        Address to = new InternetAddress(toField.getText(  ));
        Address from = new InternetAddress(fromField.getText(  ));
      
        msg.setContent(message.getText(  ), "text/plain");
        msg.setFrom(from);
        msg.setRecipient(Message.RecipientType.TO, to);
        msg.setSubject(subjectField.getText(  ));
        
        // This can take a non-trivial amount of time so 
        // spawn a thread to handle it. 
        Runnable r = new Runnable(  ) {
          public void run(  ) {
            try {
              Transport.send(msg);
            }
            catch (Exception e) {
              e.printStackTrace(  ); 
            }
          } 
        };
        Thread t = new Thread(r);
        t.start(  );
        
        message.setText("");
      }
      catch (Exception e) {
        // We should really bring up a more specific error dialog here.
        e.printStackTrace(  ); 
      }
      
    } 
    
  }

  public static void main(String[] args) {

    SMTPClient client = new SMTPClient(  );
    // Next line requires Java 1.3. We want to set up the
    // exit behavior here rather than in the constructor since
    // other programs that use this class may not want to exit 
    // the application when the SMTPClient window closes.
    client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    client.show(  );

  }
}

This is far from an ideal program. The GUI could be more cleanly separated from the mailing code. And it would be better to bring up an error dialog if something went wrong rather than just printing a stack trace of the exception on System.err. However, since none of that would teach us anything about the JavaMail API, I leave all that as an exercise for the interested reader.

A simple GUI mail program

Figure 19-1. A simple GUI mail program

Sending Email from an Applet

In terms of GUIs and the JavaMail API, there’s no difference between sending email from an applet and an application. However, the browser’s security manager can get in your way. Like everything else in this book, the JavaMail API can’t get around the normal restrictions on network connections from applets. An applet that wants to send email can still talk only to the host the applet itself came from.

Fortunately, however, most hosts that run web servers also run SMTP servers.[32] If this is the case, then it’s quite straightforward to make an applet that sends email. The JavaMail API and the Java Activation Framework on which it depends aren’t included with most browsers, but since they’re implemented in pure Java in the javax package, browsers can download the necessary classes from the server. For example, this APPLET element references not only the applet’s own code but also the mail.jar and activation.jar files for the JavaMail API and the Java Activation Framework, respectively:

<APPLET CODE=SMTPApplet ARCHIVE="activation.jar,mail.jar" 
        WIDTH=600 HEIGHT=400>
  <PARAM NAME="to" VALUE="[email protected]">
  <PARAM NAME="subject" VALUE="Hay Orders">
  <PARAM NAME="from" VALUE="noone">  
</APPLET>

Note

In practice, the JavaMail API works only in HotJava and Internet Explorer. Netscape Navigator 4.x and earlier place additional restrictions on what resources an untrusted applet is allowed to load. These restrictions break the JavaMail API in applets.

Example 19.3 is a simple applet that sends email. The address to send email to and the subject are read from PARAM tags. The address to send email from is also read from a PARAM tag, but the user has the option to change it. The text to send is typed into a text area by the user. Finally, the server is determined by looking at the applet’s codebase.

Example 19-3. An Applet That Sends Email

import java.applet.*;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
import java.awt.event.*;
import java.awt.*;


public class SMTPApplet extends Applet {

  private Button    sendButton   = new Button("Send Message");
  private Label     fromLabel    = new Label("From: "); 
  private Label     subjectLabel = new Label("Subject: "); 
  private TextField fromField    = new TextField(40); 
  private TextField subjectField = new TextField(40); 
  private TextArea  message      = new TextArea(30, 60); 
  
  private String toAddress = "";

  public SMTPApplet(  ) {
    
    this.setLayout(new BorderLayout(  ));
    
    Panel north = new Panel(  );
    north.setLayout(new GridLayout(3, 1));
    
    Panel n1 = new Panel(  );
    n1.add(fromLabel);
    n1.add(fromField);
    north.add(n1);
    
    Panel n2 = new Panel(  );
    n2.add(subjectLabel);
    n2.add(subjectField);
    north.add(n2);

    this.add(north, BorderLayout.NORTH);
    
    message.setFont(new Font("Monospaced", Font.PLAIN, 12));
    this.add(message, BorderLayout.CENTER);

    Panel south = new Panel(  );
    south.setLayout(new FlowLayout(FlowLayout.CENTER));
    south.add(sendButton);
    sendButton.addActionListener(new SendAction(  ));
    this.add(south, BorderLayout.SOUTH);    
    
  }
  
  public void init(  ) {
    
    String subject = this.getParameter("subject");
    if (subject == null) subject = "";
    subjectField.setText(subject);
    
    toAddress = this.getParameter("to");
    if (toAddress == null) toAddress = "";
    
    String fromAddress = this.getParameter("from");
    if (fromAddress == null) fromAddress = ""; 
    fromField.setText(fromAddress);      
    
  }

  class SendAction implements ActionListener {
   
    public void actionPerformed(ActionEvent evt) {
      
      try {
        Properties props = new Properties(  );
        props.put("mail.host", getCodeBase().getHost(  ));
         
        Session mailConnection = Session.getInstance(props, null);
        final Message msg = new MimeMessage(mailConnection);
  
        Address to = new InternetAddress(toAddress);
        Address from = new InternetAddress(fromField.getText(  ));
      
        msg.setContent(message.getText(  ), "text/plain");
        msg.setFrom(from);
        msg.setRecipient(Message.RecipientType.TO, to);
        msg.setSubject(subjectField.getText(  ));
        
        // This can take a non-trivial amount of time so 
        // spawn a thread to handle it. 
        Runnable r = new Runnable(  ) {
          public void run(  ) {
            try {
              Transport.send(msg);
            }
            catch (Exception e) {
              e.printStackTrace(  ); 
            }
          } 
        };
        Thread t = new Thread(r);
        t.start(  );
        
        message.setText("");
      }
      catch (Exception e) {
        // We should really bring up a more specific error dialog here.
        e.printStackTrace(  ); 
      }
    } 
  }
}

Figure 19.2 shows this applet running in Internet Explorer 4.0.1 on the Macintosh. I’ve been careful to only use methods and classes available in Java 1.1 so that this applet will run across the most web browsers possible. I also avoided using Swing so that there’d be one less large JAR file to download. As it is, the mail.jar and activation.jar files that this applet requires take up almost 300K, more than I’m comfortable with but manageable on a fast connection.

The SMTP applet

Figure 19-2. The SMTP applet

Proper behavior of this applet depends on several external factors:

  • The browser must support at least Java 1.1 with a security model no stricter than the default.

  • The mail.jar and activation.jar files must be available in the applet’s codebase.

  • The web server that serves the applet must also be an SMTP server willing to relay mail from the client system to the receiver system. Nowadays, most open SMTP relays have been shut down to avoid abuse by spammers, so this can be a sticking point. If it is, you’ll get an exception like this:

    javax.mail.SendFailedException: 550 <[email protected]>... Relaying denied

    However, you should at least be able to send email to addresses in the web server’s domain.



[32] This is perhaps a little more likely to be true of a Unix web server than a Mac or Windows web server, since most Mac and Windows servers don’t ship with SMTP servers by default.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.255.134