Sunday, September 4, 2011

A Form Processing Servlet

2.2 A Form Processing Servlet

This section shows how to
  • process form data
  • manage persistent data
  • use init parameters
The next Servlet that we are going to write provides a user interface to a mailing list through HTML forms. A user should be able to enter an email address in a text field and press a button to subscribe to the list or another button to unsubscribe.
The Servlet consists of two major parts: Data managment and client interaction.

Data management

The data management is rather straight-forward for an experienced Java programmer. We use a java.lang.Vector object which contains the email addresses as Strings. Since a Servlet can have data which persists between requests we load the address list only once, when the Servlet is initialized, and save it every time it has been changed by a request. An alternative approach would be keeping the list in memory while the Servlet is active and writing it to disk in the destroy method. This would avoid the overhead of saving the address list after every change but is less fail-safe. If for some reason the address file can't be written to disk or the server crashes and cannot destroy the Servlet, all changes to the list will be lost even though the users who submitted the requests to change the list received positive responses.


Data management

The data management is rather straight-forward for an experienced Java programmer. We use a java.lang.Vector object which contains the email addresses as Strings. Since a Servlet can have data which persists between requests we load the address list only once, when the Servlet is initialized, and save it every time it has been changed by a request. An alternative approach would be keeping the list in memory while the Servlet is active and writing it to disk in the destroy method. This would avoid the overhead of saving the address list after every change but is less fail-safe. If for some reason the address file can't be written to disk or the server crashes and cannot destroy the Servlet, all changes to the list will be lost even though the users who submitted the requests to change the list received positive responses.
The following parts of the Servlet are related to data management:

  8:    private Vector addresses;
  9:    private String filename;
 10:
 11:    public void init(ServletConfig config) throws ServletException
 12:    {
 13:      super.init(config);
 14:      filename = config.getInitParameter("addressfile");
 15:      if(filename == null)
 16:        throw new UnavailableException(this,
 17:                                       "The \"addressfile\" property "+
 18:                                       "must be set to a file name");
 19:      try
 20:      {
 21:        ObjectInputStream in =
 22:          new ObjectInputStream(new FileInputStream(filename));
 23:        addresses = (Vector)in.readObject();
 24:        in.close();
 25:      }
 26:      catch(FileNotFoundException e) { addresses = new Vector(); }
 27:      catch(Exception e)
 28:      {
 29:        throw new UnavailableException(this,
 30:                                       "Error reading address file: "+e);
 31:      }
 32:    }

104:    private synchronized boolean subscribe(String email) throws IOException
105:    {
106:      if(addresses.contains(email)) return false;
107:      addresses.addElement(email);
108:      save();
109:      return true;
110:    }
111:
112:    private synchronized boolean unsubscribe(String email) throws IOException
113:    {
114:      if(!addresses.removeElement(email)) return false;
115:      save();
116:      return true;
117:    }
118:
119:    private void save() throws IOException
120:    {
121:      ObjectOutputStream out =
122:        new ObjectOutputStream(new FileOutputStream(filename));
123:      out.writeObject(addresses);
124:      out.close();
125:    } 
 

Client interaction

The client interaction is handled by two of the standard HttpServlet methods, doGet and doPost.
The doGet method replies to GET requests by sending an HTML page which contains the list of the currently subscribed addresses and the form that is used to subscribe or unsubscribe an address:

 34: protected void doGet(HttpServletRequest req,
 35:                      HttpServletResponse res)
 36:           throws ServletException, IOException
 37: {
 38:   res.setContentType("text/html");
 39:   res.setHeader("pragma", "no-cache");
 40:   PrintWriter out = res.getWriter();
 41:   out.print("<HTML><HEAD><TITLE>List Manager</TITLE></HEAD>");
 42:   out.print("<BODY><H3>Members:</H3><UL>");
 43:   for(int i=0; i<addresses.size(); i++)
 44:     out.print("<LI>" + addresses.elementAt(i));
 45:   out.print("</UL><HR><FORM METHOD=POST>");
 46:   out.print("Enter your email address: <INPUT TYPE=TEXT NAME=email><BR>");
 47:   out.print("<INPUT TYPE=SUBMIT NAME=action VALUE=subscribe>");
 48:   out.print("<INPUT TYPE=SUBMIT NAME=action VALUE=unsubscribe>");
 49:   out.print("</FORM></BODY></HTML>");
 50:   out.close();
 51: }


