Design Java servlets with the Delegation Event Model

Use the idea of Delegation Event Model to build robust and flexible servlet programs

As a servlet developer, you probably know the importance of having a single entry point for all the POST/GET requests in your servlet Web application. With such a front servlet (also referred to as a "request dispatching servlet"), your application has a chance to do the things common to all request handlers, like session verification and so on. You also know that for a complex application you should not put your entire code into one servlet. But what should you do?

We faced such a situation during a programming project several months ago. At the beginning, we used one front servlet and a list of if-else statements to dispatch requests to individual handlers. It worked fine, but as our application continued to evolve, the if-else sequence in our front servlet code became longer and longer -- a definite design concern.

The problem we encountered with the long if-else list led us to the idea of the Delegation Event Model in JDK1.1, which offers a good method for handling GUI events in Java. With this idea in mind, we extended the model to our servlet program. Along with other relevant design improvements, we eventually built a servlet application with a simple, flexible, and extensible architecture. In this article, we will demonstrate our design, and the ideas behind it, for building servlet applications using the delegation event model.

The RequestHandler interface

Our servlet design starts with the RequestHandler interface. Like those XXXListener interfaces in the java.awt.event package, any objects with this interface can be registered to handle a specific event or request. For example, when you add an ActionListerner to a button, the listener's actionPerformed() method will be called when the button is clicked. Similarly, if you add a RequestHandler to a servlet's HTTP POST event, the handler's processRequest() method will be called when the servlet receives this specific POST request.

Here is the RequestHandler interface:

package article.servlet;

import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import article.obj.User;

public interface RequestHandler { public void processRequest(Object user, HttpServletRequest req, HttpServletResponse res, Object[] services, Object log) throws ServletException, IOException; }

Let's examine the interface code. First, all request handlers implement the RequestHandler interface. In its only defined method, processRequest(), the first argument is a User object, which indicates who is making the request. The handler can use this object in the Web request processing. Most GET/POST handlers need to know who the requester is in order to react accordingly. The third argument is a collection of service objects, which can be database connections, RMI server references, or even ORB references needed by the RequestHandler. In today's heterogeneous world, it is common for a Web application to use several databases or remote services. The log PrintStream makes it possible to log information for this application to a separate place.

Multiple sequential methods such as setParameters() and processRequest() are not suitable to the multithreaded servlet environment, because instance variables are used to hold values. As a rule of thumb, instance variables should be avoided whenever possible in servlets to keep concurrent request processing thread-safe.

The front servlet

The front servlet takes all the GET/POST requests and uses their request arguments to determine where to dispatch them. It instantiates all the specific request handlers and manages them so that they handle various requests correctly. Let's take a look:

package article.ui;

import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*;

import article.servlet.*; import article.util.html.*; import article.obj.*; import article.client.*;

public class FrontSvt extends HttpServlet {

private Hashtable _postHandlers = new Hashtable(100); private Hashtable _getHandlers = new Hashtable(100);

private Object[] _services; private PrintStream _log; private HtmlRoutine _routine = new HtmlRoutine();

private RequestHandler _searchByIdH; private RequestHandler _loginH; private RequestHandler _showDemoPageH; private RequestHandler _showMenuPageH; private RequestHandler _selectDemoH; private RequestHandler _selectSiteH; private RequestHandler _selectHistH; private RequestHandler _showResultH;

private RequestHandler _logoutH; private RequestHandler _disconnectH; private RequestHandler _reloadH;

private String _msg = "";

/** * init method */ public void init(ServletConfig config) throws ServletException {

super.init(config);

try { //First: get service references _services = new Object[2]; //make db connections or find RMI references //then store them in _services ...

//Second: init RequestHandlers _loginH = new LoginH(); _showMenuPageH = new ShowMenuPageH(); _showDemoPageH = new ShowDemoPageH(); _selectDemoH = new SelectDemoH(); _selectSiteH = new SelectSiteH(); _selectHistH = new SelectHistH(); _showResultH = new ShowResultH(); _searchByIdH = new SearchByIdH();

_logoutH = new LogoutH(); _disconnectH = new DisconnectH(); _reloadH = new ReloadH();

//Third: put RequestHandlers in place addPostRequestHandler("login", _loginH); addPostRequestHandler("showMenuPage", _showMenuPageH); addPostRequestHandler("showDemoPage"_showDemoPageH); addPostRequestHandler("selectDemo", _selectDemoH); addPostRequestHandler("selectSite", _selectSiteH); addPostRequestHandler("selectHist", _selectHistH); addPostRequestHandler("showResult", _showResultH); addPostRequestHandler("searchById", _searchByIdH);

addGetRequestHandler("logout", _logoutH); addGetRequestHandler("disconnect", _disconnectH); addGetRequestHandler("reload", _reloadH); } catch(Exception e) { _msg = _msg+ "init() failed. "+e.getMessage(); System.out.println(_msg); } }

public void destroy() { //First: release resources and clean up ... super.destroy(); System.out.println("\n"+Client.getCurrentLocalTimeStr()+" - XXXXX servlet destroyed."); }

public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

//Set output HTML content type res.setContentType("text/html");

//Get output stream ServletOutputStream out = res.getOutputStream();

String handlerName = req.getParameter("handler"); if(handlerName.equals("login")) { _loginH.processRequest(null, req, res, _services, _log); return; }

HttpSession session = req.getSession(false); if(session == null) { String msg="You have been logged out or your session is timed out. "; msg+="Please relogin. <a href=\"" + Client.getLoginPageUrl() + "\">"; msg+="Goto login page</a>"; _routine.printMsgPage(out, msg); return; }

User user = (User)session.getValue("article.user"); RequestHandler handler = getPostRequestHandler(handlerName); if(handler == null) { String msg="Unregistered POST request. "; _routine.printMsgPage(out, msg); return; }

handler.processRequest(user, req, res, _services, _log); return; }

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

//Set output HTML content type res.setContentType("text/html");

//Get output stream ServletOutputStream out = res.getOutputStream();

HttpSession session = req.getSession(false);

if(session == null) { String msg="You have been logged out or your session is timed out. "; msg+="Please relogin. <a href=\"" + Client.getLoginPageUrl() + "\">"; msg+="Goto login page</a>"; _routine.printMsgPage(out, msg); return; }

User user = (User)session.getValue("article.user"); String query = req.getQueryString(); String handlerName = getGetRequestHandlerName(query); RequestHandler handler = getGetRequestHandler(handlerName); if(handler == null) { String msg="Unregistered GET request. "; _routine.printMsgPage(out, msg); return; }

handler.processRequest(user, req, res, _services, _log); return; }

