The presentation-tier request handling mechanism must control and coordinate processing of each user across multiple requests. Such control mechanisms may be managed in either a centralized or decentralized manner.
The system requires a centralized access point for presentation-tier request handling to support the integration of system services, content retrieval, view management, and navigation. When the user accesses the view directly without going through a centralized mechanism, two problems may occur:
Additionally, distributed control is more difficult to maintain, since changes will often need to be made in numerous places.
Use a controller as the initial point of contact for handling a request. The controller manages the handling of the request, including invoking security services such as authentication and authorization, delegating business processing, managing the choice of an appropriate view, handling errors, and managing the selection of content creation strategies.
The controller provides a centralized entry point that controls and manages Web request handling. By centralizing decision points and controls, the controller also helps reduce the amount of Java code, called scriptlets, embedded in the JavaServer Pages (JSP) page.
Centralizing control in the controller and reducing business logic in the view promotes code reuse across requests. It is a preferable approach to the alternative-embedding code in multiple views-because that approach may lead to a more error-prone, reuse-by-copy- and-paste environment.
Typically, a controller coordinates with a dispatcher component. Dispatchers are responsible for view management and navigation. Thus, a dispatcher chooses the next view for the user and vectors control to the resource. Dispatchers may be encapsulated within the controller directly or can be extracted into a separate component.
While the Front Controller pattern suggests centralizing the handling of all requests, it does not limit the number of handlers in the system, as does a Singleton. An application may use multiple controllers in a system, each mapping to a set of distinct services.
Figure 7.7 represents the Front Controller class diagram pattern.
Figure 7.7 Front Controller class diagram
Figure 7.8 shows the sequence diagram representing the Front Controller pattern. It depicts how the controller handles a request.
Figure 7.8 Front Controller sequence diagram
Controller
The controller is the initial contact point for handling all requests in the system. The controller may delegate to a helper to complete authentication and authorization of a user or to initiate contact retrieval.
Dispatcher
A dispatcher is responsible for view management and navigation, managing the choice of the next view to present to the user, and providing the mechanism for vectoring control to this resource.
A dispatcher can be encapsulated within a controller or can be a separate component working in coordination. The dispatcher provides either a static dispatching to the view or a more sophisticated dynamic dispatching mechanism.
The dispatcher uses the RequestDispatcher object (supported in the servlet specification) and encapsulates some additional processing.
Helper
A helper is responsible for helping a view or controller complete its processing. Thus, helpers have numerous responsibilities, including gathering data required by the view and storing this intermediate model, in which case the helper is sometimes referred to as a value bean. Additionally, helpers may adapt this data model for use by the view. Helpers can service requests for data from the view by simply providing access to the raw data or by formatting the data as Web content.
A view may work with any number of helpers, which are typically implemented as JavaBeans components (JSP 1.0+) and custom tags (JSP 1.1+). Additionally, a helper may represent a Command object, a delegate (see "Business Delegate" on page 248), or an XSL Transformer, which is used in combination with a stylesheet to adapt and convert the model into the appropriate form.
View
A view represents and displays information to the client. The view retrieves information from a model. Helpers support views by encapsulating and adapting the underlying data model for use in the display.
There are several strategies for implementing a controller.
Servlet Front Strategy
This strategy suggests implementing the controller as a servlet. Though semantically equivalent, it is preferred to the JSP Front Strategy. The controller manages the aspects of request handling that are related to business processing and control flow. These responsibilities are related to, but logically independent of, display formatting, and are more appropriately encapsulated in a servlet rather than in a JSP page.
The Servlet Front Strategy does have some potential drawbacks. In particular, it does not leverage some of the JSP runtime environment utilities, such as automatic population of request parameters into helper properties. Fortunately, this drawback is minimal because it is relatively easy to create or obtain similar utilities for general use. There is also the possibility that the functionality of some of the JSP utilities may be included as standard servlet features in a future version of the servlet specification. Example 7.14 is an example of the Servlet Front Strategy.
Example 7.14 Servlet Front Strategy Sample Code
public class EmployeeController extends HttpServlet {
// Initializes the servlet.
public void init(ServletConfig config) throws
ServletException {
super.init(config);
}
// Destroys the servlet.
public void destroy() {
}
/** Processes requests for both HTTP
* <code>GET</code> and <code>POST</code> methods.
* @param request servlet request
* @param response servlet response
*/
protected void processRequest(HttpServletRequest
request, HttpServletResponse response)
throws ServletException, java.io.IOException {
String page;
/**ApplicationResources provides a simple API
* for retrieving constants and other
* preconfigured values**/
ApplicationResources resource =
ApplicationResources.getInstance();
try {
// Use a helper object to gather parameter
// specific information.
RequestHelper helper = new
RequestHelper(request);
Command cmdHelper= helper.getCommand();
// Command helper perform custom operation
page = cmdHelper.execute(request, response);
}
catch (Exception e) {
LogManager.logMessage(
"EmployeeController:exception : " +
e.getMessage());
request.setAttribute(resource.getMessageAttr(),
"Exception occurred : " + e.getMessage());
page = resource.getErrorPage(e);
}
// dispatch control to view
dispatch(request, response, page);
}
/** Handles the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, java.io.IOException {
processRequest(request, response);
}
/** Handles the HTTP <code>POST</code> method.
* @param request servlet request
* @param response servlet response
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, java.io.IOException {
processRequest(request, response);
}
/** Returns a short description of the servlet */
public String getServletInfo() {
return "Front Controller Pattern" +
" Servlet Front Strategy Example";
}
protected void dispatch(HttpServletRequest request,
HttpServletResponse response,
String page)
throws javax.servlet.ServletException,
java.io.IOException {
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher(page);
dispatcher.forward(request, response);
}
}
JSP Front Strategy
This strategy suggests implementing the controller as a JSP page. Though semantically equivalent, the Servlet Front Strategy is preferred to the JSP Front Strategy. Since the controller handles processing that is not specifically related to display formatting, it is a mismatch to implement this component as a JSP page.
Implementing the controller as a JSP page is clearly not preferred for another reason: It requires a software developer to work with a page of markup in order to modify request handling logic. Thus, a software developer will typically find the JSP Front Strategy more cumbersome when completing the cycle of coding, compilation, testing, and debugging. Example 7.15 is an example of the JSP Front Strategy.
Example 7.15 Front Strategy Sample Code
<%@page contentType="text/html"%>
<%@ page import="corepatterns.util.*" %>
<html>
<head><title>JSP Front Controller</title></head>
<body>
<h3><center> Employee Profile </h3>
<%
/**Control logic goes here...
At some point in this code block we retrieve
employee information, encapsulate it within a value
object and place this bean in request scope with the
key "employee". This code has been omitted.
We either dispatch to another JSP at this point or
simply allow the remaining portions of scriptlet
code to execute**/
%>
<jsp:useBean id="employee" scope="request"
class="corepatterns.util.EmployeeTO"/>
<FORM method=POST >
<table width="60%">
<tr>
<td> First Name : </td>
<td> <input type="text"
name="<%=Constants.FLD_FIRSTNAME%>"
value="<jsp:getProperty name="employee"
property="firstName"/>"> </td>
</tr>
<tr>
<td> Last Name : </td>
<td> <input type="text"
name="<%=Constants.FLD_LASTNAME%>"
value="<jsp:getProperty name="employee"
property="lastName"/>"></td>
</tr>
<tr>
<td> Employee ID : </td>
<td> <input type="text"
name="<%=Constants.FLD_EMPID%>"
value="<jsp:getProperty name="employee"
property="id"/>"> </td>
</tr>
<tr>
<td> <input type="submit"
name="employee_profile"> </td>
<td> </td>
</tr>
</table>
</FORM>
</body>
</html>
Command and Controller Strategy
Based on the Command pattern [GoF], the Command and Controller Strategy suggests providing a generic interface to the helper components to which the controller may delegate responsibility, minimizing the coupling among these components (see "View Helper" on page 33 for more information on helper components). Adding to or changing the work that needs to be completed by these helpers does not require any changes to the interface between the controller and the helpers, but rather to the type and/or content of the commands. This provides a flexible and easily extensible mechanism for developers to add request handling behaviors.
Finally, because the command processing is not coupled to the command invocation, the command processing mechanism may be reused with various types of clients, not just with Web browsers. This strategy also facilitates the creation of composite commands (see Composite pattern [GoF]). See Example 7.16 for sample code and Figure 7.9 for a sequence diagram.
Example 7.16 Command and Controller Strategy Sample Code
/** This processRequest method is invoked from both
* the servlet doGet and doPost methods **/
protected void processRequest(HttpServletRequest
request, HttpServletResponse response)
throws ServletException, java.io.IOException {
String resultPage;
try {
RequestHelper helper = new RequestHelper(request);
/** the getCommand() method internally uses a
factory to retrieve command objects as follows:
Command command = CommandFactory.create(
request.getParameter("op"));
**/
Command command = helper.getCommand();
// delegate request to a command object helper
resultPage = command.execute(request, response);
}
catch (Exception e) {
LogManager.logMessage("EmployeeController",
e.getMessage() );
resultPage = ApplicationResources.getInstance().
getErrorPage(e);
}
dispatch(request, response, resultPage);
}
Figure 7.9 Command and Controller Strategy sequence diagram
Physical Resource Mapping Strategy
All requests are made to specific physical resource names rather than logical names. An example is the following URL: http://some.server.com/resource1.jsp
. In the case of a controller, an example URL might be http://some.server.com/ servlet/Controller
. The Logical Resource Mapping Strategy is typically preferred over this strategy because it provides much greater flexibility. The Logical Resource Mapping Strategy lets you modify resource mappings in a declarative manner, via a configuration file. This is much more flexible than the Physical Resource Mapping Strategy, which requires that you make changes to each resource, as is necessary when implementing this strategy.
Logical Resource Mapping Strategy
Requests are made to logical resource names rather than to specific physical names. The physical resources to which these logical names refer may then be modified in a declarative manner.
For example, the URL http://some.server.com/process
may be mapped as follows:
process=resource1.jsp
OR
process=resource2.jsp
OR
process=servletController
Multiplexed Resource Mapping Strategy
This is actually a substrategy of Logical Resource Naming Strategy. This strategy maps not just a single logical name, but an entire set of logical names, to a single physical resource. For example, a wildcard mapping might map all requests that end with .ctrl
to a specific handler.
A request and mapping might look as shown in Table 7-1.
Table 7-1
Request | Mapping |
---|---|
http://some.server.com/action.ctrl | *.ctrl = servletController |
In fact, this is the strategy JSP page engines use in order to ensure that requests for JSP page resources (that is, resources whose names end in .jsp
) are processed by a specific handler.
Additional information can also be added to a request, providing further details to leverage for this logical mapping. See Table 7-2.
Table 7-2
Request | Mapping |
---|---|
http://some.server.com/profile.ctrl?usecase= create | *.ctrl = servletController |
A key benefit of using this strategy is that it provides great flexibility when designing your request handling components. When combined with other strategies, such as the Command and Controller Strategy, you can create a powerful request handling framework.
Consider a controller that handles all requests ending in .ctrl
, as described above. Also, consider the left side of this dot-delimited resource name ( profile
in the above example) to be one part of the name of a use case. Now combine this name with the query parameter value ( create
in the above example). We are signaling our request handler that we want to process a use case called create profile
. Our multiplexed resource mapping sends the request to our servletController, which is part of the mapping shown in Table 7-2 . Our controller creates the appropriate command object, as described in the Command and Controller Strategy. How does the controller know the command object to which it should delegate? Leveraging the additional information in the request URI, the controller delegates to the command object that handles profile creation. This might be a ProfileCommand object that services requests for Profile creation and modification, or it might be a more specific ProfileCreationCommand object.
Dispatcher in Controller Strategy
When the dispatcher functionality is minimal, it can be folded into the controller, as shown in Figure 7.10.
Figure 7.10 Dispatcher in the Controller sequence diagram
Base Front Strategy
Used in combination with the Servlet Front Strategy, this strategy suggests implementing a controller base class, whose implementation other controllers may extend. The base front may contain common and default implementations, while each subclass can override these implementations. The drawback of this strategy is the fact that any shared superclass, while promoting reuse and sharing, raises the issue of creating a fragile hierarchy, where changes necessary for one subclass affect all subclasses.
Filter Controller Strategy
Filters provide similar support for centralizing request processing control (see Intercepting Filter pattern). Thus, some aspects of a controller can reasonably be implemented as a filter. At the same time, filters primarily focus on request interception and decoration, not request processing and response generation. While there are overlapping responsibilities, such as managing logging or debugging, each component complements the other when used appropriately.
A controller provides a central place to handle system services and business logic across multiple requests. A controller manages business logic processing and request handling. Centralized access to an application means that requests are easily tracked and logged. Keep in mind, though, that as control centralizes, it is possible to introduce a single point of failure. In practice, this rarely is a problem, though, since multiple controllers typically exist, either within a single server or in a cluster.
A controller centralizes control, providing a choke point for illicit access attempts into the Web application. In addition, auditing a single entrance into the application requires fewer resources than distributing security checks across all pages.
A controller promotes cleaner application partitioning and encourages reuse, as code that is common among components moves into a controller or is managed by a controller.
The Front Controller pattern, in conjunction with the View Helper pattern, describes factoring business logic out of the view and providing a central point of control and dispatch. Flow logic is factored forward into the controller and data handling code moves back into the helpers.
Both Intercepting Filter and Front Controller describe ways to centralize control of certain types of request processing, suggesting different approaches to this issue.
The Dispatcher View and Service to Worker patterns are another way to name the combination of the View Helper pattern with a dispatcher, and Front Controller pattern. Dispatcher View and Service to Worker, while structurally the same, describe different divisions of labor among components.