Wattle Software - producers of XMLwriter XML editor
 Home | Search | Site Map 
XMLwriter
 Screenshots
 Features
 About Latest Version
 Awards & Reviews
 User Comments
 Customers
Download
 Download XMLwriter
 Download Plug-ins
 Download Help Manual
 MSXML Updates
 Downloading FAQ
Buy
 Buy XMLwriter
 Pricing
 Upgrading
 Sales Support
 Sales FAQ
Support
 Sales Support
 Technical Support
 Submit a Bug Report
 Feedback & Requests
 Technical FAQ
Resources
 XML Books
 XML Links
 XML Training
 XMLwriter User Tools
 The XML Guide
 XML Book Samples
Wattle Software
 About Us
 Contact Details
 News
Professional WAP

Buy this book

Back Contents Next

A WAP-based E-mail Application

Web mail accounts such as the ubiquitous 'Hotmail', or 'Yahoo! Mail' are fast becoming the most popular kind of e-mail – there are almost 170 million subscribers to this type of account.

 

Web-based mail allows users convenient web access to their mail account without any messy machine configuration issues. Our second sample application implements a simple WAP mail system, allowing access to an SMTP/POP3-based e-mail account.

 

This example uses Java servlets and JavaMail to:

 

Compose, send and reply to mail via an SMTP server

View an inbox and read mail using a POP3 service

Determine the number of messages waiting in the inbox

Delete mail

Diagram of WAPMail's Functionality

Below is a diagram illustrating the functionality of the basic web mail system we will create in WAPMail.java. The user can logon to the system, view their POP3 inbox, read mail, reply to, compose, send, and delete e-mail.

 

 

WAPMail.java

WAPMail provides all the basic functionality described above, but the code has been designed for illustration rather than deployment in a production environment. It should be noted that the code is limited in the following ways:

 

It has minimal error checking.

There is no guarantee that a WML deck of more that 1400 bytes will be accepted by the WAP browser. Large emails or a long list of emails in the inbox will cause problems.

It is not implemented for maximum speed and efficiency (for example, use of String concatenation rather than StringBuffers)

It can only be used by a single user – session information is held in a static class variable. We would need to use some session tracking code to make this code multi-user and scaleable.

It has some problems if the browser does not correctly respond to Cache-Control response headers.

 

WAPMail.java is quite a long piece of code. Below, we'll step through it one piece at a time so we might clearly understand what the code is doing.

 

The first piece of code shows the servlet's main decision code. When the servlet receives a HTTP POST or GET request, it executes the doPost() or doGet() respectively. If the user has not logged in, and had the necessary session information created, they are shown the splash screen, which requires them to login to proceed:

 

import java.io.*;

import java.util.*;

import javax.mail.*;

import javax.mail.internet.*;

import javax.servlet.*;

import javax.servlet.http.*;

import com.wrox.util.WML;

 

public class WAPMail extends HttpServlet implements SingleThreadModel

{

   private static String inboxString = "INBOX";

   private static UserSessionData _userSessionData;

 

   public void doGet (HttpServletRequest request,

                      HttpServletResponse response)

      throws ServletException, IOException

      {

         this.doPost(request,response);

      }

 

   public void doPost (HttpServletRequest request,

                       HttpServletResponse response)

      throws ServletException, IOException

      {

         UserSessionData userSessionData = getUserSessionData();

         String action = request.getParameter("action");

 

         //Main decision making "loop"

         try {

            //SHOW SPLASH SCREEN

 

            if ((action == null) ||

               ((!action.equalsIgnoreCase("login")) &&

               (userSessionData == null))) {

               this.splashScreen(request, response);

            }

            //LOGIN

            else if (action.equalsIgnoreCase("login")) {

               this.login(request, response);

            }

            //LOGOUT

            else if (action.equalsIgnoreCase("logout")) {

               this.logout(request, response);

            }

            //SHOW MAIN MENU

            else if (action.equalsIgnoreCase("mainmenu")) {

               this.mainMenu(request, response, this.getUserSessionData());

            }

            //COMPOSE

            else if (action.equalsIgnoreCase("compose")) {

               this.compose(request, response);

            }

            //SEND

            else if (action.equalsIgnoreCase("send")) {

               this.send(request, response, this.getUserSessionData());

            }

            //VIEW IN BOX

            else if (action.equalsIgnoreCase("viewinbox")) {

               this.viewInbox(request, response, this.getUserSessionData());

            }

            //READ

            else if (action.equalsIgnoreCase("read")) {

               this.read(request, response, this.getUserSessionData());

            }

            //DELETE

            else if (action.equalsIgnoreCase("delete")) {

               this.deleteMessage(request, response, this.getUserSessionData());

            }

            //REPLY

            else if (action.equalsIgnoreCase("reply")) {

               this.replyToMessage(request, response,

                                   this.getUserSessionData());

            }

         } catch (MessagingException me) {

            me.printStackTrace();

         }

      }

 

      public void splashScreen(HttpServletRequest request,

                            HttpServletResponse response)

         throws ServletException, IOException, MessagingException

      {

      this.splashScreen(request, response, "");

      }

   }

 

   public void splashScreen(HttpServletRequest request,

                        HttpServletResponse response, String message)

         throws ServletException, IOException, MessagingException {

 

      WML wml = new WML();

      wml.addCard("WAPMailSplash");

      wml.println("<do type=\"accept\" label=\"Login\">" +

 

         "<go href=\"" + request.getRequestURI() + "\" method=\"post\">" +

            "<postfield name=\"action\" value=\"login\"/>" +

            "<postfield name=\"uid\" value=\"$uid\"/>" +

            "<postfield name=\"pwd\" value=\"$pwd\"/>" +

            "</go>" +

            "</do>" +

            "<p align=\"center\">" +

            "WAP E-mail" +

            "</p>");

      if (message != "") {

         wml.println("<p align=\"left\">" +

                    "<i>" + message + "</i>" +

                    "</p>");

      }

         wml.println("<p>" +

             "Username:" +

             "<input name=\"uid\" title=\"user name\"/><br/>" +

             "Password:" +

             "<input name=\"pwd\" type=\"password\" title=\"password\"/><br/>" +

             "</p>");

      wml.endCard();

      wml.outputWML(response, true);

   }

 

The user is required to log in by specifying their POP3 username (e-mail address) and password. Here is a screen shot sequence of the login process using the Phone.com browser simulator:

 

   

 

If there are any problems in the login process, for instance an incorrect password, the user is presented with this error message, and an opportunity to re-try the login:

 

 

This login process creates an SMTP and a POP3 session, storing them in an instance of UserSessionData, which models this information throughout the rest of the WAPMail code. We will see the code for this class at the end of the WAPMail listing.

 

It is the use of a static variable to store the UserSessionData instance that limits the use of the system to a single user. To allow multiple access to the system, WAPMail could be re-modeled to track the user's HttpSession, making use of this to provide an indexed list of these UserSessionData objects. Here this is left as an exercise for the reader.

 

The next piece of code shows the login process:

 

   public void login(HttpServletRequest request, HttpServletResponse response)

         throws ServletException, IOException, MessagingException {

 

      try {

         String pop3host = "pop.yourISP.net";

         String smtphost = "smtp.yourISP.net";

         String username = request.getParameter("uid");

         String password = request.getParameter("pwd");

 

         //Get SMTP Session

         Properties props = System.getProperties();

         props.put("mail.smtp.host", smtphost);

         //Get SMTP session

         Session smtpSession = Session.getInstance(props, null);

                           smtpSession.setDebug(false);

        //Get POP3 Session

        Session pop3Session = Session.getInstance(System.getProperties(), null);

        pop3Session.setDebug(false);

        //Get POP3 Store

        Store pop3Store = pop3Session.getStore("pop3");

        pop3Store.connect(pop3host, username, password);

 

         //Create a new UserSessionData object

         UserSessionData usd = new UserSessionData(smtpSession, pop3Session,

                                                 pop3Store, username);

         this.setUserSessionData(usd);

 

         //if all okay show main menu

         this.mainMenu(request, response, usd);

 

      } catch (Exception e) {

         e.printStackTrace();

         this.splashScreen(request, response, "Error logging in!");

      }

   }

 

   public void logout(HttpServletRequest request, HttpServletResponse response)

         throws ServletException, IOException, MessagingException {

 

      try {

         this.getUserSessionData().destroy();

         this._userSessionData = null;

 

         WML wml = new WML();

         wml.addCard("WAPMailLogout");

         wml.println("<p align=\"left\">" +

                           "Thank you for using WAP Mail<br/>" +

                           "<anchor>Restart E-mail" +

                           "<go href=\"" + request.getRequestURI() + "\"/>" +

                           "</anchor>" +

                           "</p>");

         wml.endCard();

         wml.outputWML(response, true);

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

 

   public void mainMenu(HttpServletRequest request,

 

                HttpServletResponse response, UserSessionData userSessionData)

      throws ServletException, IOException, MessagingException {

 

      try {

         WML wml = new WML();

         wml.addCard("WAPMailMainMenu", "Main Menu");

         wml.println("<p align=\"left\">" +

              "<anchor>1. Read Mail " + this.getInboxCount(userSessionData) +

              "<go href=\"" + request.getRequestURI() +

              "?action=viewinbox\"/>" +

              "</anchor><br/>" +

              "<anchor>2. Compose" +

              "<go href=\"" + request.getRequestURI() + "?action=compose\"/>" +

              "</anchor><br/>" +

              "<anchor>3. Logout" +

              "<go href=\"" + request.getRequestURI() + "?action=logout\"/>" +

              "</anchor><br/>" +

              "</p>");

         wml.endCard();

         wml.outputWML(response, true);

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

 

This code takes the user's username and password as supplied, and attempts to connect to his POP3 inbox, to enable the user to read his mail. It also creates an SMTP session in order to send mail. We can see the logout method below, and the method that constructs the main menu for the WAP mail system.

 

This produces the mainmenu of WAPMail:

 

 

The following code  shows the compose() method, which constructs a WML page that enables the user of our mail system to enter the details of an email: who it is to, what the subject is, and the text of the e-mail:

 

   public void compose(HttpServletRequest request, HttpServletResponse response)

         throws ServletException, IOException, MessagingException {

      this.compose(request, response, "", "");

   }

 

   public void compose(HttpServletRequest request,

                  HttpServletResponse response, String to, String subject)

         throws ServletException, IOException, MessagingException {

 

      WML wml = new WML();

      wml.addCard("WAPMailCompose");

      wml.println("<p>" +

        "<fieldset title=\"Compose\">" +

 

        "To:<input name=\"to\" maxlength=\"100\" emptyok=\"false\"" +

        " value=\"" + to + "\"/><br/>" +

        "cc:<input name=\"cc\" maxlength=\"100\" emptyok=\"true\"/><br/>" +

        "Subject:<input name=\"subject\" value=\"" + subject +

        "\" emptyok=\"true\"/><br/>" +

        "Text:<input name=\"text\" maxlength=\"500\" emptyok=\"true\"/><br/>" +

        "</fieldset>" +

        "<anchor> Send " +

        "<go href=\"" + request.getRequestURI() + "\" method=\"post\">" +

        "<postfield name=\"action\" value=\"send\"/>" +

        "<postfield name=\"to\" value=\"$to\"/>" +

        "<postfield name=\"cc\" value=\"$cc\"/>" +

        "<postfield name=\"subject\" value=\"$subject\"/>" +

        "<postfield name=\"text\" value=\"$text\"/>" +

        "</go>" +

        "</anchor>" +

        "</p>");

      wml.endCard();

      wml.outputWML(response, true);

}

 

To compose an e-mail, the user must enter:

 

A recipient

A circulation list (optional – leave blank if not required)

A subject for the message

Some text for the body of the message

 

   

 

The example of constructing a message above relies on the fact that the String 'to' is a single Internet address in the form of john_doe@wapbook.org:

 

message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

 

If you wish to give your users the ability to type in a comma delimited list of e-mail addresses then you can use the parse() method of the InternetAddress class to return an array of InternetAddress objects and use the setRecipients()of the message object:

 

message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));

 

If an invalid string is encountered when trying to construct an InternetAddress object, an AddressException is thrown.

 

Now, on with the code: The next section shows the method that sends e-mail using the same JavaMail process we encountered in our first code example. We can also see listed below the viewInbox() method that shows the e-mails waiting in our inbox:

 

   public void send(HttpServletRequest request,

                  HttpServletResponse response, UserSessionData userSessionData)

         throws ServletException, IOException, MessagingException {

 

      try {

         String from = userSessionData.getEmailAddress();

         String to = request.getParameter("to");

         String cc = request.getParameter("cc");

         String subject = request.getParameter("subject");

         String text = request.getParameter("text");

 

         //Define message

         MimeMessage message = new

         MimeMessage(userSessionData.getSmtpSession());

         message.setFrom(new InternetAddress(from));

         message.addRecipient(Message.RecipientType.TO, 

                            new InternetAddress(to));

         try {

            message.addRecipient(Message.RecipientType.CC, 

                            new InternetAddress(cc));

         } catch (AddressException ae) {

            //Bad cc address

         }

         message.setSubject(subject);

         message.setText(text);

 

         //send message

         Transport.send(message);

 

         this.mainMenu(request, response, userSessionData);

      }catch (Exception e) {

         e.printStackTrace();

      }

 

   }

 

   public void viewInbox(HttpServletRequest request,

                  HttpServletResponse response, UserSessionData userSessionData)

         throws ServletException, IOException, MessagingException {

 

      try {

         //Get Folder

         Folder folder = userSessionData.getPop3Store().getFolder(inboxString);

         folder.open(Folder.READ_ONLY);

 

         //Get Directory

         Message message[] = folder.getMessages();

         int n = message.length;


 

         WML wml = new WML();

         wml.addCard("WAPMailViewInbox");

         wml.println("<p align=\"left\">");

         wml.println("Inbox:<br/>");

         for (int i=0; i < n; i++) {

            String emailAddress =

                ((InternetAddress)message[i].getFrom()[0]).getAddress();

            wml.println((i + 1) + ": " + emailAddress + "<br/>" +

               message[i].getSubject() + "<br/>" +

               "<anchor>Read" +

               "<go href=\"" + request.getRequestURI() + "\" method=\"post\">" +

               "<postfield name=\"action\" value=\"read\"/>" +

               "<postfield name=\"index\" value=\"" + (i + 1) + "\"/>" +

               "</go>" +

               "</anchor><br/>" +

               "<anchor>Delete" +

               "<go href=\"" + request.getRequestURI() + "\" method=\"post\">" +

               "<postfield name=\"action\" value=\"delete\"/>" +

               "<postfield name=\"index\" value=\"" + (i + 1) + "\"/>" +

               "</go>" +

               "</anchor><br/>" +

               "------------<br/>");

         }

         wml.println("</p>" +

              "<p>" +

              "<anchor>Main Menu" +

              "<go href=\"" + request.getRequestURI() + "?action=mainmenu\"/>" +

              "</anchor>" +

              "</p>");

         wml.endCard();

         wml.outputWML(response, true);

 

         //Close connection

         folder.close(false);

 

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

 

We can view the inbox as follows:

 

 

The following code shows the method that reads a particular e-mail:

 

   public void read(HttpServletRequest request,

                  HttpServletResponse response, UserSessionData userSessionData)

         throws ServletException, IOException, MessagingException {


 

      try {

         //Get Folder

         Folder folder = userSessionData.getPop3Store().getFolder(inboxString);

         folder.open(Folder.READ_ONLY);

 

         //Get Message

         String indexString = request.getParameter("index");

 

         int messageIndex = Integer.parseInt(indexString);

         Message message = folder.getMessage(messageIndex);

         Object messageContent = message.getContent();

 

         WML wml = new WML();

         wml.addCard("WAPMailReadMail");

         String emailAddress =

                ((InternetAddress)message.getFrom()[0]).getAddress();

         wml.println("<p align=\"left\">" +

                  "From: " + emailAddress + "<br/>" +

                  message.getSubject() + "<br/>" +

                  "------------<br/>");

         if (message.isMimeType("text/plain") &&

                          messageContent instanceof String) {

            wml.println((String)messageContent);

         } else {

            wml.println("Error! WAP Mail can only read plaintext e-mails");

         }

         wml.println("------------<br/>" +

            "<anchor>Reply" +

               "<go href=\"" + request.getRequestURI() + "\" method=\"post\">" +

                  "<postfield name=\"action\" value=\"reply\"/>" +

                  "<postfield name=\"to\" value=\"" + emailAddress + "\"/>" +

                  "<postfield name=\"subject\" value=\"Re: " +

                  message.getSubject() + "\"/>" +

               "</go>" +

            "</anchor><br/>" +

            "<anchor>Delete" +

               "<go href=\"" + request.getRequestURI() + "\" method=\"post\">" +

                  "<postfield name=\"action\" value=\"delete\"/>" +

                  "<postfield name=\"index\" value=\"" + messageIndex + "\"/>" +

               "</go>" +

            "</anchor><br/>" +

            "------------<br/>" +

            "</p>" +

            "<p>" +

            "<anchor>Main Menu" +

            "<go href=\"" + request.getRequestURI() + "?action=mainmenu\"/>" +

            "</anchor><br/>" +

            "<anchor>Back to Inbox" +

            "<go href=\"" + request.getRequestURI() + "?action=viewinbox\"/>" +

            "</anchor>" +

            "</p>");

         wml.endCard();

         wml.outputWML(response, true);

 

         //Close connection

         folder.close(false);

 

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

 

 

It is then possible to read an e-mail:

 

 

The final section of the WAPMail code shows the method that deletes emails from our inbox, and also some utility methods including a method that facilitates replying to a mail, and a method that determines the number of emails waiting in the inbox:

 

   public void deleteMessage (HttpServletRequest request,

                  HttpServletResponse response, UserSessionData userSessionData)

         throws ServletException, IOException, MessagingException {

      try {

         //Get Folder

         Folder folder = userSessionData.getPop3Store().getFolder(inboxString);

         folder.open(Folder.READ_WRITE);

 

         //Get Directory

         int messageIndex = Integer.parseInt(request.getParameter("index"));

         Message message = folder.getMessage(messageIndex);

 

         message.setFlag(Flags.Flag.DELETED, true);

 

         //close connection

         folder.close(true);

 

         this.mainMenu(request, response, userSessionData);

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

 

   public void replyToMessage(HttpServletRequest request,

                  HttpServletResponse response, UserSessionData userSessionData)

         throws ServletException, IOException, MessagingException {

      //Not using the reply mechanism of the Message object

      String to = request.getParameter("to");

      String subject = request.getParameter("subject");

      this.compose(request, response, to, subject);

   }

 

   public String getInboxCount (UserSessionData userSessionData)

   throws Exception {

      //Get Folder

      Folder folder = userSessionData.getPop3Store().getFolder(inboxString);

      folder.open(Folder.READ_ONLY);

 

      //Get Count

      int totalCount = folder.getMessageCount();

 

      //Close Connection

      folder.close(false);

 

      return "(total: " + totalCount + ")";

   }

 

   private static UserSessionData getUserSessionData() {

      return _userSessionData;

   }

 

   private static void setUserSessionData(UserSessionData usd) {

      _userSessionData = usd;

   }

 

   public String getServletInfo() {

      return "A simple WAP e-mail application servlet";

   }

}

 

This class is used by WAPMail as a storage mechanism for the SMTP and POP3 sessions that are created by the login process.

 

By storing the user's session information in a separate class (UserSessionData) we remove some of the session logic from the rest of the servlet, which should make it easier to extend the functionality of WAPMail as the need arises:

 

// Storage class for user's session information

class UserSessionData {

   private Session _smtpSession;

   private Session _pop3Session;

   private Store _pop3store;

   private String _userEmailAddress;

 

   public UserSessionData(Session newSmtpSession,

                  Session newPop3Session,

                  Store newPop3Store,

                  String newUserEmailAddress) {

      setSmtpSession(newSmtpSession);

      setPop3Session(newPop3Session);

      setPop3Store(newPop3Store);

      setEmailAddress(newUserEmailAddress);

   }

 

   public void destroy() {

      try {

         if (getPop3Store() != null) {

            getPop3Store().close();

         }

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

 

   public void setEmailAddress(String newUserEmailAddress) {

      _userEmailAddress = newUserEmailAddress;

   }

 

   public String getEmailAddress() {

      return _userEmailAddress;

   }

 

   public Session getSmtpSession() {

      return _smtpSession;

   }

 

   public void setSmtpSession(Session newSmtpSession) {

      _smtpSession = newSmtpSession;

   }

 

   public Session getPop3Session() {

      return _pop3Session;

   }

 

   public void setPop3Session(Session newPop3Session) {

      _pop3Session = newPop3Session;

   }

 

   public Store getPop3Store() {

      return _pop3store;

   }

 

   public void setPop3Store(Store newPop3Store) {

      _pop3store = newPop3Store;

   }

}

 

This specifies the logout screen:

 

Enhancements to WAPMail

The implementation of WAPMail is designed to illustrate basic e-mail functionality and is not a very elegant example of Java servlet programming. Many things could be done to improve the design of WAPMail, for example:

 

 

 

 

 

 

 

 

Use reflection to eliminate the huge if statement in the doPost() method.

Use a separate JavaBean to model the mail system moving the logic out of the Java servlet, with which the Servlet would then interface.

Use HttpSession tracking to allow multi-user access to WAPMail.

Allow the user to specify the POP3 and SMTP servers; perhaps allow for multiple accounts to be read.

If the e-mail contains illegal WML characters, then the e-mail text needs to be parsed to ensure that we encounter no problems. For example, the following use of '<' and '>' in the e-mail address field seen in some e-mails is an illegal character sequence in WML, and causes the generated WML response to be rejected by the browser: for example, John Doe <John_Doe@wapbook.org>

Sending and Receiving E-Mail using ASP

So far in this chapter, we've concentrated on using Java for incorporating e-mail into WAP applications. If you're in the Microsoft camp, however, you will most likely be using ASP to generate your WAP (and web) content. In this section we'll show how to use ASP with VBScript to generate WML pages that access email functionality.

 

Due to space constraints we're going to keep the examples simple – if you've got this far in the book then you must already be familiar with WML, and if you're interested enough to read this section then presumably that means you're already familiar with ASP itself too! So, we're not going to stop to explain anything about ASP itself, and we're not going to waste time writing a sophisticated set of WML pages – instead we'll just demonstrate a simple page that sends an email based on a couple of options, as well as another page that lets you check for incoming email messages, all from your mobile phone.

 

In order to keep things simple, we'll also use CDONTS rather than CDO The examples here were tested with CDONTS running on Windows 2000 Professional, with IIS 5 installed. CDONTS will be default use the SMTP service that comes with IIS as its mail service. However, if you prefer, you can run CDONTS using Exchange Server.

Sending an E-Mail

Sending an email with CDONTS could hardly be easier. For example, here's the VBScript code that sends an email addressed to my own local account, simon@biggybiggy.lt.local.

 

   dim objSendMail

   set objSendMail = Server.CreateObject("CDONTS.NewMail")

   objSendMail.Send "editors@wrox.com", "simon@biggybiggy.lt.local", _

      "Meeting!", _

      "Meeting to go over final adjustments to Pro WAP is today at 2pm."

   set objSendMail = nothing

 

In other words all we need to do is create an object of type CDONTS.NewMail and call its Send method. The four parameters passed are (in order):

 

The address of the sender (Note that no validation is performed on this: You can put in any address you want – it doesn't have to be your own address).

 

The address the mail should be delivered to

The title of the message

The text of the message body

 

You can also optionally add a fifth parameter, the message importance, which can take any of the values 0 (low), 1(normal, the default value) and 2(high).