Recipe 19.9 Program: MailClient


This program is a simplistic GUI-based mail client. It uses the Swing GUI components (see Chapter 14) along with JavaMail. The program loads a Properties file (see Recipe 7.7) to decide which mail server to use for outgoing mail (see Recipe 19.2), as well as the name of a mail server for incoming mail and a Store class (see this chapter's Introduction and Recipe 19.7). 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.

Figure 19-2. MailClient in reading mode
figs/jcb2_1902.gif


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.

Figure 19-3. MailClient in compose mode
figs/jcb2_1903.gif


The code reuses 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( );         // Construct and load the Properties for the mail reader and sender.         Properties mailProps = new FileProperties(propsFileName);         // 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 + "\n" +                     "(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\n(" + 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 Recipe 19.8.

The MailComposeBean used for the Sending tab is a GUI component for composing a mail message. It uses the Mailer class from Recipe 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 add( ), set( ), 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:\n" + 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( ));             // TODO  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:\n" + me.toString( ),                 "Error", JOptionPane.ERROR_MESSAGE);         } catch (Exception e) {             JOptionPane.showMessageDialog(null,                 "Mail Sending Error:\n" + 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", "spam-magnet@darwinsys.com", 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. 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 multiwindow 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 that 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.

See Also

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 through the link 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 (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 they discuss what is now perhaps the biggest problem facing Internet mail users.



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

Similar book on Amazon

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