Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Backstage at the JDC - Servlets In-Depth

 
 


The previous two articles in the Backstage at the JDC series discussed the infrastructure and servlets behind session and registration management. This "behind the scenes" look explores the various other servlets used by the JavaSM Developer Connection SM (JDC) to provide the functionalities of the site.

Some of the facilities covered in this article are: retrieving userId information, password mailing, restricted page access, and bug voting (a poll-taking application).  All the examples are based upon Java Web Server 1.0.3 (the JDC plans on porting to version 1.1 soon). If you haven't already read the previous Backstage at the JDC articles, it would be helpful to scan over the source code, because this article expands upon many of the ideas presented there see: JDC Session Management and JDC Registration Management.  Also, if you are new to the Java Web Server, or to servlets in general, you might want to review the background articles presented at the end of this article.

The source for the servlets presented in this article as well as the previous two "Backstage at the JDC" articles is available for download here.

JDC Servlets

The servlets used in the JDC fall into three basic categories:

  • Those for filtering HTML files.
  • Those providing some automated service (similar to CGI scripts).
  • Those that expand some inline information.

Filtering servlets generally process a given HTML page and insert additional information before sending the page on to the browser. (Note: with JWS 1.1, there are new features that help to facilitate the building of dynamic pages, but that discussion will be saved for another time). One of the places currently using a filtering servlet is the Bug Parade, where a servlet replaces various special tags with pertinent bug data before serving up the page.

The JDC password mailer facility, meanwhile, is an example of an automated service servlet. Receiving either an email address or a userId via an HTML form, the servlet looks up the user's forgotten password and mails it directly to the user.

Finally, servlets that expand and insert some inline information into a page (such as a parameter value obtained via database lookup) are used throughout the JDC. Note that such inline expansion servlets are also sometimes known as server-side includes.

All the code samples in this article are available for download. The examples generally only show information pertinent to the issues being discussed, and may not contain all the imports and disclaimers included in the complete download

Building a Base

Because many of the  servlets used in the JDC require session and member information, two common abstractions are provided: SessionHttpServlet and JDCHttpServlet. The SessionHttpServlet provides session information, while the JDCHttpServlet provides member information.

SessionHttpServlet

