19.2 Sending Email

     

Sending messages is the most basic email need of a Java program. 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 popular service that tracks the sales rank of authors' books on Amazon.com and notifies them periodically via email. 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 users of an event when they're not sitting in front of the computer that the program is running on is to send them email.

The JavaMail API provides everything programs need to send email. To send a message, a program follows these eight simple 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. Individually, each of the steps is 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 the server requests one. 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 when using the local SMTP server, 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:

 Address bill = new InternetAddress("god@microsoft.com", "Bill Gates"); Address elliotte = new InternetAddress("elharo@metalab.unc.edu"); 

The setFrom( ) method allows us to say who's sending the message by setting the From: header. There's no protection against forgery. 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, along with 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 <god@microsoft.com> To: elharo@metalab.unc.edu Subject: You must comply. Resistance is futile. You will be assimilated! 

I've shown this message 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 (elharo@metalab.unc.edu), the sender (god@microsoft.com), 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("god@microsoft.com",         "Bill Gates");       Address elliotte = new InternetAddress("elharo@metalab.unc.edu");            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 ex) {       ex.printStackTrace( );      }        } } 

19.2.1 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 ex) {               ex.printStackTrace( );              }           }          };         Thread t = new Thread(r);         t.start( );                  message.setText("");       }       catch (Exception ex) {         // I should really bring up a more specific error dialog here.         ex.printStackTrace( );        }            }         }   public static void main(String[] args) {     SMTPClient client = new SMTPClient( );     // Next line requires Java 1.3 or later. I 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 it all as an exercise for the interested reader.

Figure 19-1. A simple GUI mail program
figs/jnp3_1901.gif

19.2.2 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 and other remotely loaded code. An applet that wants to send email can still talk only to the host the applet itself came from.

Fortunately, however, many hosts that run web servers also run SMTP servers. If this is the case, it's quite straightforward for an applet to send 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="hamp@sideview.mtsterling.ky.us">   <PARAM NAME="subject" VALUE="Hay Orders">   <PARAM NAME="from" VALUE="noone">   </APPLET> 

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 ex) {               ex.printStackTrace( );              }           }          };         Thread t = new Thread(r);         t.start( );                  message.setText("");       }       catch (Exception ex) {         // We should really bring up a more specific error dialog here.         ex.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 this applet runs 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.

Figure 19-2. The SMTP applet
figs/jnp3_1902.gif

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. These days, 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 <hamp@sideview.mtsterling.ky.us>...  Relaying denied 

    However, you should at least be able to send email to addresses in the web server's domain. You may be able to set up one of these addresses to automatically forward the messages to their eventual recipient.



Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net