This program is a simplistic GUI-based mail
client. It uses the Swing GUI components
(see Chapter 13) along with JavaMail. The program
loads a Properties
file (see Section 7.8) to decide what mail server to use for
outgoing mail (see Section 19.3), as well as the name
of a mail server for incoming mail and a Store
class (see this chapter’s Introduction and Section 19.6). The main
class, MailClient
,
is simply a
JComponent
with a JTabbedPane
to let you switch between reading mail and sending mail.
When first started, the program behaves as a mail reader, as shown in Figure 19-2.
You can click on the Sending tab to make it show the Mail Compose window, shown in Figure 19-3. I am typing a message to an ISP about some SPAM I received.
The code is pretty simple; it uses the
MailReaderBean
presented earlier and a similar
MailComposeBean
for sending mail. Example 19-11 is the main program.
Example 19-11. MailClient.java
import com.darwinsys.util.FileProperties; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.util.*; /** Standalone MailClient GUI application. */ public class MailClient extends JComponent implements MailConstants { /** The quit button */ JButton quitButton; /** The read mode */ MailReaderBean mrb; /** The send mode */ MailComposeFrame mcb; /** Construct the MailClient JComponent a default Properties filename */ public MailClient( ) throws Exception { this(PROPS_FILE_NAME); } /** Construct the MailClient JComponent with no Properties filename */ public MailClient(String propsFileName) throws Exception { super( ); // Get the Properties for the mail reader and sender. // Save them in System.properties so other code can find them. FileProperties mailProps = new FileProperties(propsFileName); mailProps.load( ); // Gather some key values String proto = mailProps.getProperty(RECV_PROTO); String user = mailProps.getProperty(RECV_USER); String pass = mailProps.getProperty(RECV_PASS); String host = mailProps.getProperty(RECV_HOST); if (proto==null) throw new IllegalArgumentException(RECV_PROTO + "==null"); // Protocols other than "mbox" need a password. if (!proto.equals("mbox") && (pass == null || pass.equals("ASK"))) { String np; do { // VERY INSECURE -- should use JDialog + JPasswordField! np = JOptionPane.showInputDialog(null, "Please enter password for " + proto + " user " + user + " on " + host + " " + "(warning: password WILL echo)", "Password request", JOptionPane.QUESTION_MESSAGE); } while (np == null || (np != null && np.length( ) == 0)); mailProps.setProperty(RECV_PASS, np); } // Dump them all into System.properties so other code can find. System.getProperties( ).putAll(mailProps); // Construct the GUI // System.out.println("Constructing GUI"); setLayout(new BorderLayout( )); JTabbedPane tbp = new JTabbedPane( ); add(BorderLayout.CENTER, tbp); tbp.addTab("Reading", mrb = new MailReaderBean( )); tbp.addTab("Sending", mcb = new MailComposeFrame( )); add(BorderLayout.SOUTH, quitButton = new JButton("Exit")); // System.out.println("Leaving Constructor"); } /** "main program" method - run the program */ public static void main(String[] av) throws Exception { final JFrame f = new JFrame("MailClient"); // Start by checking that the javax.mail package is installed! try { Class.forName("javax.mail.Session"); } catch (ClassNotFoundException cnfe) { JOptionPane.showMessageDialog(f, "Sorry, the javax.mail package was not found (" + cnfe + ")", "Error", JOptionPane.ERROR_MESSAGE); return; } // create a MailClient object MailClient comp; if (av.length == 0) comp = new MailClient( ); else comp = new MailClient(av[0]); f.getContentPane( ).add(comp); // Set up action handling for GUI comp.quitButton.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent e) { f.setVisible(false); f.dispose( ); System.exit(0); } }); f.addWindowListener(new WindowAdapter( ) { public void windowClosing(WindowEvent e) { f.setVisible(false); f.dispose( ); System.exit(0); } }); // Set bounds. Best at 800,600, but works at 640x480 // f.setLocation(140, 80); // f.setSize (500,400); f.pack( ); f.setVisible(true); } }
The MailReaderBean
used in the Reading
tab is exactly the same as the one shown in Section 19.8.
The MailComposeBean
used for the Sending tab is a
GUI component for composing a mail message. It uses the
Mailer
class from Section 19.3 to
do the actual sending. Example 19-12 shows the
MailComposeBean
program.
Example 19-12. MailComposeBean.java
import com.darwinsys.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.io.*; import javax.activation.*; import javax.mail.*; import javax.mail.internet.*; /** MailComposeBean - Mail gather and send Component Bean. * * Can be used as a Visible bean or as a Non-Visible bean. * If setVisible(true), puts up a mail compose window with a Send button. * If user clicks on it, tries to send the mail to a Mail Server * for delivery on the Internet. * * If not visible, use addXXX(), setXXX(), and doSend( ) methods. * */ public class MailComposeBean extends JPanel { /** The parent frame to be hidden/disposed; may be JFrame, JInternalFrame * or JPanel, as necessary */ private Container parent; private JButton sendButton, cancelButton; private JTextArea msgText; // The message! // The To, Subject, and CC lines are treated a bit specially, // any user-defined headers are just put in the tfs array. private JTextField tfs[], toTF, ccTF, subjectTF; // tfsMax MUST == how many are current, for focus handling to work private int tfsMax = 3; private final int TO = 0, SUBJ = 1, CC = 2, BCC = 3, MAXTF = 8; /** The JavaMail session object */ private Session session = null; /** The JavaMail message object */ private Message mesg = null; private int mywidth; private int myheight; /** Construct a MailComposeBean with no default recipient */ MailComposeBean(Container parent, String title, int height, int width) { this(parent, title, null, height, width); } /** Construct a MailComposeBean with no arguments (needed for Beans) */ MailComposeBean( ) { this(null, "Compose", null, 300, 200); } /** Constructor for MailComposeBean object. * * @param parent Container parent. If JFrame or JInternalFrame, * will setvisible(false) and dispose( ) when * message has been sent. Not done if "null" or JPanel. * @param title Title to display in the titlebar * @param recipient Email address of recipient * @param height Height of mail compose window * @param width Width of mail compose window */ MailComposeBean(Container parent, String title, String recipient, int width, int height) { super( ); this.parent = parent; mywidth = width; myheight = height; // THE GUI Container cp = this; cp.setLayout(new BorderLayout( )); // Top is a JPanel for name, address, etc. // Centre is the TextArea. // Bottom is a panel with Send and Cancel buttons. JPanel tp = new JPanel( ); tp.setLayout(new GridLayout(3,2)); cp.add(BorderLayout.NORTH, tp); tfs = new JTextField[MAXTF]; tp.add(new JLabel("To: ", JLabel.RIGHT)); tp.add(tfs[TO] = toTF = new JTextField(35)); if (recipient != null) toTF.setText(recipient); toTF.requestFocus( ); tp.add(new JLabel("Subject: ", JLabel.RIGHT)); tp.add(tfs[SUBJ] = subjectTF = new JTextField(35)); subjectTF.requestFocus( ); tp.add(new JLabel("Cc: ", JLabel.RIGHT)); tp.add(tfs[CC] = ccTF = new JTextField(35)); // Centre is the TextArea cp.add(BorderLayout.CENTER, msgText = new JTextArea(70, 10)); msgText.setBorder(BorderFactory.createTitledBorder("Message Text")); // Bottom is the apply/cancel button JPanel bp = new JPanel( ); bp.setLayout(new FlowLayout( )); bp.add(sendButton = new JButton("Send")); sendButton.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent e) { try { doSend( ); } catch(Exception err) { System.err.println("Error: " + err); JOptionPane.showMessageDialog(null, "Sending error: " + err.toString( ), "Send failed", JOptionPane.ERROR_MESSAGE); } } }); bp.add(cancelButton = new JButton("Cancel")); cancelButton.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent e) { maybeKillParent( ); } }); cp.add(BorderLayout.SOUTH, bp); } public Dimension getPreferredSize( ) { return new Dimension(mywidth, myheight); } public Dimension getMinimumSize( ) { return getPreferredSize( ); } /** Do the work: send the mail to the SMTP server. * * ASSERT: must have set at least one recipient. */ public void doSend( ) { try { Mailer m = new Mailer( ); FileProperties props = new FileProperties(MailConstants.PROPS_FILE_NAME); String serverHost = props.getProperty(MailConstants.SEND_HOST); if (serverHost == null) { JOptionPane.showMessageDialog(parent, """ + MailConstants.SEND_HOST + "" must be set in properties" "No server!", JOptionPane.ERROR_MESSAGE); return; } m.setServer(serverHost); String tmp = props.getProperty(MailConstants.SEND_DEBUG); m.setVerbose(tmp != null && tmp.equals("true")); String myAddress = props.getProperty("Mail.address"); if (myAddress == null) { JOptionPane.showMessageDialog(parent, ""Mail.address" must be set in properties", "No From: address!", JOptionPane.ERROR_MESSAGE); return; } m.setFrom(myAddress); m.setToList(toTF.getText( )); m.setCcList(ccTF.getText( )); // m.setBccList(bccTF.getText( )); if (subjectTF.getText().length( ) != 0) { m.setSubject(subjectTF.getText( )); } // Now copy the text from the Compose TextArea. m.setBody(msgText.getText( )); // XXX I18N: use setBody(msgText.getText( ), charset) // Finally, send the sucker! m.doSend( ); // Now hide the main window maybeKillParent( ); } catch (MessagingException me) { me.printStackTrace( ); while ((me = (MessagingException)me.getNextException( )) != null) { me.printStackTrace( ); } JOptionPane.showMessageDialog(null, "Mail Sending Error: " + me.toString( ), "Error", JOptionPane.ERROR_MESSAGE); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Mail Sending Error: " + e.toString( ), "Error", JOptionPane.ERROR_MESSAGE); } } private void maybeKillParent( ) { if (parent == null) return; if (parent instanceof Frame) { ((Frame)parent).setVisible(true); ((Frame)parent).dispose( ); } if (parent instanceof JInternalFrame) { ((JInternalFrame)parent).setVisible(true); ((JInternalFrame)parent).dispose( ); } } /** Simple test case driver */ public static void main(String[] av) { final JFrame jf = new JFrame("DarwinSys Compose Mail Tester"); System.getProperties( ).setProperty("Mail.server", "mailhost"); System.getProperties( ).setProperty("Mail.address", "nobody@home"); MailComposeBean sm = new MailComposeBean(jf, "Test Mailer", "[email protected]", 500, 400); sm.setSize(500, 400); jf.getContentPane( ).add(sm); jf.setLocation(100, 100); jf.setVisible(true); jf.addWindowListener(new WindowAdapter( ) { public void windowClosing(WindowEvent e) { jf.setVisible(false); jf.dispose( ); System.exit(0); } }); jf.pack( ); } }
Further, the MailComposeBean
program is a
JavaBean, so it can be used in GUI builders and even have its fields
set within a JSP (see Section 18.9). It has a
main
method, which allows it to be used
standalone (primarily for testing).
To let you compose one or more email messages concurrently, messages
being composed are placed in a JDesktopPane
,
Java’s implementation of
Multiple-Document Interface (MDI). Example 19-13 shows
how to construct a
multi-window email
implementation. Each MailComposeBean
must be
wrapped in a JInternalFrame
, which is what you
need to place components in the JDesktopPane
. This
wrapping is handled inside MailReaderFrame
, one
instance of which is created in the MailClient
constructor. The MailReaderFrame
method
newSend( )
creates an instance of
MailComposeBean
and shows it in the
JDesktopFrame
, returning a reference to the
MailComposeBean
so that the caller can use methods
such as addRecipient()
and send( )
. It also creates a Compose button and places it below the
desktop pane, so you can create a new composition window by clicking
the button.
Example 19-13. MailComposeFrame.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; /** A frame for (possibly) multiple MailComposeBean windows. */ public class MailComposeFrame extends JPanel { JDesktopPane dtPane; JButton newButton; protected int nx, ny; /** To be useful here, a MailComposeBean has to be inside * its own little JInternalFrame. */ public MailComposeBean newSend( ) { // Make the JInternalFrame wrapper JInternalFrame jf = new JInternalFrame( ); // Bake the actual Bean MailComposeBean newBean = new MailComposeBean(this, "Compose", 400, 250); // Arrange them on the diagonal. jf.setLocation(nx+=10, ny+=10); // Make the new Bean be the contents of the JInternalFrame jf.setContentPane(newBean); jf.pack( ); jf.toFront( ); // Add the JInternalFrame to the JDesktopPane dtPane.add(jf); return newBean; } /* Construct a MailComposeFrame, with a Compose button. */ public MailComposeFrame( ) { setLayout(new BorderLayout( )); dtPane = new JDesktopPane( ); add(dtPane, BorderLayout.CENTER); newButton = new JButton("Compose"); newButton.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent e) { newSend( ); } }); add(newButton, BorderLayout.SOUTH); } }
The file TODO.txt
in the email source directory
lists a number of improvements that would have to be added to the
MailClient
program to make it functional enough
for daily use (delete and reply functionality, menus, templates,
aliases, and much more). But it is a start, and provides a structure
to build
on.
Sun maintains a mailing list specifically for the
JavaMail API. Read about the
javamail-interest
list near the bottom of the main
API page, at http://java.sun.com/products/javamail/. This
is also a good place to find other provider classes; Sun has a POP3
provider, and there is a list of third-party products. You can also
download the complete source code for the JavaMail API from
Sun’s community source project; there is a link to this on the
main API page.
There are now several books that discuss Internet mail. David Wood’s Programming Internet Email (O’Reilly) discusses all aspects of Internet email, with an emphasis on Perl but with a chapter and examples on JavaMail. Similarly, Kevin Johnson’s Internet Email Protocols: A Developer’s Guide (Addison-Wesley) covers the protocols and has appendixes on various programming languages, including Java. The Programmer’s Guide to Internet Mail: Smtp, Pop, Imap, and Ldap, by John Rhoton (Digital Press) and Essential E-Mail Standards: RFCs and Protocols Made Practical by Pete Loshin (John Wiley) cover the protocols without much detail on Java implementation. Internet E-Mail: Protocols, Standards, and Implementation by Lawrence E. Hughes (Artech House Telecommunications) covers a great deal of general material, but emphasizes Microsoft technologies and doesn’t say much about JavaMail. Finally, the books Stopping Spam: Stamping Out Unwanted Email and News Postings by Alan Schwartz and Simson Garfinkel (O’Reilly) and Removing the Spam: Email Processing and Filtering (Addison-Wesley) by Geoff Mulligan aren’t about JavaMail, but discuss what is now perhaps the biggest problem facing Internet mail users.
18.189.170.134