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:
Set the mail.host
property to point to the local
mail server.
Start a mail session with the Session.getInstance( )
method.
Create a new Message
object, probably by
instantiating one of its concrete subclasses.
Set the message’s From: address.
Set the message’s To: address.
Set the message’s Subject:.
Set the content of the message.
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( ); } } }
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.
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>
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.
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.
18.225.255.134