public void addPostRequestHandler(String name, RequestHandler handler) { _postHandlers.put(name, handler); }

public void removePostRequestHandler(String name) { _postHandlers.remove(name); }

public void removeAllPostRequestHandlers() { _postHandlers.clear(); }

public void addGetRequestHandler(String name, RequestHandler handler) { _getHandlers.put(name, handler); }

public void removeGetRequestHandler(String name) { _getHandlers.remove(name); }

public void removeAllGetRequestHandlers() { _getHandlers.clear(); }

private RequestHandler getPostRequestHandler(String name) { return (RequestHandler)_postHandlers.get(name); }

private RequestHandler getGetRequestHandler(String name) { return (RequestHandler)_getHandlers.get(name); } }

Let's point out a few important aspects of the servlet code above. Our front servlet has two RequestHandler collections. One is for POST requests and the other is for GET requests. The collections are implemented with Hashtable for quick and simple lookup. The front servlet can register a RequestHandler by the addPostRequestHandler() or addGetRequestHandler() method with its given name as the key in the hashtable. This design requires that every HTTP POST request have a hidden field named "handler" in the HTML form. Its value is the name of the requested handler. Whenever the front servlet receives a POST request, it first gets the name of its handler, then finds the legitimate responder in its registered handler list, and finally delegates the handling. Likewise, the handler's name must be coded in the query string for GET requests. In this arrangement, the front servlet operates as a request-generating source and dispatcher, quite like an AWT component in JDK 1.1.

The RequestHandler object

Every request handler implements the RequestHandler interface. We'll use the LoginH as an example. RequestHandler objects implement the RequestHandler interface, and they handle a specific Web request. Here is a login request handler example defined as LoginH.java:

package article.ui;

import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*;

import article.obj.*; import article.servlet.*; import article.client.*;

public class LoginH implements RequestHandler { HtmlRoutine _routine = new HtmlRoutine();

public void processRequest(Object user, HttpServletRequest req, HttpServletResponse res, Object[] services, Object log) throws ServletException, IOException {

ServletOutputStream out = res.getOutputStream();

String userId = req.getParameter("userID"); String password = req.getParameter("password");

if(userId == null || password == null||userId.length()<1 ||password.length()<1) { _routine.printMsgPage(out, "User name and password are required for login."); return; }

User userr = null; Client client = (Client)services[0]; try { userr = client.login(userId, password); if(userr == null) { _routine.printMsgPage(out, "Unable to login."); return; } else { HttpSession session = req.getSession(true); session.putValue("article.user", userr); String broadcastMsg = client.getBroadcastMsg(); _routine.printNewsPage(out, broadcastMsg); return; }

} catch(Exception e) { _routine.printMsgPage(out, "unable to login with exception: "+e.getMessage()); } } }

1 2 Page 1
Page 1 of 2