The SessionHttpServlet is the base class for any servlet that requires Session information. Basically, this class extracts the SessionId from the browser Cookie, retrieves the Session instance from the SessionServlet, and then invokes the serviceRequest method. The serviceRequest method receives the HttpServletRequest, the HttpServletResponse, and the Session object (see sample code below):

  1. abstract public class SessionHttpServlet extends HttpServlet
  2. {
  3.   /**
  4.    * Reference to the SessionServlet instance.
  5.    */
  6.   protected SessionServlet sessionServlet;
  7.   /**
  8.    * Intialize the Servlet and get a referecne
  9.    * to the SessionServlet
  10.    * @param config Servlet Configuration informatin
  11.    * @return void
  12.    * @throws ServletException Thrown if a problem
  13.    * occurs during initalization.
  14.    */
  15.   public void init (ServletConfig config) throws ServletException
  16.   {
  17.     super.init(config);
  18.     try
  19.     {
  20.       // The ServletContext accessed via the ServletConfig contains
  21.       // named references to all servlets configured in the
  22.       // servlets.properties file. The name of the servlet
  23.       // is the named assigned in the properties. For example:
  24.       // servlet.code.SessionServlet= sun.jdc.uth3.SessionServlet
  25.       // is named SessionServlet
  26.       sessionServlet = (SessionServlet)( config.getServletContext().getServlet("SessionServlet"));
  27.     }
  28.     catch (Exception e)
  29.     {
  30.       throw new ServletException (e.getMessage());
  31.     }
  32.   }
  33.   /**
  34.    * Main service routine. Here we get the   Session instance and call serviceRequest
  35.    * @param request The HttpServletRequest
  36.    * @param response The HttpServletResponse
  37.    * @return void
  38.    * @throws IOException
  39.    * @throws ServletException
  40.    */
  41.   public void service (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
  42.   {
  43.     // Find the Session instance based on the  session Id stored in the Cookie.
  44.     // To be safe, we check for all  possible cookie values set for
  45.     // the request.
  46.     Cookie c[] = Cookie.getCookies(request);
  47.     Session session = null;
  48.     if ( c != null )
  49.     {
  50.       for ( int i = 0; i < c.length && session == null; i++ )
  51.       {
  52.         session = sessionServlet.whoIs( c[i].getValue());
  53.       }
  54.     }
  55.     serviceRequest (request, response, session);
  56.   }
  57.   /**
  58.    * Method invoke with Session information.
  59.    * @param request The HttpServletRequest
  60.    * @param response The HttpServletResponse
  61.    * @param session Session instance associated  with the request. The instance can be null, meaning no valid
  62.    * Session is associated with the request.
  63.    * @return void
  64.    * @throws IOException
  65.    * @throws ServletException
  66.    */
  67.   abstract protected void serviceRequest ( HttpServletRequest request, HttpServletResponse response, Session session) throws IOException, ServletException;
 

Meanwhile, obtaining information about specific member accounts is achieved by extending the SessionHttpServlet into the JDCHttpServlet.

JDCHttpServlet

A Session contains not only a unique SessionId but also the userId. By extending the SessionHttpServlet, you can obtain specific member information. The JDCHttpServlet extends the SessionHttpServlet to include JDCMember information. As part of this class, serviceRequest is again defined as an abstract method. Extenders of JDCHttpServlet implement the method when needing JDCMember information as part of a given request:

  1. abstract public class JDCHttpServlet extends SessionHttpServlet
  2. {
  3.   /**
  4.    * UserRegistry instance
  5.    */
  6.   protected UserRegistry userRegistry;
  7.   /**
  8.    * Initialize the Servlet and get a reference to the
  9.    * SessionServlet
  10.    * @param config Servlet Configuration information
  11.    * @return void
  12.    * @throws ServletException Thrown if a problem occurs during
  13.    * initialization.
  14.    */
  15.   public void init (ServletConfig config) throws ServletException
  16.   {
  17.     super.init(config);
  18.     userRegistry = new UserRegistryDefaultImpl();
  19.   }
  20.   /**
  21.    * Method invoke with Session information.
  22.    * @param request The HttpServletRequest
  23.    * @param response The HttpServletResponse
  24.    * @param session Session instance associated  with the request. The instance can be null, meaning no valid
  25.    * Session is associated with the request.
  26.    * @return void
  27.    * @throws IOException
  28.    * @throws ServletException
  29.    */
  30.   protected void serviceRequest (HttpServletRequest request, HttpServletResponse response, Session session) throws IOException, ServletException
  31.   {
  32.     JDCMember member = null;
  33.     if ( session != null )
  34.     {
  35.       // Look up the id from the UserRegistry
  36.       try
  37.       {
  38.         JDCMember m[] = userRegistry.getMember (session.getUserId());
  39.         if ( m.length == 1 )
  40.         {
  41.           member = m[0];
  42.         }
  43.         else
  44.         {
  45.           member = null;
  46.         }
  47.       }
  48.       catch (JDCAdminException e)
  49.       {
  50.         e.printStackTrace();
  51.         member = null;
  52.       }
  53.     }
  54.     serviceRequest (request, response, member);
  55.   }
  56.   /**
  57.    * Method invoke with Member information.
  58.    * @param request The HttpServletRequest
  59.    * @param response The HttpServletResponse
  60.    * @param member JDCMember instance associated with the request. The instance can be null, meaning no valid
  61.    * JDCMember is associated with the request.
  62.    * @return void
  63.    * @throws IOException
  64.    * @throws ServletException
  65.    */
  66.   abstract protected void serviceRequest (HttpServletRequest request, HttpServletResponse response, JDCMember member) throws IOException, ServletException;
  67. }
 

With a common base from which to extend additional servlets, it is now a simple matter to provide the many necessary services of the JDC. For example, a page that displays a member's registration information can easily be implemented by extending the JDCHttpServlet class. The sample code below (MemberInfoServlet) uses the JDCHttpServlet to output a member's userId and email address information. (Note: The actual servlet call is placed in a file, member.shtml.)

MemberInfoServlet is an example of an inline expansion servlet--first discussed in the introduction. If you've ever wondered why some Web pages (such as the JDC Toolbar, toolbar.shtml) end in .shtml, this is the reason. The Java Web Server looks at any .shtml file, and executes the servlets embedded within the page. The output from the servlets then becomes part of the served page's content. So in this particular case, the servlet call is placed in a file, member.shtml. Then, when you request the member.shtml page, your userId and email information is seamlessly inserted into the page.

The member.shtml file should look something like this before the servlet is expanded:

      <servlet code=sun.jdc.uth3.MemberInfoServlet>       </servlet>

When a user accesses the page, the results sent back to the browser look like this:

      UserId: duke

      Email Address: duke@tall.latte

Below is the sample code for the MemberInfoServlet:

  1. public class MemberInfoServlet extends JDCHttpServlet
  2. {
  3.   /**
  4.    * Method invoke with Member information.
  5.    * @param request The HttpServletRequest
  6.    * @param response The HttpServletResponse
  7.    * @param member JDCMember instance associated  with the request. The instance can be null, meaning no valid
  8.    * JDCMember is associated with the request.
  9.    * @return void
  10.    * @throws IOException
  11.    * @throws ServletException
  12.    */
  13.   protected void serviceRequest (HttpServletRequest  request, HttpServletResponse response,  JDCMember member) throws IOException, ServletException
  14.   {
  15.     PrintStream print = new PrintStream (new BufferedOutputStream (response.getOutputStream()));
  16.     if ( member != null )
  17.     {
  18.       print.println ("<P>UserId: " + member.getUserId()+"</P>");
  19.       print.println ("<P>Email Address: " + member.getEmail()+"</P>");
  20.     }
  21.     else
  22.     {
  23.       print.println ("No member information!");
  24.     }
  25.     print.flush();
  26.     print.close();
  27.   }
  28. }
 

The SessionHttpServlet and JDCHttpServlet are found in many of the pages where JDC-related applets reside. Such servlets are often used to provide information (such as a userId) needed by the applets.

JDC Applets

All the JDC applets require a userId. In the ChatApplet, the userId identifies the participants in a chat room. For the GroupReader applet, the userId identifies who posted a given question. One very handy way to get the userId to the applets is to extend the SessionHttpServlet and create an inline servlet. As previously discussed, the inline servlet is expanded when the page is served by the Java Web Server, and its output then appears in its place.

Below is an example using the JDC's toolbar.shtml page. Note that the AppletTagServlet is called twice. The action param pertains to the servlet call, while the output from the servlet call then becomes the "value" portion of the two applet params. This is what the page looks like before the servlets are expanded:

      <applet code=jdc.ToolBar.ToolBar width=100 height=100>
      <param name=UserId value=<servlet code=AppletTagServlet>< param name=action value=UserId></servlet>>
      <param name=protocol value=<servlet code=AppletTagServlet><param name=action value=protocol></servlet>>
      </applet>

The AppletTagServlet is called (twice) before the page is sent to the browser, and it fills in the following information:

      <param name=UserId value=tony>
      <param name=protocol value=true>

Once the page is expanded, and sent back to your browser, the final applet tag for JDC member "Duke" looks something like this:

      <applet code=jdc.ToolBar.TooLBar width=100 height=100>
      <param name=UserId value="duke">
      <param name=protocol value="true">
      </applet>

The following code is a sample version of the AppletTagServlet which performs the above inline expansion of the UserId and protocol fields:

  1. public class AppletTagServlet extends SessionHttpServlet
  2. {
  3.   /**
  4.    * Implements the serviceRequest method to  expand the tag. We are interested in
  5.    * in the value of "action" retrieved  from the request.getParameter() method. If
  6.    * action == "UserId", then  expand the UserId associated with the Session. If
  7.    * action == "Protocol", then try and  determine if the request is coming through
  8.    * a proxy server and if so, set to false.
  9.    * @param request The HttpServletRequest
  10.    * @param response The HttpServletResponse
  11.    * @param session Session instance  associated with the request.
  12.    * @throws IOException
  13.    * @throws ServletExcpetion
  14.    */
  15.   public void serviceRequest (HttpServletRequest request, HttpServletResponse response, Session session) throws ServletException, IOException
  16.   {
  17.  
  18.     String value = "NO ACTION";
  19.     String action;
  20.     if ( (action=request.getParameter ("action")) != null )
  21.     {
  22.       if ( action.toLowerCase().equals  ("protocol") )
  23.       {
  24.         // If the request is forwarded, then the user is probably coming
  25.         // through a proxy server, so hint to the applet to use the Firewall
  26.         // tunneling connection by default.
  27.         if ( request.getHeader ("Forwarded") == null )
  28.         {
  29.           value = "true";
  30.         }
  31.         else
  32.         {
  33.           value = "false";
  34.         }
  35.       }
  36.       else if ( action.toLowerCase().equals ("userid") )
  37.       {
  38.         if ( session != null )
  39.         {
  40.           value = session.getUserId();
  41.         }
  42.         else
  43.        
  44.           value = "JDCGuest";
  45.         }
  46.      
  47.       else
  48.       {
  49.         value = "INVALID ACTION:"+action;
  50.       }
  51.     }
  52.     PrintStream out = new PrintStream (new BufferedOutputStream (response.getOutputStream()));
  53.     out.println (value);
  54.     out.flush();
  55.     out.close();
  56.   }
  57. }
 

BugVoting

One of the other servlet-driven features on the JDC is bug voting. This section covers the overall approach used to implement this facility.  Two servlets drive bug voting: BugVoteServlet and BugFilterServlet.  As you might expect, the BugFilterServlet is responsible for filling in information as you browse the Bug Parade pages--your own votes, the number of votes for a given bug, comments, and other pertinent data values. Meanwhile, the BugVoteServlet handles the actual voting and commenting process for a given bug.

All JDC member bug votes are stored in an Oracle database. When the BugVoteServlet is first started, it builds an internal vote table from the database--containing a variety of information about each member's bug vote: number of votes, status, comments, and so on. The table is built in a low priority thread so as not to restrict the web server from serving pages when restarted. The servlet goes through each vote in the database and tabulates how many votes a given bug has received. Since the table is built at startup, a performance penalty is only incurred when restarting the web server--which doesn't happen often. Once the table is built, it then provides accurate and instantaneous voting results.

After startup, the BugVoteServlet provides three basic services: vote for a bug, clear a vote, and comment on a bug. The HTML page consists of a form for each possible action. Every bug vote is abstracted to a BugVote class, which maintains all required information about voting. When a request is submitted to the BugVoteServlet, the BugVote instance is first located in the table, and then the appropriate action is taken: add a vote, remove a vote, or add a comment.

The BugFilterServlet displays the bug votes for a given JDC member, along with how many votes a specific bug has accrued, and its comments. Information about each bug is embedded in the page via special hidden tags (found in each corresponding bug .html file). The BugFilterServlet expands the tags in the page, substituting them with their companion data. So when you request a bug page, the SessionServlet first verifies that you are logged in, and the filter servlet then reads the page and replaces the special hidden tags with the relevant bug information.

Restricting Pages

One of the nice things about using object-oriented programming and design techniques, and the Servlet API, is that you can easily extend the behavior of your servlets. The JDC site uses this technique to ensure that certain restricted pages are accessible only by specific members.

When the SessionServlet serves up a page, it calls the serviceRequest method. Along with the request and response instances, the session instance is also passed into the method. Because the method has access to the session instance, it implicitly also has access to the UserId. In order to restrict access to a given page, one need only extend the SessionServlet class, and then appropriately override the serviceRequest, method. The following sample code demonstrates a very simple RestrictedSessionServlet, which limits the /developer/duke.html page solely to the access of JDC member duke:

  1. public class RestrictedSessionServlet extends SessionServlet
  2. {
  3.   /**
  4.    * Method called by the service routine if a valid Session
  5.    * exists and a request can be served.
  6.    * @param session The session.
  7.    * @param request The Original HttpServletRequest.
  8.    * @param response The Original HttpServletResponse.
  9.    * @throws IOException
  10.    * @throws ServletException
  11.    * @return void
  12.    */
  13.   protected void serviceRequest (Session session, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
  14.   {
  15.     boolean allowed = false;
  16.     // First check and see if the request is the page we want to restrict.
  17.     if(request.getRequestURI().equals( "/developer/duke.html") )
  18.     {
  19.       // Now check and see if the UserId is Duke  and if so service the page
  20.       if( session.getUserId().equals("duke") )
  21.       {
  22.         allowed = true;
  23.       }
  24.       else
  25.       {
  26.         allowed = false;
  27.       }
  28.     }
  29.     else
  30.     {
  31.       allowed = true;
  32.     }
  33.     if( allowed )
  34.     {
  35.       super.serviceRequest(session, request, response);
  36.     }
  37.     else
  38.     {
  39.       response.sendError(HttpServletResponse.SC_FORBIDDEN,"This page is restricted");
  40.     }
  41.   }
  42. }
 

That's all there is to it! You can now develop your own solutions with more elaborate page and user restrictions.

MailerServlet

If you've ever forgotten your password, and then requested an automated lookup from the JDC, you've likely used the /developer/passwordMailer.html page. The facility initially requires either a JDC UserId or an email address as input (to be entered by the JDC member). It then performs an automated password lookup from the JDC UserRegistry database, and then sends the password, via JavaMail, back to the anxiously waiting JDC member!

The mailer servlet itself is really quite simple, and with the JavaMail API, development is a snap. Below is the sample servlet. This version is a simplified implementation of the real thing, and performs password lookup based solely upon an entered UserId. In order to function, it requires using the UserRegistry and JDCMember classes discussed in the Registration Management article and the JavaMail API.

  1. public class PasswordMailServlet extends HttpServlet
  2. {
  3.   /**
  4.    * Mail session, not to be confused  with the SessionServlet's
  5.    * session information.
  6.    */
  7.   private javax.mail.Session _mailSession;
  8.   /**
  9.    * UserRegistry containing registration information
  10.    */
  11.   private UserRegistry _registry;
  12.   /**
  13.    * Email address put into the "from:"  header of the email
  14.    */
  15.   private String _from;
  16.   /**
  17.    * Init the Mail session. The initParamter "from""must be passed
  18.    * and contain a valid email address put into the email's "from:"
  19.    * header.
  20.    * @param config Servlet configuration
  21.    * @return void
  22.    * @throws ServletException
  23.    */
  24.   public void init (ServletConfig config) throws ServletException
  25.   {
  26.     _mailSession = javax.mail.Session.getDefaultInstance (System.getProperties(), null);
  27.     _registry = new UserRegistryDefaultImpl();
  28.     _from = config.getInitParameter ("from");
  29.   }
  30.   /**
  31.    * This is the main service routine from the servlet.
  32.    * @param request The Servlet request.
  33.    * @param response The Servlet response.
  34.    * @return void.
  35.    * @throws IOException
  36.    * @throws ServletException
  37.    */
  38.   public void service (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
  39.   {
  40.     String userId = request.getParameter ("userid");
  41.     if ( userId != null )
  42.     {
  43.       Message msg;
  44.       try
  45.       {
  46.         JDCMember m[] = _registry.getMember (userId);
  47.         if ( m.length == 1 )
  48.         {
  49.           // All JavaMail stuff. Check the JavaMail-1.0.1 API documentation for
  50.           // more details.
  51.           msg = new MimeMessage (_mailSession);
  52.           msg.addFrom (InternetAddress.parse (_from, false));
  53.           msg.addRecipients (javax.mail.Message.RecipientType.TO, InternetAddress.parse (m[0].getEmail(), false));
  54.           msg.setSubject ("Auto Reply");
  55.           msg.setText ("Your password is:  " + m[0].getPassword());
  56.           Transport.send (msg);
  57.           response.sendError (HttpServletResponse.SC_OK, "Mail Sent");
  58.         }
  59.         else
  60.         {
  61.           response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "User doesn't exist");
  62.         }
  63.       }
  64.       catch (Exception e)
  65.       {
  66.         System.err.println ("PasswordMailServlet.service:"+e.getMessage());
  67.         response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Cannot send email.");
  68.       }
  69.       finally
  70.       {
  71.         msg = null;
  72.       }
  73.     }
  74.     else
  75.     {
  76.       response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Must provide a userId");
  77.     }
  78.  
  79.   }
  80. }
 

Conclusion

This article along with the two previous Session and Registration Management articles concludes the coverage on servlets. The JDC uses the Java Web Server and the Servlet API to implement the various functions available to its members.   

If you are new to the Java Web Server, or to servlets, you might find the following documentation helpful:

coffeecup

Tony Squier is a JDC tools engineer. He developed the JDC registration-management code. He invites any feedback you have about the article or the code, such as enhancements or potential bugs. If you have comments please send them to: Tony Squier

Steven Meloan is a writer, journalist, and former software developer. His work has appeared in Wired, Rolling Stone, BUZZ, San Francisco Examiner, ZDTV's "The Site," and American Cybercast's "The Pyramid."