By Tony Squier and Steven Meloan
(May 1998)
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):
- abstract public class
SessionHttpServlet extends HttpServlet
- {
- /**
- *
Reference to the SessionServlet instance.
- */
- protected SessionServlet sessionServlet;
-
- /**
- * Intialize the Servlet and get a referecne
- * to the SessionServlet
- * @param config Servlet Configuration informatin
- * @return void
- * @throws ServletException Thrown if a problem
- * occurs during initalization.
- */
- public void init (ServletConfig
config) throws ServletException
- {
- super.init(config);
- try
- {
-
// The ServletContext accessed via the ServletConfig contains
-
// named references to all servlets configured in the
-
// servlets.properties file. The name of the servlet
-
// is the named assigned in the properties. For example:
-
// servlet.code.SessionServlet= sun.jdc.uth3.SessionServlet
-
// is named SessionServlet
-
sessionServlet = (SessionServlet)( config.getServletContext().getServlet("SessionServlet"));
- }
- catch (Exception e)
- {
-
throw new ServletException (e.getMessage());
- }
- }
-
- /**
- * Main service routine. Here we get the
Session instance and call serviceRequest
- * @param request The HttpServletRequest
- * @param response The HttpServletResponse
- * @return void
- * @throws IOException
- * @throws ServletException
- */
- public void service
(HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException
- {
-
// Find the Session instance based on the
session Id stored in the Cookie.
-
// To be safe, we check for all
possible cookie values set for
-
// the request.
-
Cookie c[] = Cookie.getCookies(request);
- Session session = null;
- if ( c != null )
- {
- for ( int i = 0; i < c.length &&
session == null; i++ )
- {
-
session = sessionServlet.whoIs( c[i].getValue());
- }
- }
- serviceRequest (request, response, session);
- }
-
- /**
- * Method invoke with Session information.
- * @param request The HttpServletRequest
- * @param response The HttpServletResponse
- * @param session Session instance associated
with the request. The instance can be null, meaning no valid
- * Session is associated with the request.
- * @return void
- * @throws IOException
- * @throws ServletException
- */
- 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:
- abstract public class JDCHttpServlet
extends
SessionHttpServlet
- {
- /**
- * UserRegistry instance
- */
- protected UserRegistry
userRegistry;
-
- /**
- * Initialize the Servlet and get a reference to the
- * SessionServlet
- * @param config Servlet Configuration information
- * @return void
- * @throws ServletException Thrown if a problem occurs
during
- * initialization.
- */
- public void init (ServletConfig
config) throws ServletException
- {
- super.init(config);
- userRegistry = new UserRegistryDefaultImpl();
- }
-
- /**
- * Method invoke with Session information.
- * @param request The HttpServletRequest
- * @param response The HttpServletResponse
- * @param session Session instance associated
with the request. The instance can be null, meaning no valid
- * Session is associated with the request.
- * @return void
- * @throws IOException
- * @throws ServletException
- */
- protected void serviceRequest
(HttpServletRequest request, HttpServletResponse response, Session session)
throws IOException, ServletException
- {
- JDCMember member = null;
- if ( session != null )
- {
- //
Look up the id from the UserRegistry
- try
- {
- JDCMember m[] =
userRegistry.getMember (session.getUserId());
- if ( m.length == 1 )
- {
- member = m[0];
- }
- else
- {
- member = null;
- }
- }
- catch (JDCAdminException e)
- {
- e.printStackTrace();
- member = null;
- }
- }
- serviceRequest (request, response, member);
- }
-
- /**
- * Method invoke with Member information.
- * @param request The HttpServletRequest
- * @param response The HttpServletResponse
- * @param member JDCMember instance associated with the
request. The instance can be null, meaning no valid
- * JDCMember is associated with the request.
- * @return void
- * @throws IOException
- * @throws ServletException
- */
- abstract protected void
serviceRequest (HttpServletRequest request, HttpServletResponse response,
JDCMember member) throws IOException, ServletException;
-
- }
|
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:
-
public class MemberInfoServlet extends JDCHttpServlet
- {
-
- /**
- * Method invoke with Member information.
- * @param request The HttpServletRequest
- * @param response The HttpServletResponse
- * @param member JDCMember instance associated
with the request. The instance can be null, meaning no valid
- * JDCMember is associated with the request.
- * @return void
- * @throws IOException
- * @throws ServletException
- */
-
protected void serviceRequest (HttpServletRequest
request, HttpServletResponse response,
JDCMember member) throws IOException, ServletException
- {
- PrintStream print = new PrintStream (new
BufferedOutputStream (response.getOutputStream()));
- if ( member != null )
- {
- print.println ("<P>UserId: " +
member.getUserId()+"</P>");
- print.println ("<P>Email Address: " +
member.getEmail()+"</P>");
- }
- else
- {
- print.println ("No member
information!");
- }
- print.flush();
- print.close();
- }
- }
|
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:
- public class AppletTagServlet extends
SessionHttpServlet
- {
- /**
- * Implements the serviceRequest method to
expand the tag. We are interested in
- * in the value of "action" retrieved
from the request.getParameter() method. If
- * action == "UserId", then
expand the UserId associated with the Session. If
- * action == "Protocol", then try and
determine if the request is coming through
- * a proxy server and if so, set to false.
- * @param request The HttpServletRequest
- * @param response The HttpServletResponse
- * @param session Session instance
associated with the request.
- * @throws IOException
- * @throws ServletExcpetion
- */
- public void serviceRequest (HttpServletRequest request,
HttpServletResponse response, Session session) throws ServletException,
IOException
- {
-
- String value = "NO ACTION";
- String action;
- if ( (action=request.getParameter ("action")) != null
)
- {
- if ( action.toLowerCase().equals
("protocol") )
- {
-
// If the request is forwarded, then the user is probably coming
-
// through a proxy server, so hint to the applet to use the Firewall
-
// tunneling connection by default.
- if ( request.getHeader
("Forwarded") == null )
- {
-
value = "true";
- }
- else
- {
- value =
"false";
- }
- }
- else if ( action.toLowerCase().equals
("userid") )
- {
- if ( session != null )
- {
- value =
session.getUserId();
- }
- else
-
- value =
"JDCGuest";
- }
-
- else
- {
- value = "INVALID
ACTION:"+action;
- }
- }
- PrintStream out = new PrintStream (new
BufferedOutputStream (response.getOutputStream()));
- out.println (value);
- out.flush();
- out.close();
- }
- }
|
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:
- public class RestrictedSessionServlet extends
SessionServlet
- {
- /**
- * Method called by the service routine if a valid Session
- * exists and a request can be served.
- * @param session The session.
- * @param request The Original HttpServletRequest.
- * @param response The Original HttpServletResponse.
- * @throws IOException
- * @throws ServletException
- * @return void
- */
- protected void serviceRequest
(Session session, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- {
- boolean allowed = false;
-
-
// First check and see if the request is the page we want to restrict.
-
if(request.getRequestURI().equals( "/developer/duke.html") )
- {
-
// Now check and see if the UserId is Duke
and if so service the page
- if(
session.getUserId().equals("duke") )
- {
- allowed = true;
- }
- else
- {
- allowed = false;
- }
- }
- else
- {
- allowed = true;
- }
- if( allowed )
- {
- super.serviceRequest(session, request,
response);
- }
- else
- {
-
response.sendError(HttpServletResponse.SC_FORBIDDEN,"This page is
restricted");
- }
- }
- }
|
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.
- public class
PasswordMailServlet extends HttpServlet
- {
- /**
- * Mail session, not to be confused
with the SessionServlet's
- * session information.
- */
- private javax.mail.Session _mailSession;
-
- /**
- * UserRegistry containing registration information
- */
- private UserRegistry _registry;
-
- /**
- * Email address put into the "from:"
header of the email
- */
- private String _from;
-
- /**
- * Init the Mail session. The initParamter "from""must be
passed
- * and contain a valid email address put into the email's
"from:"
- * header.
- * @param config Servlet configuration
- * @return void
- * @throws ServletException
- */
- public void init (ServletConfig
config) throws ServletException
- {
- _mailSession = javax.mail.Session.getDefaultInstance
(System.getProperties(), null);
- _registry = new UserRegistryDefaultImpl();
- _from = config.getInitParameter ("from");
- }
-
- /**
- * This is the main service routine from the servlet.
- * @param request The Servlet request.
- * @param response The Servlet response.
- * @return void.
- * @throws IOException
- * @throws ServletException
- */
-
public void service (HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException
- {
- String userId = request.getParameter ("userid");
- if ( userId != null )
- {
- Message msg;
- try
- {
- JDCMember m[] =
_registry.getMember (userId);
- if ( m.length == 1 )
- {
-
// All JavaMail stuff. Check the JavaMail-1.0.1 API documentation for
-
// more details.
-
msg = new MimeMessage (_mailSession);
-
msg.addFrom (InternetAddress.parse (_from, false));
- msg.addRecipients
(javax.mail.Message.RecipientType.TO, InternetAddress.parse (m[0].getEmail(),
false));
- msg.setSubject
("Auto Reply");
- msg.setText ("Your
password is: " + m[0].getPassword());
- Transport.send
(msg);
- response.sendError
(HttpServletResponse.SC_OK, "Mail Sent");
- }
- else
- {
- response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "User doesn't exist");
- }
- }
- catch (Exception e)
- {
- System.err.println
("PasswordMailServlet.service:"+e.getMessage());
- response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Cannot send email.");
- }
- finally
- {
- msg = null;
- }
- }
- else
- {
- response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Must provide a userId");
- }
-
- }
- }
|
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:
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."
|
|