Friday, May 6, 2011
Wednesday, June 2, 2010
DESIGN PATTERNS I N JAVA
Designing Enterprise Applications
with the J2EETM Platform, Second Edition
________________________________________
4.4 Web-Tier Application Framework Design
Model-View-Controller ("MVC") is the BluePrints recommended architectural design pattern for interactive applications. MVC, described in Chapter 11, organizes an interactive application into three separate modules: one for the application model with its data representation and business logic, the second for views that provide data presentation and user input, and the third for a controller to dispatch requests and control flow. Most Web-tier application frameworks use some variation of the MVC design pattern.
The MVC design pattern provides a host of design benefits. MVC separates design concerns (data persistence and behavior, presentation, and control), decreasing code duplication, centralizing control, and making the application more easily modifiable. MVC also helps developers with different skill sets to focus on their core skills and collaborate through clearly defined interfaces. For example, a J2EE application project may include developers of custom tags, views, application logic, database functionality, and networking. An MVC design can centralize control of such application facilities as security, logging, and screen flow. New data sources are easy to add to an MVC application by creating code that adapts the new data source to the view API. Similarly, new client types are easy to add by adapting the new client type to operate as an MVC view. MVC clearly defines the responsibilities of participating classes, making bugs easier to track down and eliminate.
This section describes how to use MVC to organize a J2EE Web application design using the sample application's Web Application Framework design as an example. Many of the key classes described (the controller, the templating service, the abstract action class, and so on) are usable for any application, not just for an online shopping application.
A J2EE application's Web tier serves HTTP requests. At the highest level, the Web tier does four basic things in a specific order: interprets client requests, dispatches those requests to business logic, selects the next view for display, and generates and delivers the next view. (See Figure 4.2.)
Figure 4.2 The Web-Tier Service Cycle
The Web-tier controller receives each incoming HTTP request and invokes the requested business logic operation in the application model. Based on the results of the operation and state of the model, the controller then selects the next view to display. Finally, the controller generates the selected view and transmits it to the client for presentation.
Figure 4.2 is deceptively simple. An enterprise application's Web tier commonly has the following requirements:
• An application design must have a strategy for serving current and future client types.
• A Web-tier controller must be maintainable and extensible. Its tasks include mapping requests to application model operations, selecting and assembling views, and managing screen flow. Good structure can minimize code complexity.
• Application model API design and technology selection have important implications for an application's complexity, scalability, and software quality.
• Choosing an appropriate technology for generating dynamic content improves development and maintenance efficiency.
The BluePrints best practice is to implement the Web tier of a J2EE enterprise application using an appropriate Web application framework. (See Section 4.4.5 on page 114.) The next several sections describe the general design of a J2EE application Web tier. If you choose to use a Web application framework, the following discussion will help you to understand what the framework does and how to use it. If you write your own Web-tier architectural code, the following design discussions will help you make educated decisions about how to use the technology.
4.4.1 Structuring the Web Tier
Overall structure is the most important consideration in a Web-tier design. Both the sample application and the various existing Web application frameworks implement some form of "Model 2" architecture, where a servlet manages client communication and business logic execution, and presentation resides mainly in JSP pages.
The literature on Web-tier technology in the J2EE platform frequently uses the terms "Model 1" and "Model 2" without explanation. This terminology stems from early drafts of the JSP specification, which described two basic usage patterns for JSP pages. While the terms have disappeared from the specification document, they remain in common use. Model 1 and Model 2 simply refer to the absence or presence (respectively) of a controller servlet that dispatches requests from the client tier and selects views.
A Model 1 architecture consists of a Web browser directly accessing Web-tier JSP pages. The JSP pages access Web-tier JavaBeans that represent the application model, and the next view to display (JSP page, servlet, HTML page, and so on) is determined either by hyperlinks selected in the source document or by request parameters. A Model 1 application control is decentralized, because the current page being displayed determines the next page to display. In addition, each JSP page or servlet processes its own inputs (parameters from GET or POST). In some Model 1 architectures, choosing the next page to display occurs in scriptlet code, but this usage is considered poor form. (See the design guideline Section 4.2.6.8 on page 89.)
A Model 2 architecture introduces a controller servlet between the browser and the JSP pages or servlet content being delivered. The controller centralizes the logic for dispatching requests to the next view based on the request URL, input parameters, and application state. The controller also handles view selection, which decouples JSP pages and servlets from one another. Model 2 applications are easier to maintain and extend, because views do not refer to each other directly. The Model 2 controller servlet provides a single point of control for security and logging, and often encapsulates incoming data into a form usable by the back-end MVC model. For these reasons, the Model 2 architecture is recommended for most interactive applications.
An MVC application framework can greatly simplify implementing a Model 2 application. Application frameworks such as Apache Struts and JavaServer FacesTM (see Section 4.4.5 on page 114) include a configurable front controller servlet, and provide abstract classes that can be extended to handle request dispatches. Some frameworks include macro languages or other tools that simplify application construction.
The Model 1 architecture can provide a more lightweight design for small, static applications. Model 1 architecture is suitable for applications that have very simple page flow, have little need for centralized security control or logging, and change little over time. Model 1 applications can often be refactored to Model 2 when application requirements change.
4.4.1.0.1 When to Switch from Model 1 to Model 2
JSP pages in a Model 1 application that use scripting elements, custom tags, or JavaScript to forward requests should be refactored to Model 2.
A Model 1 architecture is best when the page navigation is simple and fixed, and when a simple directory structure can represent the structure of the pages in the application. Such applications usually embed the page flow information in the links between the pages. The presence of forward in a JSP page implies that logic embedded in the page is making a decision about the next page to display.
Over time, as the application grows and changes, page flow logic accumulates. The application becomes difficult to maintain because the page flow logic is distributed across multiple pages. The best time to switch from Model 1 to Model 2 is before this maintenance problem arises. This is why it's usually best to choose Model 2 from the outset, basing the application on an existing Web controller framework that best meets application requirements. Model 1 remains a viable option for simple, static applications.
4.4.2 Web-Tier MVC Controller Design
The Model 2 architecture uses servlets for processing requests and selecting views. The Front Controller architectural design pattern centralizes an application's request processing and view selection in a single component. Each type of Web client sends requests to and receives responses from a single URL, simplifying client development. The Front Controller receives requests from the client and dispatches them to the application model. This single point of dispatch makes the Front Controller a logical place for such global facilities as security and logging. The Front Controller also selects and formats the next client view. The controller is also an application of the Mediator pattern, because it decouples view components from one another.
In the J2EE platform, a Front Controller is typically implemented as a servlet. The sample application's Front Controller servlet handles all HTTP requests. The user views, discussed in the next section, are mostly JSP pages chosen by the Front Controller.
4.4.2.1 Web-Tier Controller Design
A Web-tier MVC controller maps incoming requests to operations on the application model, and selects views based on model and session state. Web-tier controllers have a lot of duties, so they require careful design to manage complexity. Because most enterprise applications grow over time, extensibility is an important requirement. This section describes some strategies for the internal structure of a controller in the Web tier, illustrated by example code adapted from the Web Application Framework, part of the BluePrints sample application.
4.4.2.1.1 Identifying the Operation to Perform
When a controller receives an HTTP request, it needs to be able to distinguish what application operation is being requested. How can the client, for example, request that the server create a new user? There are several ways to indicate to the server which operation to perform. The more common methods include:
• Indicate the operation in a hidden form field, which a POST operation delivers to the controller; for example:
• Indicate the operation in a HTTP GET query string parameter; for example:
http://myHost/myApp/servlets/myServlet?op=createUser
• Use a servlet mapping to map all URLs with a particular suffix or base URL to a specific servlet. A servlet mapping is a deployment descriptor definition that compares request paths to a pattern and dispatches matching requests to the corresponding servlet. For example, imagine that a Web application's deployment descriptor defines the following servlet mapping:
myServlet
*.do
Imagine also that the servlet's context path is http://myServer/myApp/servlets. The servlet container would direct a request with URL http://myServer/myApp/createUser.do myServlet to myServlet, because the request URL matches the pattern *.do. Servlet myServlet can extract the requested operation's name from the request URL. Chapter 11 of the Java Servlet 2.3 specification defines servlet mappings.
Of the three options discussed here, the BluePrints recommendation is to use servlet mappings when they are available. Servlet mappings provide the most flexible way to control where to route URLs based on patterns in the URLs. Most Web application frameworks (see Section 4.4.5 on page 114) use servlet mappings to direct requests to the appropriate front controller for an application.
The sample application uses a servlet mapping to handle request URLs. The servlet container maps all request URLs matching *.do to the main Web-tier controller servlet, MainServlet.java. Another servlet mapping routes all URLs matching *.screen to the templating service, which assembles composite views.
4.4.2.1.2 Invoking Model Methods
Once the controller has determined which operation to perform, it must invoke the corresponding application model method with parameters derived from the request. A naive controller design might use a large if-then-else statement, as shown in Code Example 4.6.
if (op.equals("createUser")) {
model.createUser(request.getAttribute("user"),
request.getAttribute("pass"));
} else if (op.equals("changeUserInfo") {
// ... and so on...
}
Code Example 4.6 A Poorly Designed Controller
The if-then-else approach leads to a very large service method, which is difficult to read and still more difficult to maintain. A better approach is to use the Command pattern. The sample application defines an abstract class Action, which represents a single application model operation. A controller can look up concrete Action subclasses by name and delegate requests to them. Sample code for the abstract class Action and a concrete class CreateUserAction appears in Code Example 4.7.
// Action.java:
public abstract class Action {
protected Model model;
public Action(Model model) { this.model = model; }
public abstract String getName();
public abstract Object perform(HttpServletRequest req);
};
// CreateUserAction.java:
public class CreateUserAction extends Action {
public CreateUserAction(Model model) {
super(model);
}
public String getName() { return "createUser"; }
public Object perform(HttpServletRequest req) {
return model.createUser(req.getAttribute("user"),
req.getAttribute("pass"));
}
}
Code Example 4.7 An Abstract Action Class and a Concrete Subclass
Code Example 4.7 defines an abstract class Action, which has a name and a perform method that executes a model method corresponding to the name. For example, Action's concrete subclass CreateUserAction has the name "createUser". Its perform method invokes the model method createUser using parameters extracted from the HTTP request.
public class ControllerServlet extends HttpServlet {
private HashMap actions;
public void init() throws ServletException {
actions = new HashMap();
CreateUserAction cua = new CreateUserAction(model);
actions.put(cua.getName(), cua);
//... create and add more actions
}
public void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws IOException, ServletException {
// First identify operation "op" from URL.
// method getOperation() is defined elsewhere.
String op = getOperation(req.getRequestURL());
// Then find and execute corresponding Action
Action action = (Action)actions.get(op);
Object result = null;
try {
result = action.perform(req);
} catch (NullPointerException npx) {
//... handle error condition: no such action
}
// ... Use result to determine next view (see next section)
}
//... other methods...
}
Code Example 4.8 Using a Map to Identify and Execute Actions
Code Example 4.8 shows a controller servlet that maintains a hash map of Action objects, each indexed by its name. When the servlet loads, the servlet container calls the method init, which fills the hash map with Action objects that invoke model operations. The hash map key is the name of the operation. Each time the servlet's service method receives a request, it identifies the name of the operation to perform, looks up the corresponding Action in the hash map, and executes it by invoking the Action's perform method. The Action returns a result object that the servlet uses, along with other data, to decide which view to display next. When this controller receives a request containing the name createUser, it finds an instance of CreateUserAction in the hash map. It then invokes the Action's perform method, which uses the model to create a user (as shown in Code Example 4.7).
The code samples shown in this section are greatly simplified for clarity. The Web Application Framework used by the sample application provides a full, working example of this sort of controller called MainServlet. The servlet container dispatches requests with a servlet mapping: it forwards all URLs matching *.do to the MainServlet. Code Example 4.8 demonstrates how to provide an extensible framework for dispatching client requests.
The sample application improves the extensibility of the servlet code in Code Example 4.8 even further by externalizing the map of requests to actions. The controller in the sample application initializes the actions hash map from an external XML file, which contains pairs of operation names and corresponding Action class names. The controller servlet initializes the action map with the request names and action classes referred to in the XML file. The XML file is deployed as a resource in the Web application archive. Adding a new Action is as simple as adding a concrete Action subclass to the application archive and defining a configuration file mapping that associates the request URL with the action class. An example of such a mapping appears in Code Example 4.9. With no code modification, the sample application controller servlet can dispatch requests using actions that did not even exist when the controller was written.
Dispatching service requests to the application model is only half of the Web-tier controller's job. It is also responsible for determining the next view to display.
4.4.2.1.3 Controlling Dynamic Screen Flow
The succession of views that a Web application user sees is called screen flow. A Web-tier controller controls screen flow by selecting the next view a user sees. In static Web sites, screens (usually Web pages) are statically linked to one another. By contrast, a controller dynamically chooses the "next" screen in response to both user actions and model operation results.
In this section, the term "view" means a Web resource with a URL from which Web content is available. A view might be a JSP page, a servlet, static content, or some combination of the three, assembled into a page. Typically, the "next" view to display depends on one or more of:
• The current view
• The results of any operation on the application model, returned by modelmethod invocations
• Possibly other server-side state, kept in PageContext, ServletRequest, HttpSession, and ServletContext.
For example, the next view to display after a sign on view very likely depends on:
• The current view
• The user id and password contained in the request
• The success or failure of the sign on operation, and
• Possibly other server-side state. Examples of such state might include a maximum number of allowed users (application scope), or the URL the user was trying to access (request scope). See Section 4.4.7 on page 116 for a description of state and its scope.
The controller uses this data to determine which view to display next. A Web controller "displays a view" by forwarding the request to a JSP page, servlet, or other component that renders the view in a format compatible with the client; for example, returning HTML to a browser.
The controller in the sample application uses two components to select and generate views: a screen flow manager, which selects the next view to display; and a templating service, which actually generates the view content. The controller uses the screen flow manager to select a view, and forwards the request to the templating service, which assembles and delivers a view to the client. Both the screen flow manager and the templating servlet are generic components that are usable in any Web application. The component-based design reduces component coupling, promoting code reuse and simplifying the controller design.
Figure 4.3 Web-Tier Controller OID
Figure 4.3 is an object interaction diagram that shows the Web-tier controller interacting with other Web-tier classes. The diagram shows the following sequence of calls:
1. The controller receives a POST from the client.
2. The controller creates an Action corresponding to the requested operation (as described in the previous section).
3. The controller calls the Action's perform method.
4. perform calls a model business method.
5. The controller calls the screen flow manager to select the next view to display.
6. The screen flow manager determines the next view and returns its name to the controller.
7. The controller forwards the request to the templating service, which assembles and delivers the selected view to the client.
Most request URLs map to a specific view URL. For example, the screen flow map can define that the view signoff.screen always follows request URL /signoff.do. Sometimes the next screen to display depends on model operation results, server-side state, or user activity. For example, the next view following the request URL /signin.do, which signs a user into the system, depends on whether the sign in operation succeeded.
In the sample application, an application assembler configures the screen flow manager with an XML-based file called a screen flow map. The screen flow map defines a next view name for each request URL. For dynamic view selection, a screen flow map can also map a request URL to a flow handler, which is a Java class that selects the next view programmatically. Flow handlers are typically written by component providers or application assemblers.
4.4.2.1.4 Example
The Web Application Framework screen flow map mappings.xml configures the screen flow manager. Sample application Code Example 4.9 shows a URL action mapping that uses a flow handler to determine the next view in code.
com.sun.j2ee.blueprints.petstore.controller.web.actions.SignOffHTMLAction
Code Example 4.9 Excerpt from the Sample Application Screen Flow Map
In Code Example 4.9, the url-mapping element defines a mapping for request URL /signoff.do. The action element declares an action of class Signoff-HTMLAction, which performs the business logic for this URL (signing off a user). An application assembler or a component provider wrote the action class SignoffHTMLAction to sign a user off of the application. The screen attribute tells the screen flow manager to display screen signoff.screen after the action completes.
Figure 4.4 shows the result of an HTTP POST to the URL /signoff.do.
Figure 4.4 OID of POST to Flow Handler Defined in Code Example 4.9
The servlet container deployment descriptor has a servlet mapping from pattern *.do to the controller, so when a client POSTs a request to /verifysignin.do, the following actions occur:
1. The servlet container routes the request to the controller.
2. The controller creates an instance class SignoffHTMLAction and passes the request to it.
3. The controller calls the SignoffHTMLAction's perform method.
4. The SignoffHTMLAction object calls the model method that signs the user out of the application.
5. The controller asks the screen flow manager for the next view.
6. The controller forwards the request to the URL signoff.screen.
7. The templating servlet generates and delivers the requested view (a templated JSP page) to the client, so the user receives a view indicating that signoff succeeded.
The last piece of the puzzle not yet explained is how to map a view name in the design just described to an actual Web component (JSP page, servlet, and so on). Views and templating are discussed in Section 4.4.3 on page 110.
4.4.2.2 Serving Multiple Client Types
Web applications may support only one type of client with a single protocol, or multiple clients with different protocols, security policies, presentation logic, and workflows. Web clients may include several versions of a few different browsers, MIDP clients, so-called "rich" clients with stand-alone APIs, and Web service interfaces. Long-lived applications may need to be able to handle new types of Web clients.
Each type of client needs its own controller, which specializes in the protocols for that client type. A particular type of client may also need different presentation components for form factor or other reasons.
Following are some options for how to service requests from clients that use different application-level protocols. (Web-tier clients use HTTP for transport.) Each of the following alternatives expands upon Figure 4.2 by adding flexibility and increasing complexity.
Figure 4.5 Using a Front Controller to Handle Browser Interaction
Applications with a single client type can implement a single front controller. For example, a browser-only application is shown in Figure 4.5. Its single Front Controller servlet receives HTTP requests from the browser, translates the contents of these requests into operations on the application model, and serves views of result data as HTML (or XML). Additional controllers can support new client types, as shown in Figure 4.6.
Figure 4.6 Supporting Multiple Client Types with Multiple Controllers
The multiple-controller approach in Figure 4.6 provides extensibility for any future Web client types, including those that do not yet exist. In fact, because servlets aren't limited to HTTP, this architecture can support even non-Web clients. Each controller can implement the workflow, presentation logic, and security constraints unique to its client type. Notice also that the code implementing the application model is shared by all of the controllers. This separation of model and controller ensures identical application behavior across client types and eases maintenance and testing.
Some application functionality, particularly security, can be easier to manage from a single point. Introducing a protocol router, as shown in Figure 4.7, can provide a single point of control for all Web clients, each of which still retain their own controllers.
Figure 4.7 Using a Protocol Router for Centralized Control
The protocol router in Figure 4.7 is either a servlet or servlet filter that determines the client type and dispatches the request to the appropriate controller. The router typically uses the HTTP header User-agent to determine what sort of client is requesting service. The protocol router can implement application-wide functionality such as security or logging. The client-specific controllers can implement behavior specific to each client's particular protocol.
The Front Controllers in Figure 4.7 may or may not be servlets. If the Front Controllers are servlets, the protocol router dispatches requests to them using RequestDispatcher.forward. If the protocol router is a servlet, the Front Controllers can be a layer of simple objects to which the router delegates request processing.
Note that the controller alternatives shown in the last few figures can be implemented incrementally. Each of the approaches can be built on the preceding one. The BluePrints recommendation is to adopt and adapt the alternative that most closely matches current application requirements, and add new functionality as necessary.
Templating is an application of the Composite View design pattern, discussed in Chapter 11, which builds a page from a number of independent view components.
4.4.3 Web-Tier MVC View Design
MVC views display data produced by the MVC model. View components (also known as "presentation components") in the Web tier are usually JSP pages and servlets, along with such static resources as HTML pages, PDF files, graphics, and so on. JSP pages are best used for generating text-based content, often HTML or XML. Servlets are most appropriate for generating binary content or content with variable structure. (For an in-depth explanation of appropriate technology usage, see Section 4.2.6.1 on page 82 and Section 4.2.6.4 on page 86.)
HTML browsers are very lightweight clients, so the Web tier generates and often styles dynamic content for browsers. Heavyweight clients can implement relatively more view functionality in the Client tier, and less in the Web tier. Such clients include stand-alone rich clients, application clients, and clients that use special content formats such as MacroMedia Flash or Adobe Portable Document Format (PDF).
Web-tier components are not limited to serving HTML-over-HTTP Web browsers. The Web tier may also serve MIDP clients using proprietary protocols, rich clients using XML, or Web service peers requesting services with Electronic Business XML (ebXML) or Simple Object Access Protocol (SOAP) messages. Each of these examples uses a different application-level protocol, while using HTTP for transport. A properly designed Web tier unifies access to application functionality for any client type. The Web tier also provides virtual session management for some client types.
See Chapter 3 for more on J2EE client technologies.
4.4.3.1 Templating
One typical application requirement is that application views have a common layout. A template is a presentation component that composes separate subviews into a page with a specific layout. Each subview, such as a banner, a navigation bar, or document body content, is a separate component. Views that share a template have the same layout, because the template controls the layout.
For example, Figure 4.8 shows the layout of a single page created by a template. Across the top of the page is a banner, on the left is a navigation menu, a footer appears at the bottom, and the body content occupies the remaining space.
Figure 4.8 A Template Composes Other Views into a Consistent Layout
Using templates in an application design centralizes control of the overall layout of pages in the application, easing maintenance. Changing the layout in the template file changes the page layout for the entire application. More importantly, the individual subviews (like the "Navigation Menu" in Figure 4.8) are used by reference in the template instead of by copy-and-paste. Therefore, changing a subview means changing a single source file instead of changing all the files in which that subview occurs.
Template implementation is most easily explained by example. In the sample application, a JSP page called a template file specifies the page layout. The template file is a standard JSP page that uses custom tags to include subviews into each area of the page. The template references the individual subviews by name.
Code Example 4.10 is an example from the sample application that produces the layout shown in Figure 4.8. This file, called template.jsp, is a JSP page that produces HTML. The file specifies the page layout as standard HTML tags, and includes the content of other JSP pages using the custom tag insert, shown underlined in the code example.
<%@ taglib uri="/WEB-INF/tlds/taglib.tld" prefix="template" %>
with the J2EETM Platform, Second Edition
________________________________________
4.4 Web-Tier Application Framework Design
Model-View-Controller ("MVC") is the BluePrints recommended architectural design pattern for interactive applications. MVC, described in Chapter 11, organizes an interactive application into three separate modules: one for the application model with its data representation and business logic, the second for views that provide data presentation and user input, and the third for a controller to dispatch requests and control flow. Most Web-tier application frameworks use some variation of the MVC design pattern.
The MVC design pattern provides a host of design benefits. MVC separates design concerns (data persistence and behavior, presentation, and control), decreasing code duplication, centralizing control, and making the application more easily modifiable. MVC also helps developers with different skill sets to focus on their core skills and collaborate through clearly defined interfaces. For example, a J2EE application project may include developers of custom tags, views, application logic, database functionality, and networking. An MVC design can centralize control of such application facilities as security, logging, and screen flow. New data sources are easy to add to an MVC application by creating code that adapts the new data source to the view API. Similarly, new client types are easy to add by adapting the new client type to operate as an MVC view. MVC clearly defines the responsibilities of participating classes, making bugs easier to track down and eliminate.
This section describes how to use MVC to organize a J2EE Web application design using the sample application's Web Application Framework design as an example. Many of the key classes described (the controller, the templating service, the abstract action class, and so on) are usable for any application, not just for an online shopping application.
A J2EE application's Web tier serves HTTP requests. At the highest level, the Web tier does four basic things in a specific order: interprets client requests, dispatches those requests to business logic, selects the next view for display, and generates and delivers the next view. (See Figure 4.2.)
Figure 4.2 The Web-Tier Service Cycle
The Web-tier controller receives each incoming HTTP request and invokes the requested business logic operation in the application model. Based on the results of the operation and state of the model, the controller then selects the next view to display. Finally, the controller generates the selected view and transmits it to the client for presentation.
Figure 4.2 is deceptively simple. An enterprise application's Web tier commonly has the following requirements:
• An application design must have a strategy for serving current and future client types.
• A Web-tier controller must be maintainable and extensible. Its tasks include mapping requests to application model operations, selecting and assembling views, and managing screen flow. Good structure can minimize code complexity.
• Application model API design and technology selection have important implications for an application's complexity, scalability, and software quality.
• Choosing an appropriate technology for generating dynamic content improves development and maintenance efficiency.
The BluePrints best practice is to implement the Web tier of a J2EE enterprise application using an appropriate Web application framework. (See Section 4.4.5 on page 114.) The next several sections describe the general design of a J2EE application Web tier. If you choose to use a Web application framework, the following discussion will help you to understand what the framework does and how to use it. If you write your own Web-tier architectural code, the following design discussions will help you make educated decisions about how to use the technology.
4.4.1 Structuring the Web Tier
Overall structure is the most important consideration in a Web-tier design. Both the sample application and the various existing Web application frameworks implement some form of "Model 2" architecture, where a servlet manages client communication and business logic execution, and presentation resides mainly in JSP pages.
The literature on Web-tier technology in the J2EE platform frequently uses the terms "Model 1" and "Model 2" without explanation. This terminology stems from early drafts of the JSP specification, which described two basic usage patterns for JSP pages. While the terms have disappeared from the specification document, they remain in common use. Model 1 and Model 2 simply refer to the absence or presence (respectively) of a controller servlet that dispatches requests from the client tier and selects views.
A Model 1 architecture consists of a Web browser directly accessing Web-tier JSP pages. The JSP pages access Web-tier JavaBeans that represent the application model, and the next view to display (JSP page, servlet, HTML page, and so on) is determined either by hyperlinks selected in the source document or by request parameters. A Model 1 application control is decentralized, because the current page being displayed determines the next page to display. In addition, each JSP page or servlet processes its own inputs (parameters from GET or POST). In some Model 1 architectures, choosing the next page to display occurs in scriptlet code, but this usage is considered poor form. (See the design guideline Section 4.2.6.8 on page 89.)
A Model 2 architecture introduces a controller servlet between the browser and the JSP pages or servlet content being delivered. The controller centralizes the logic for dispatching requests to the next view based on the request URL, input parameters, and application state. The controller also handles view selection, which decouples JSP pages and servlets from one another. Model 2 applications are easier to maintain and extend, because views do not refer to each other directly. The Model 2 controller servlet provides a single point of control for security and logging, and often encapsulates incoming data into a form usable by the back-end MVC model. For these reasons, the Model 2 architecture is recommended for most interactive applications.
An MVC application framework can greatly simplify implementing a Model 2 application. Application frameworks such as Apache Struts and JavaServer FacesTM (see Section 4.4.5 on page 114) include a configurable front controller servlet, and provide abstract classes that can be extended to handle request dispatches. Some frameworks include macro languages or other tools that simplify application construction.
The Model 1 architecture can provide a more lightweight design for small, static applications. Model 1 architecture is suitable for applications that have very simple page flow, have little need for centralized security control or logging, and change little over time. Model 1 applications can often be refactored to Model 2 when application requirements change.
4.4.1.0.1 When to Switch from Model 1 to Model 2
JSP pages in a Model 1 application that use scripting elements, custom tags, or JavaScript to forward requests should be refactored to Model 2.
A Model 1 architecture is best when the page navigation is simple and fixed, and when a simple directory structure can represent the structure of the pages in the application. Such applications usually embed the page flow information in the links between the pages. The presence of forward in a JSP page implies that logic embedded in the page is making a decision about the next page to display.
Over time, as the application grows and changes, page flow logic accumulates. The application becomes difficult to maintain because the page flow logic is distributed across multiple pages. The best time to switch from Model 1 to Model 2 is before this maintenance problem arises. This is why it's usually best to choose Model 2 from the outset, basing the application on an existing Web controller framework that best meets application requirements. Model 1 remains a viable option for simple, static applications.
4.4.2 Web-Tier MVC Controller Design
The Model 2 architecture uses servlets for processing requests and selecting views. The Front Controller architectural design pattern centralizes an application's request processing and view selection in a single component. Each type of Web client sends requests to and receives responses from a single URL, simplifying client development. The Front Controller receives requests from the client and dispatches them to the application model. This single point of dispatch makes the Front Controller a logical place for such global facilities as security and logging. The Front Controller also selects and formats the next client view. The controller is also an application of the Mediator pattern, because it decouples view components from one another.
In the J2EE platform, a Front Controller is typically implemented as a servlet. The sample application's Front Controller servlet handles all HTTP requests. The user views, discussed in the next section, are mostly JSP pages chosen by the Front Controller.
4.4.2.1 Web-Tier Controller Design
A Web-tier MVC controller maps incoming requests to operations on the application model, and selects views based on model and session state. Web-tier controllers have a lot of duties, so they require careful design to manage complexity. Because most enterprise applications grow over time, extensibility is an important requirement. This section describes some strategies for the internal structure of a controller in the Web tier, illustrated by example code adapted from the Web Application Framework, part of the BluePrints sample application.
4.4.2.1.1 Identifying the Operation to Perform
When a controller receives an HTTP request, it needs to be able to distinguish what application operation is being requested. How can the client, for example, request that the server create a new user? There are several ways to indicate to the server which operation to perform. The more common methods include:
• Indicate the operation in a hidden form field, which a POST operation delivers to the controller; for example:
• Indicate the operation in a HTTP GET query string parameter; for example:
http://myHost/myApp/servlets/myServlet?op=createUser
• Use a servlet mapping to map all URLs with a particular suffix or base URL to a specific servlet. A servlet mapping is a deployment descriptor definition that compares request paths to a pattern and dispatches matching requests to the corresponding servlet. For example, imagine that a Web application's deployment descriptor defines the following servlet mapping:
Imagine also that the servlet's context path is http://myServer/myApp/servlets. The servlet container would direct a request with URL http://myServer/myApp/createUser.do myServlet to myServlet, because the request URL matches the pattern *.do. Servlet myServlet can extract the requested operation's name from the request URL. Chapter 11 of the Java Servlet 2.3 specification defines servlet mappings.
Of the three options discussed here, the BluePrints recommendation is to use servlet mappings when they are available. Servlet mappings provide the most flexible way to control where to route URLs based on patterns in the URLs. Most Web application frameworks (see Section 4.4.5 on page 114) use servlet mappings to direct requests to the appropriate front controller for an application.
The sample application uses a servlet mapping to handle request URLs. The servlet container maps all request URLs matching *.do to the main Web-tier controller servlet, MainServlet.java. Another servlet mapping routes all URLs matching *.screen to the templating service, which assembles composite views.
4.4.2.1.2 Invoking Model Methods
Once the controller has determined which operation to perform, it must invoke the corresponding application model method with parameters derived from the request. A naive controller design might use a large if-then-else statement, as shown in Code Example 4.6.
if (op.equals("createUser")) {
model.createUser(request.getAttribute("user"),
request.getAttribute("pass"));
} else if (op.equals("changeUserInfo") {
// ... and so on...
}
Code Example 4.6 A Poorly Designed Controller
The if-then-else approach leads to a very large service method, which is difficult to read and still more difficult to maintain. A better approach is to use the Command pattern. The sample application defines an abstract class Action, which represents a single application model operation. A controller can look up concrete Action subclasses by name and delegate requests to them. Sample code for the abstract class Action and a concrete class CreateUserAction appears in Code Example 4.7.
// Action.java:
public abstract class Action {
protected Model model;
public Action(Model model) { this.model = model; }
public abstract String getName();
public abstract Object perform(HttpServletRequest req);
};
// CreateUserAction.java:
public class CreateUserAction extends Action {
public CreateUserAction(Model model) {
super(model);
}
public String getName() { return "createUser"; }
public Object perform(HttpServletRequest req) {
return model.createUser(req.getAttribute("user"),
req.getAttribute("pass"));
}
}
Code Example 4.7 An Abstract Action Class and a Concrete Subclass
Code Example 4.7 defines an abstract class Action, which has a name and a perform method that executes a model method corresponding to the name. For example, Action's concrete subclass CreateUserAction has the name "createUser". Its perform method invokes the model method createUser using parameters extracted from the HTTP request.
public class ControllerServlet extends HttpServlet {
private HashMap actions;
public void init() throws ServletException {
actions = new HashMap();
CreateUserAction cua = new CreateUserAction(model);
actions.put(cua.getName(), cua);
//... create and add more actions
}
public void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws IOException, ServletException {
// First identify operation "op" from URL.
// method getOperation() is defined elsewhere.
String op = getOperation(req.getRequestURL());
// Then find and execute corresponding Action
Action action = (Action)actions.get(op);
Object result = null;
try {
result = action.perform(req);
} catch (NullPointerException npx) {
//... handle error condition: no such action
}
// ... Use result to determine next view (see next section)
}
//... other methods...
}
Code Example 4.8 Using a Map to Identify and Execute Actions
Code Example 4.8 shows a controller servlet that maintains a hash map of Action objects, each indexed by its name. When the servlet loads, the servlet container calls the method init, which fills the hash map with Action objects that invoke model operations. The hash map key is the name of the operation. Each time the servlet's service method receives a request, it identifies the name of the operation to perform, looks up the corresponding Action in the hash map, and executes it by invoking the Action's perform method. The Action returns a result object that the servlet uses, along with other data, to decide which view to display next. When this controller receives a request containing the name createUser, it finds an instance of CreateUserAction in the hash map. It then invokes the Action's perform method, which uses the model to create a user (as shown in Code Example 4.7).
The code samples shown in this section are greatly simplified for clarity. The Web Application Framework used by the sample application provides a full, working example of this sort of controller called MainServlet. The servlet container dispatches requests with a servlet mapping: it forwards all URLs matching *.do to the MainServlet. Code Example 4.8 demonstrates how to provide an extensible framework for dispatching client requests.
The sample application improves the extensibility of the servlet code in Code Example 4.8 even further by externalizing the map of requests to actions. The controller in the sample application initializes the actions hash map from an external XML file, which contains pairs of operation names and corresponding Action class names. The controller servlet initializes the action map with the request names and action classes referred to in the XML file. The XML file is deployed as a resource in the Web application archive. Adding a new Action is as simple as adding a concrete Action subclass to the application archive and defining a configuration file mapping that associates the request URL with the action class. An example of such a mapping appears in Code Example 4.9. With no code modification, the sample application controller servlet can dispatch requests using actions that did not even exist when the controller was written.
Dispatching service requests to the application model is only half of the Web-tier controller's job. It is also responsible for determining the next view to display.
4.4.2.1.3 Controlling Dynamic Screen Flow
The succession of views that a Web application user sees is called screen flow. A Web-tier controller controls screen flow by selecting the next view a user sees. In static Web sites, screens (usually Web pages) are statically linked to one another. By contrast, a controller dynamically chooses the "next" screen in response to both user actions and model operation results.
In this section, the term "view" means a Web resource with a URL from which Web content is available. A view might be a JSP page, a servlet, static content, or some combination of the three, assembled into a page. Typically, the "next" view to display depends on one or more of:
• The current view
• The results of any operation on the application model, returned by modelmethod invocations
• Possibly other server-side state, kept in PageContext, ServletRequest, HttpSession, and ServletContext.
For example, the next view to display after a sign on view very likely depends on:
• The current view
• The user id and password contained in the request
• The success or failure of the sign on operation, and
• Possibly other server-side state. Examples of such state might include a maximum number of allowed users (application scope), or the URL the user was trying to access (request scope). See Section 4.4.7 on page 116 for a description of state and its scope.
The controller uses this data to determine which view to display next. A Web controller "displays a view" by forwarding the request to a JSP page, servlet, or other component that renders the view in a format compatible with the client; for example, returning HTML to a browser.
The controller in the sample application uses two components to select and generate views: a screen flow manager, which selects the next view to display; and a templating service, which actually generates the view content. The controller uses the screen flow manager to select a view, and forwards the request to the templating service, which assembles and delivers a view to the client. Both the screen flow manager and the templating servlet are generic components that are usable in any Web application. The component-based design reduces component coupling, promoting code reuse and simplifying the controller design.
Figure 4.3 Web-Tier Controller OID
Figure 4.3 is an object interaction diagram that shows the Web-tier controller interacting with other Web-tier classes. The diagram shows the following sequence of calls:
1. The controller receives a POST from the client.
2. The controller creates an Action corresponding to the requested operation (as described in the previous section).
3. The controller calls the Action's perform method.
4. perform calls a model business method.
5. The controller calls the screen flow manager to select the next view to display.
6. The screen flow manager determines the next view and returns its name to the controller.
7. The controller forwards the request to the templating service, which assembles and delivers the selected view to the client.
Most request URLs map to a specific view URL. For example, the screen flow map can define that the view signoff.screen always follows request URL /signoff.do. Sometimes the next screen to display depends on model operation results, server-side state, or user activity. For example, the next view following the request URL /signin.do, which signs a user into the system, depends on whether the sign in operation succeeded.
In the sample application, an application assembler configures the screen flow manager with an XML-based file called a screen flow map. The screen flow map defines a next view name for each request URL. For dynamic view selection, a screen flow map can also map a request URL to a flow handler, which is a Java class that selects the next view programmatically. Flow handlers are typically written by component providers or application assemblers.
4.4.2.1.4 Example
The Web Application Framework screen flow map mappings.xml configures the screen flow manager. Sample application Code Example 4.9 shows a URL action mapping that uses a flow handler to determine the next view in code.
com.sun.j2ee.blueprints.petstore.controller.web.actions.SignOffHTMLAction
Code Example 4.9 Excerpt from the Sample Application Screen Flow Map
In Code Example 4.9, the url-mapping element defines a mapping for request URL /signoff.do. The action element declares an action of class Signoff-HTMLAction, which performs the business logic for this URL (signing off a user). An application assembler or a component provider wrote the action class SignoffHTMLAction to sign a user off of the application. The screen attribute tells the screen flow manager to display screen signoff.screen after the action completes.
Figure 4.4 shows the result of an HTTP POST to the URL /signoff.do.
Figure 4.4 OID of POST to Flow Handler Defined in Code Example 4.9
The servlet container deployment descriptor has a servlet mapping from pattern *.do to the controller, so when a client POSTs a request to /verifysignin.do, the following actions occur:
1. The servlet container routes the request to the controller.
2. The controller creates an instance class SignoffHTMLAction and passes the request to it.
3. The controller calls the SignoffHTMLAction's perform method.
4. The SignoffHTMLAction object calls the model method that signs the user out of the application.
5. The controller asks the screen flow manager for the next view.
6. The controller forwards the request to the URL signoff.screen.
7. The templating servlet generates and delivers the requested view (a templated JSP page) to the client, so the user receives a view indicating that signoff succeeded.
The last piece of the puzzle not yet explained is how to map a view name in the design just described to an actual Web component (JSP page, servlet, and so on). Views and templating are discussed in Section 4.4.3 on page 110.
4.4.2.2 Serving Multiple Client Types
Web applications may support only one type of client with a single protocol, or multiple clients with different protocols, security policies, presentation logic, and workflows. Web clients may include several versions of a few different browsers, MIDP clients, so-called "rich" clients with stand-alone APIs, and Web service interfaces. Long-lived applications may need to be able to handle new types of Web clients.
Each type of client needs its own controller, which specializes in the protocols for that client type. A particular type of client may also need different presentation components for form factor or other reasons.
Following are some options for how to service requests from clients that use different application-level protocols. (Web-tier clients use HTTP for transport.) Each of the following alternatives expands upon Figure 4.2 by adding flexibility and increasing complexity.
Figure 4.5 Using a Front Controller to Handle Browser Interaction
Applications with a single client type can implement a single front controller. For example, a browser-only application is shown in Figure 4.5. Its single Front Controller servlet receives HTTP requests from the browser, translates the contents of these requests into operations on the application model, and serves views of result data as HTML (or XML). Additional controllers can support new client types, as shown in Figure 4.6.
Figure 4.6 Supporting Multiple Client Types with Multiple Controllers
The multiple-controller approach in Figure 4.6 provides extensibility for any future Web client types, including those that do not yet exist. In fact, because servlets aren't limited to HTTP, this architecture can support even non-Web clients. Each controller can implement the workflow, presentation logic, and security constraints unique to its client type. Notice also that the code implementing the application model is shared by all of the controllers. This separation of model and controller ensures identical application behavior across client types and eases maintenance and testing.
Some application functionality, particularly security, can be easier to manage from a single point. Introducing a protocol router, as shown in Figure 4.7, can provide a single point of control for all Web clients, each of which still retain their own controllers.
Figure 4.7 Using a Protocol Router for Centralized Control
The protocol router in Figure 4.7 is either a servlet or servlet filter that determines the client type and dispatches the request to the appropriate controller. The router typically uses the HTTP header User-agent to determine what sort of client is requesting service. The protocol router can implement application-wide functionality such as security or logging. The client-specific controllers can implement behavior specific to each client's particular protocol.
The Front Controllers in Figure 4.7 may or may not be servlets. If the Front Controllers are servlets, the protocol router dispatches requests to them using RequestDispatcher.forward. If the protocol router is a servlet, the Front Controllers can be a layer of simple objects to which the router delegates request processing.
Note that the controller alternatives shown in the last few figures can be implemented incrementally. Each of the approaches can be built on the preceding one. The BluePrints recommendation is to adopt and adapt the alternative that most closely matches current application requirements, and add new functionality as necessary.
Templating is an application of the Composite View design pattern, discussed in Chapter 11, which builds a page from a number of independent view components.
4.4.3 Web-Tier MVC View Design
MVC views display data produced by the MVC model. View components (also known as "presentation components") in the Web tier are usually JSP pages and servlets, along with such static resources as HTML pages, PDF files, graphics, and so on. JSP pages are best used for generating text-based content, often HTML or XML. Servlets are most appropriate for generating binary content or content with variable structure. (For an in-depth explanation of appropriate technology usage, see Section 4.2.6.1 on page 82 and Section 4.2.6.4 on page 86.)
HTML browsers are very lightweight clients, so the Web tier generates and often styles dynamic content for browsers. Heavyweight clients can implement relatively more view functionality in the Client tier, and less in the Web tier. Such clients include stand-alone rich clients, application clients, and clients that use special content formats such as MacroMedia Flash or Adobe Portable Document Format (PDF).
Web-tier components are not limited to serving HTML-over-HTTP Web browsers. The Web tier may also serve MIDP clients using proprietary protocols, rich clients using XML, or Web service peers requesting services with Electronic Business XML (ebXML) or Simple Object Access Protocol (SOAP) messages. Each of these examples uses a different application-level protocol, while using HTTP for transport. A properly designed Web tier unifies access to application functionality for any client type. The Web tier also provides virtual session management for some client types.
See Chapter 3 for more on J2EE client technologies.
4.4.3.1 Templating
One typical application requirement is that application views have a common layout. A template is a presentation component that composes separate subviews into a page with a specific layout. Each subview, such as a banner, a navigation bar, or document body content, is a separate component. Views that share a template have the same layout, because the template controls the layout.
For example, Figure 4.8 shows the layout of a single page created by a template. Across the top of the page is a banner, on the left is a navigation menu, a footer appears at the bottom, and the body content occupies the remaining space.
Figure 4.8 A Template Composes Other Views into a Consistent Layout
Using templates in an application design centralizes control of the overall layout of pages in the application, easing maintenance. Changing the layout in the template file changes the page layout for the entire application. More importantly, the individual subviews (like the "Navigation Menu" in Figure 4.8) are used by reference in the template instead of by copy-and-paste. Therefore, changing a subview means changing a single source file instead of changing all the files in which that subview occurs.
Template implementation is most easily explained by example. In the sample application, a JSP page called a template file specifies the page layout. The template file is a standard JSP page that uses custom tags to include subviews into each area of the page. The template references the individual subviews by name.
Code Example 4.10 is an example from the sample application that produces the layout shown in Figure 4.8. This file, called template.jsp, is a JSP page that produces HTML. The file specifies the page layout as standard HTML tags, and includes the content of other JSP pages using the custom tag insert, shown underlined in the code example.
<%@ taglib uri="/WEB-INF/tlds/taglib.tld" prefix="template" %>