by Stephen Rao and Mary Xing

Design Java servlets with the Delegation Event Model

news
Sep 1, 199910 mins

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()); } } }

The HTML

Let’s briefly look at an HTML form that can make a POST request to the pointed Java servlet running in a Web server. In HTML, we have a consistent way of specifying the name of an HTTP request handler. In an HTML POST form, for example, we always have a hidden field called “handler.” Its value is the name of the handler. In a GET request, the query string starts with “handler=xxx.” Here is an HTML code snippet of a log-in page example:

Discussion of the code

As demonstrated in this article, our approach to creating a robust and flexible servlet application was simulate the Event Delegation Model in the servlet framework. It is not exactly identical to the model in the java.awt package of JDK 1.1 — we borrowed the event delegation pattern and tailored the details for the servlets. There are several areas in which we could go further to pursue more specific delegation, such as defining a PostRequestHandler as a sub-interface of the RquestHandler. We chose not to do that, however, because we do not see much benefit compared to the effort it would take. The code examples included in this article show a servlet in a three-tier or n-tier application context that might involve a number of services. Although the Java Servlet API offers logging functions, we have found it useful to put a log object in the RequestHandler interface, so the interface users can implement their ad hoc logging functions. The code examples used System.out for simplicity.

Conclusion

Event delegation is a generic model built into the Java language. Extending this model to Java servlet design brings a more generic Java programming style and philosophy to servlet programs, which is a good thing. In our design, the front servlet functions as an event source. It can register event listeners and trigger their handling procedures. Those objects that implement RequestHandler interface are event listeners/handlers. They are responsible for handling their registered POST and GET requests. Using this architecture, our servlet program became more modular, extensible, and reusable. By using the RequestHandler interface, we also achieved remarkable polymorphism, and produced simple, clear code.

none Stephen Rao is a senior software engineer at Texas Medical Center in Houston, Texas. He has an MS degree in computer science and has been involved in object-oriented software development for six years. Stephen is a Sun Certified Java Architect. Mary Xing is a software developer for Kinesix Corp. in Houston. She has focused on application development using Java technology since 1996.