Program: MailClient

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.

Mail Client in reading mode

Figure 19-2. Mail Client in reading mode

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.

Mail Client in compose mode

Figure 19-3. Mail Client in compose mode

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.

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; 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.

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

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