The response content type is again set to text/html and the response is marked as not cacheable to proxy servers and clients (because it is dynamically created) by setting an HTTP header "pragma: no-cache". The form asks the client to use the POST method for submitting form data.
Here is a typical output by this method:

A browser window showing the "Members" page
  • The doPost method receives the submitted form data, updates the address list and sends back a confirmation page:
     53:    protected void doPost(HttpServletRequest req,
     54:                          HttpServletResponse res)
     55:              throws ServletException, IOException
     56:    {
     57:      String email = req.getParameter("email");
     58:      String msg;
     59:      if(email == null)
     60:      {
     61:        res.sendError(res.SC_BAD_REQUEST,
     62:                      "No email address specified.");
     63:        return;
     64:      }
     65:      if(req.getParameter("action").equals("subscribe"))
     66:      {
     67:        if(subscribe(email))
     68:          msg = "Address " + email + " has been subscribed.";
     69:        else
     70:        {
     71:          res.sendError(res.SC_BAD_REQUEST,
     72:                        "Address " + email + " was already subscribed.");
     73:          return;
     74:        }
     75:      }
     76:      else
     77:      {
     78:        if(unsubscribe(email))
     79:          msg = "Address " + email + " has been removed.";
     80:        else
     81:        {
     82:          res.sendError(res.SC_BAD_REQUEST,
     83:                        "Address " + email + " was not subscribed.");
     84:          return;
     85:        }
     86:      }
     87:
     88:      res.setContentType("text/html");
     89:      res.setHeader("pragma", "no-cache");
     90:      PrintWriter out = res.getWriter();
     91:      out.print("<HTML><HEAD><TITLE>List Manager</TITLE></HEAD><BODY>");
     92:      out.print(msg);
     93:      out.print("<HR><A HREF=\"");
     94:      out.print(req.getRequestURI());
     95:      out.print("\">Show the list</A></BODY></HTML>");
     96:      out.close();
     97:    }
    First the form parameters "email" and "action" are retrieved        with the getParameter method of        HttpServletRequest. getParameter        (and also getParameters and        getParameterValues) can be used to retrieve        form data from both, POST and GET requests. As an alternative        you can use getQueryString for a GET request        and getInputStream for a POST request and parse the        application/x-www-urlencoded data on your own.        Note that you cannot use both ways of getting the request        data together in one request.
    Then subscribe or unsubscribe is called. When a user error occurs (i.e. no address or an already subscribed address was entered for subscribe, or a not subscribed address was entered for unsubscribe) res.sendError is used to send back an error response with a Bad Request response code.
    Finally a confirmation page is sent with the usual method. req.getRequestURI() is used to get the URI of the Servlet for a link back to the main page (which is created by doGet).
As usual, the Servlet extends javax.http.servlet.HttpServlet and overrides getServletInfo to provide a short notice. At last, here is the full source code of the ListManagerServlet:

 
  1:  import java.util.Vector;
  2:  import java.io.*;
  3:  import javax.servlet.*;
  4:  import javax.servlet.http.*;
  5:
  6:  public class ListManagerServlet extends HttpServlet
  7:  {
  8:    private Vector addresses;
  9:    private String filename;
 10:
 11:    public void init(ServletConfig config) throws ServletException
 12:    {
 13:      super.init(config);
 14:      filename = config.getInitParameter("addressfile");
 15:      if(filename == null)
 16:        throw new UnavailableException(this,
 17:                                       "The \"addressfile\" property "+
 18:                                       "must be set to a file name");
 19:      try
 20:      {
 21:        ObjectInputStream in =
 22:          new ObjectInputStream(new FileInputStream(filename));
 23:        addresses = (Vector)in.readObject();
 24:        in.close();
 25:      }
 26:      catch(FileNotFoundException e) { addresses = new Vector(); }
 27:      catch(Exception e)
 28:      {
 29:        throw new UnavailableException(this,
 30:                                       "Error reading address file: "+e);
 31:      }
 32:    }
 33:
 34:    protected void doGet(HttpServletRequest req,
 35:                         HttpServletResponse res)
 36:              throws ServletException, IOException
 37:    {
 38:      res.setContentType("text/html");
 39:      res.setHeader("pragma", "no-cache");
 40:      PrintWriter out = res.getWriter();
 41:      out.print("<HTML><HEAD><TITLE>List Manager</TITLE></HEAD>");
 42:      out.print("<BODY><H3>Members:</H3><UL>");
 43:      for(int i=0; i<addresses.size(); i++)
 44:        out.print("<LI>" + addresses.elementAt(i));
 45:      out.print("</UL><HR><FORM METHOD=POST>");
 46:      out.print("Enter your email address: <INPUT TYPE=TEXT NAME=email><BR>");
 47:      out.print("<INPUT TYPE=SUBMIT NAME=action VALUE=subscribe>");
 48:      out.print("<INPUT TYPE=SUBMIT NAME=action VALUE=unsubscribe>");
 49:      out.print("</FORM></BODY></HTML>");
 50:      out.close();
 51:    }
 52:
 53:    protected void doPost(HttpServletRequest req,
 54:                          HttpServletResponse res)
 55:              throws ServletException, IOException
 56:    {
 57:      String email = req.getParameter("email");
 58:      String msg;
 59:      if(email == null)
 60:      {
 61:        res.sendError(res.SC_BAD_REQUEST,
 62:                      "No email address specified.");
 63:        return;
 64:      }
 65:      if(req.getParameter("action").equals("subscribe"))
 66:      {
 67:        if(subscribe(email))
 68:          msg = "Address " + email + " has been subscribed.";
 69:        else
 70:        {
 71:          res.sendError(res.SC_BAD_REQUEST,
 72:                        "Address " + email + " was already subscribed.");
 73:          return;
 74:        }
 75:      }
 76:      else
 77:      {
 78:        if(unsubscribe(email))
 79:          msg = "Address " + email + " has been removed.";
 80:        else
 81:        {
 82:          res.sendError(res.SC_BAD_REQUEST,
 83:                        "Address " + email + " was not subscribed.");
 84:          return;
 85:        }
 86:      }
 87:
 88:      res.setContentType("text/html");
 89:      res.setHeader("pragma", "no-cache");
 90:      PrintWriter out = res.getWriter();
 91:      out.print("<HTML><HEAD><TITLE>List Manager</TITLE></HEAD><BODY>");
 92:      out.print(msg);
 93:      out.print("<HR><A HREF=\"");
 94:      out.print(req.getRequestURI());
 95:      out.print("\">Show the list</A></BODY></HTML>");
 96:      out.close();
 97:    }
 98:
 99:    public String getServletInfo()
100:    {
101:      return "ListManagerServlet 1.0 by Stefan Zeiger";
102:    }
103:
104:    private synchronized boolean subscribe(String email) throws IOException
105:    {
106:      if(addresses.contains(email)) return false;
107:      addresses.addElement(email);
108:      save();
109:      return true;
110:    }
111:
112:    private synchronized boolean unsubscribe(String email) throws IOException
113:    {
114:      if(!addresses.removeElement(email)) return false;
115:      save();
116:      return true;
117:    }
118:
119:    private void save() throws IOException
120:    {
121:      ObjectOutputStream out =
122:        new ObjectOutputStream(new FileOutputStream(filename));
123:      out.writeObject(addresses);
124:      out.close();
125:    }
126:  }
 
Source :http://www.novocode.com

No comments:

Post a Comment