October 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

WebMail in Java: Reading E-mail

  • December 17, 1999
  • By Benoît Marchal
  • Send Email »
  • More Articles »

In this article and the previous one, I present a Web-based e-mail client. It is similar to Hotmail, allowing you to check e-mail over the Internet. The client consists of two servlets (one for sending e-mail in the previous article and one for reading e-mail in this one) that use JavaMail to communicate with the e-mail system.

For simplicity, presentation was kept minimal, there is little error-checking, and popular features, like an address-book, are missing. However, the servlets handle e-mail attachments and demonstrate how to use sessions on a Web server.

Handling requests

For simplicity, the servlet processes GET and POST requests indistinctly, see Listing 1. As an e-mail client, the servlet must log to the e-mail server, list pending e-mails, delete e-mails, and read e-mails. The servlet decides what to do based on the action parameter it receives from the browser.

Listing 1.

doGetPost()
method.


package com.psol.webpop3;
 
import java.io.*;
import java.net.*;
import java.text.*;
import javax.mail.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.mail.internet.*;
 
public class WebPOP3
   extends HttpServlet
{
   protected void doGet(HttpServletRequest request,
                        HttpServletResponse response)
      throws IOException
   {
      doGetPost(request,response);
   }
 
   protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
      throws IOException
   {
      doGetPost(request,response);
   }
 
   protected void doGetPost(HttpServletRequest request,
                            HttpServletResponse response)
      throws IOException
   {
      try
      {
         String action = request.getParameter("action");
         if(action == null)
            doLogin(request,response);
         else if(action.equals("logout"))
            doLogout(request,response);
         else if(action.equals("list"))
            doList(request,response);
         else if(action.equals("retrieve"))
            doRetrieve(request,response);
         else if(action.equals("delete"))
            doRemove(request,response);
      }
      catch(Exception e)
      {
         PrintWriter writer = response.getWriter();
         writer.println("<B>Oops!</B><PRE>");
         e.printStackTrace(writer);
         writer.println("</PRE>");
      }
   }
 

Login

The first screen is a login screen — a form that prompts for username and password.

Listing 2.

doLogin()
method.


   protected static final String loginForm =
      "<HTML><HEAD><TITLE>WebPOP3</TITLE></HEAD>" +
      "<FORM ACTION=\"{0}\" METHOD=\"POST\">" +
      "Host: <INPUT NAME=\"host\"><BR>" +
      "User: <INPUT NAME=\"user\"><BR>" +
      "Password: <INPUT TYPE=\"PASSWORD\"" +
      " NAME=\"password\"><BR>" +
      "<INPUT TYPE=\"HIDDEN\" NAME=\"action\"" +
      " VALUE=\"list\">" +
      "<INPUT TYPE=\"SUBMIT\" NAME=\"Login\">" +
      "</FORM></BODY></HTML>";
 
   protected void doLogin(HttpServletRequest request,
                          HttpServletResponse response)
      throws IOException
   {
      printForm(loginForm,request,response);
   }
 
   protected void printForm(String form,
                            HttpServletRequest request,
                            HttpServletResponse response)
      throws IOException
   {
      PrintWriter writer = response.getWriter();
      form = MessageFormat.format(form,
         new Object[] { request.getServletPath() });
      writer.print(form);
      writer.flush();
   }
 

Viewing the inbox

After login, the servlet iterates through all pending messages and prints their subject as a hyperlink, see Listing 3. As we will see in a moment, the servlet relies on sessions to maintain the connection to the e-mail server.

Listing 3: doList() method


 
   protected static final String listMsgHeader =
      "<HTML><HEAD><TITLE>WebPOP3</TITLE></HEAD>" +
      "<BODY><P><A HREF=\"{0}?action=list\">[ Inbox ]" +
      "</A> <A HREF=\"{0}?action=logout\">[ Logout ]" +
      "</A>";
   protected static final String listMsgFooter =
      "</BODY></HTML>";
   protected static final String linkList =
      "<P><FORM ACTION=\"{0}\" METHOD=\"POST\">" +
      "<A HREF=\"{0}?action=retrieve&msg={1}\">" +
      "Message: {2}</A> " +
      "<INPUT TYPE=\"HIDDEN\" NAME=\"action\"" +
      " VALUE=\"delete\"><INPUT TYPE=\"HIDDEN\"" +
      " NAME=\"msg\" VALUE=\"{1}\">" +
      "<INPUT TYPE=\"SUBMIT\" VALUE=\"Delete\"></FORM>";
   protected static final String attachmentLink =
      "<A HREF=\"{0}?action=retrieve&msg={1}\">{2}</A>";
 
   protected void doList(HttpServletRequest request,
                         HttpServletResponse response)
      throws ServletException, IOException,
             MessagingException
   {
      printForm(listMsgHeader,request,response);
      Folder inbox = getInbox(request,response);
      if(inbox == null)
         return;
      int count = inbox.getMessageCount();
      for(int i = 1;i <= count;i++)
      {
         Message msg = inbox.getMessage(i);
         if(!msg.isSet(Flags.Flag.DELETED))
            printHyperlink(linkList,request,response,
                           String.valueOf(i),
                           msg.getSubject());
      }
      printForm(listMsgFooter,request,response);
   }
 
   protected void printHyperlink(String form,
                                 HttpServletRequest request,
                                 HttpServletResponse response,
                                 String parameters,
                                 String text)
      throws IOException
   {
      PrintWriter writer = response.getWriter();
      form = MessageFormat.format(form,
         new Object[] { request.getServletPath(),
                        parameters,
                        text });
      writer.print(form);
      writer.flush();
   }
 

Deleting a message

doRemove()
is called when the user presses the delete button. It flags the message as being deleted. Note that, unfortunately, it is not possible to call
Folder.expunge()
when using the POP3 provider.

Listing 4.

doRemove()
method.


 
   protected void doRemove(HttpServletRequest request,
                           HttpServletResponse response)
      throws ServletException, IOException,
             MessagingException
   {
      Message msg = getMsg(request,response);
      if(msg == null)
         return;
      msg.setFlag(Flags.Flag.DELETED,true);
      doList(request,response);
   }
 
   protected Message getMsg(HttpServletRequest request,
                            HttpServletResponse response)
      throws IOException, ServletException,
             MessagingException
   {
      String msgst = request.getParameter("msg");
      if(msgst != null)
      {
         int msgnr = Integer.parseInt(msgst);
         Folder inbox = getInbox(request,response);
         if(inbox == null)
            return null;
         else
            return inbox.getMessage(msgnr);
      }
      else
         return null;
   }

Displaying a message

When the user clicks on a message in the list, it ends calling

doRetrieve()
, which displays the content of the message. If the message has attachments,
doRetrieve()
prints them as hyperlinks, see Listing 5. Clicking these attachments also calls
doRetrieve()
but passes the additional part parameter. In answer for these requests, the servlet sends the raw attachment data to the browser. The servlet assumes that the browser knows how to display or save the attachment.

Listing 5.

doRetrieve()
method.


 
   protected void doRetrieve(HttpServletRequest request,
                             HttpServletResponse response)
      throws ServletException, IOException,
             MessagingException
   {
      Message msg = getMsg(request,response);
      if(msg == null)
         return;
      if(null == request.getParameter("part"))
         writeMessage(msg,request,response);
      else
         writePart(msg,request,response);
   }
 
   protected void writeEnveloppe(Message msg,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
      throws IOException, MessagingException
   {
      printForm(listMsgHeader,request,response);
      PrintWriter writer = response.getWriter();
      writer.println("<P><B>From:</B> ");
      writeAddresses(msg.getFrom(),writer);
      writer.println("<B>To:</B> ");
      writeAddresses(
         msg.getRecipients(Message.RecipientType.TO),
         writer);
      writer.println("<B>Cc:</B> ");
      writeAddresses(
         msg.getRecipients(Message.RecipientType.CC),
         writer);
      writer.println("<B>Subject:</B> ");
      writer.println(msg.getSubject() + "</P>");
      writer.flush();
   }
 
   protected void writeAddresses(Address[] addresses,
                                 PrintWriter writer)
   {
      if(addresses != null)
         for(int i = 0;i < addresses.length;i++)
            writer.println(addresses[i] + "<BR>");
      else
         writer.println("<BR>");
   }
 
   protected void writeMessage(Message msg,
                               HttpServletRequest request,
                               HttpServletResponse response)
      throws ServletException, IOException,
             MessagingException
   {
      PrintWriter writer = response.getWriter();
      writeEnveloppe(msg,request,response);
      if(msg.isMimeType("multipart/*"))
      {
         Multipart multipart = (Multipart)msg.getContent();
         for(int i = 0;i < multipart.getCount();i++)
         {
            Part p = multipart.getBodyPart(i);
            if(p.isMimeType("text/plain"))
            {
               writer.print("<PRE>");
               writer.print(p.getContent());
               writer.print("</PRE>");
            }
            else
            {
               String filename = p.getFileName();
               printHyperlink(attachmentLink,
                              request,response,
                              msg.getMessageNumber() +
                              "&part=" + i,
                              "attachment");
            }
         }
      }
      else if(msg.isMimeType("text/plain"))
      {
         writer.print("<PRE>");
         writer.print(msg.getContent());
         writer.print("</PRE>");
      }
      else
         printHyperlink(attachmentLink,
                        request,response,
                        msg.getMessageNumber() +
                        "&part=-1",
                        "open content");
      writer.flush();
   }
 
   protected void writePart(Message msg,
                            HttpServletRequest request,
                            HttpServletResponse response)
      throws IOException, MessagingException
   {
      int partnr =
         Integer.parseInt(request.getParameter("part"));
      Part p;
      if(partnr < 0)
         p = msg;
      else
      {
         Multipart multipart =
            (Multipart)msg.getContent();
         p = multipart.getBodyPart(partnr);
      }
      response.setContentType(p.getContentType());
      if(p.getFileName() != null)
         response.setHeader("Content-Disposition",
            "attachment; filename=\"" +
            p.getFileName() + "\"");
      OutputStream out = response.getOutputStream();
      InputStream in = p.getInputStream();
      int c = in.read();
      while(c != -1)
      {
         out.write(c);
         c = in.read();
      }
   }
 

Sessions

HTTP is a stateless protocol. In plain English, it means that each request is independent from the others. Specifically, the browser does not notify the server when the user enters or leaves the site. Yet, we need to decide when to log into the e-mail server and when to logout. Obviously, the login must be associated with a single user: we don't want one user to read another users' e-mail.

Servlets support the notion of a session, which is managed on top of HTTP, typically through cookies. A session identifies calls coming from a given user. Inactive sessions time-out, which is a signal that the user has left the site. We can use sessions to manage the connection to the e-mail server.

Sessions are represented by HttpSession objects. The

getInbox()
method either obtains the current session or creates a new one, see Listing 6. When it creates a new session,
getInbox()
attaches an InboxManager object to it. This class, which will be introduced in a moment, does the actual login to the mail server.

Listing 6.

getInbox()
method.


 
   protected Folder getInbox(HttpServletRequest request,
                             HttpServletResponse response)
      throws IOException, ServletException,
             MessagingException
   {
      HttpSession session = request.getSession(true);
      InboxManager inboxManager;
      if(session.isNew())
      {
         String host = request.getParameter("host"),
                user = request.getParameter("user"),
                password = request.getParameter("password");
         if(host == null || user == null ||
            password == null)
         {
            doLogin(request,response);
            return null;
         }
         URLName url = new URLName("pop3",
                                   host,
                                   110,
                                   "",
                                   user,
                                   password);
         inboxManager = new InboxManager(url);
         session.putValue("inboxmanager",inboxManager);
      }
      else
         inboxManager = (InboxManager)session.
            getValue("inboxmanager");
      return inboxManager.getInbox();
   }
 

When the user logs out, the servlet invalidates the session, in Listing 7. Even if the user forgets to logout (or loses its connection to the Internet), the session will eventually time-out.

Listing 7:

doLogout()
.


 
   protected void doLogout(HttpServletRequest request,
                           HttpServletResponse response)
      throws ServletException, IOException,
             MessagingException
   {
      HttpSession session = request.getSession(false);
      if(null != session)
         session.invalidate();
      doLogin(request,response);
   }
}
 

The InboxManager connects to the e-mail server and acquires the inbox. It implements the HttpSesssionBindingListener interface to be notified when the session is invalidated (either because of user logout or because of a time-out). When that happens, it logouts from the e-mail server.

A word of caution. You probably have to enable session time-outs manually. With Jetty, it requires setting the DefaultSessionMaxIdleTime property. Consult the documentation of your Web server.

Listing 8: InboxManager class.


 
class InboxManager
   implements HttpSessionBindingListener
{
   static private Session session =
      Session.getDefaultInstance(
         System.getProperties(),null);
   private Store store;
   private Folder inbox;
 
   public InboxManager(URLName url)
      throws NoSuchProviderException,
             MessagingException
   {
      store = session.getStore(url);
      store.connect();
      inbox = store.getFolder("INBOX");
      inbox.open(Folder.READ_WRITE);
   }
 
   public Folder getInbox()
   {
      return inbox;
   }
 
   public void valueBound(HttpSessionBindingEvent event)
 
      {}
 
   public void valueUnbound(HttpSessionBindingEvent event)
   {
      try
      {
         inbox.close(true);
         store.close();
      }
      catch(MessagingException e)
         {}
   }
}
 

Conclusion

In these two articles, you learned how to create a simple e-mail client. In particular, they demonstrated e-mail attachments, how to upload files to a Web server, and how to use HTTP sessions.

About the author

Benoît Marchal is a software engineer and consultant based in Namur, Belgium, who has been working extensively in Java and XML. He runs his own software company, Pineapplesoft. He also likes teaching and writing; his book "XML by Example" was recently published by Que.








Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel