MVC Concepts and the Front Controller J2EE Pattern

Let's look in more detail at what it means to adopt an MVC approach to web applications. In this section we'll consider theory and general principles. In the next section we'll look at some real MVC web frameworks that simplify the implementation of applications using an MVC approach.

Concepts

Let's start by looking at basic MVC concepts.

The MVC Triad

The MVC approach divides components into three kinds of object: model data objects; view objects that present model data on screen; and controller objects that react to user input, updating models appropriately.

In a true MVC architecture, such as that of Java's Swing GUI libraries, each view registers with a model, which publishes events when its data is updated. Consider the Swing JList component. This simple view component displays items in a list and allows item selection. The list data is defined in an object implementing the ListModel interface, which publishes ListDataEvents when the data changes. The JList view component provides a ListDataListener implementation that registers with the ListModel and updates the display as events are received. This is a push model: the model pushes notifications to any number of listeners, which are typically (but not necessarily) views.

As changes in a web application are usually only displayed to the user when a request is received and a new page is generated, the push model doesn't work. Hence a web application view will only render once. It won't help to register it to receive ongoing notifications. The solution is to use a pull model, in which a view is given access to the models required to render a dynamic page, and the view pulls data from the model as necessary. Since a web page may contain different controls and sections of dynamic data, more than one model may be required to back it.

Whether we use a push or pull model, the MVC architecture still delivers its key benefits. Each component type has a clear responsibility. Models contain no view-specific code. Views contain no control code or data-access code and concentrate on displaying data. Controllers create and update models; they do not depend on particular view implementations.

Although the MVC approach has the potential to improve web-tier code tremendously, it is a little complex to set up. Thus we will generally use a generic MVC web application framework, rather than implement the pattern ourselves.

Controller

As the controller is central to MVC in a web application, let's consider the controller first.

The key responsibilities of a web application controller are:

  • To examine and extract request parameters
    This may lead to the creation of command domain objects, which will be passed to business objects. An MVC framework usually provides infrastructure that simplifies working with request parameters: for example, by populating JavaBeans with parameter values.

  • To invoke business objects, passing the data extracted from the request
    Controllers will usually catch application exceptions resulting from business method invocations, modifying model creation, and view selection accordingly. (However, some fatal exceptions can simply be left to be handled by the web container, as web applications have a standard "error page" mechanism.)

  • To create the model that views will display based on the results of invoking business methods and any session state

  • In applications with server-side state, to create and manipulate session state

  • To choose a view and cause it to render, making the model data available to it

Other responsibilities may include generating web-tier log output and enforcing security restrictions (although security can also be handled declaratively in J2EE, as we saw in Chapter 6).

As these responsibilities are a mixture of application functionality (such as invoking application-specific business objects) and plumbing (such as passing model data to a named view and causing it to render content), they are usually split between application-specific and generic framework classes when using an MVC web application framework.

It's vital that controller objects have easy access to application business objects. Neither model nor view objects should require such access.

Model

A model contains data displayed by a view. In the J2EE web application, the model normally consists of JavaBeans. Many view technologies, including JSP, work best if models are JavaBeans. Models in web applications (as opposed to "true," traditional MVC) are usually dumb storage objects, such as value objects, that represent the result of a complete business operation. Once a controller completes its processing and selects a view to generate content, the model should contain all the data to display. The model itself should not perform any further data access, and it should not contact business objects. There is no reason for a model to depend on the Servlet API or a web application framework; JavaBeans used as part of a model need not be web-specific. They will often be domain objects, usable outside the web tier.

In the JSP paradigm, models are added to the request as attributes using the request.setAttribute() method. However, this isn't the only way to communicate a model to a view; not all views may find request attributes useful.

View

A view displays the information contained in a model, taking entire responsibility for markup or other content generation. A view needn't know anything about the implementation of the controller or the underlying business objects.

JSP is the most used view technology for J2EE applications, although, as we'll see in the next chapter, there are several valid alternatives. We've noted that JSP gives us the power to do things that are inappropriate in views. The following guidelines indicate what operations are appropriate in views:

  • A view should be wholly responsible for generating web content, given data models.

  • A view should not work directly with request parameters. A controller should intercept incoming requests, and provide the view with models.

  • A view should not perform data retrieval (for example, by running SQL queries). The view should merely display data, which will be exposed by one or more models. The Dispatcher View J2EE Pattern (Core J2EE Patterns) in which a JSP view retrieves data using a helper as it renders, is fundamentally broken, as it is vulnerable to unexpected errors during rendering. Core J2EE Patterns terms the controller-managed data retrieval described here the Service-to-Worker pattern. (No, I don't understand how they thought up these names, either.)

  • A view may need to perform display logic, as distinct from control logic or business logic. Examples of display logic include displaying the rows of a table in alternate colors, or displaying additional information on a generated page if the model contains optional data. Such logic does not belong in controllers, as it's unique to a particular presentation.

  • A view should not have to deal with data-retrieval failures. In practice, this means that properties and methods exposed by models should not throw exceptions; data retrieval should be complete before a view begins to render

Last, and most importantly:

Important 

It should be possible to replace any view in a web application with another that displays the same data without modifying model or controller code. A view should be thought of as meeting a contract: "Display this kind of data." It should be possible to substitute any view for another without affecting the working of the application. This is the key to ensuring that presentation can be updated without affecting workflow.

It should be possible even to replace a view with another that uses a different view technology without affecting controller or model code. For example, it should be possible to replace a JSP view with an XSLT view.

Many think that view substitutability is impossible - or prohibitively difficult - to achieve in practice. I believe otherwise, and in this chapter and the next chapter will demonstrate how it can be achieved, and the benefits it brings.

Of course, unless a view contains the correct links or form fields, it may be impossible to continue using the application. But this is a question of displaying available information, not control logic.

Giving view components such limited and clearly defined responsibilities is the best way to ensure the separation between the roles of Java developers and markup (presentation) developers that is essential on large web sites.

It also has great benefits for testing. If we know that our views merely pull data from a model supplied by a controller, we can change presentation by modifying views without any risk of breaking the behavior of our application. Testing can be limited to checking that the modified view contains no syntax errors, and displays the correct content.

Unit and regression tests will be written for controller components, to check that they initiate the correct business logic operations and expose the correct models. It's much easier to write unit tests and regression tests for Java classes than for JSP pages or other rendering technologies, so this works well. If, on the other hand, our "views" perform data retrieval, we will need to perform regression testing when we change presentation.

Control Flow

To illustrate these concepts in action, let's consider a possible flow of control in a simple MVC implementation. (Real MVC web application frameworks are a bit more refined, as we'll see.) Imagine that the request is to book several seats of a particular type for a performance of a show. Our sample application uses a more sophisticated workflow for this - the following is a stripped down example:

  1. A controller servlet receives a request. There is a mapping from the request URL to controller servlet in the application's web.xml deployment descriptor.

  2. The controller servlet examines the request parameters. The performanceID, seatTypeID, and count parameters are required. If any of these is missing or nonnumeric, the user should be sent back to the welcome view, welcome. jsp.

  3. With valid data from request parameters, the servlet invokes an allocateSeats() method on a business object that it obtains and stores as an instance variable on application startup.

  4. If the attempted booking succeeds, the servlet adds the returned array of Seat objects (the model) to the HttpServletRequest as an attribute and forwards the request to the view displayReservation.jsp. If there aren't enough seats, the servlet adds the exception containing this information to the HttpServletRequest and forwards the request to the view retryBooking.jsp.

This simplified workflow indicates how one type of request (usually a request to a particular URL) can lead to multiple views, and how views need only render content after data retrieval is complete.

The following diagram illustrates the workflow described in the text:

click to expand

Pattern Variants

There are many variants of front controller implementation. Let's consider some of them before we move on to discuss real MVC frameworks.

Template Selection Servlet

The simplest front controller approach is to use what I'll call a template selection servlet to handle incoming requests. Each template selection servlet will process request parameters, invoke the appropriate business objects and conclude by choosing a template and making model data available to it (pretty much the same workflow as in our simple walkthrough above).

As an illustration of this approach, let's look at a popular implementation. The WebMacro template solution, which we'll discuss in more detail in the next chapter, provides a convenient generic superclass, org.webmacro.servlet.WMServlet, for template selection servlets using WebMacro templates as views.

Application-specific subclasses of WMServlet need to implement the following method to process the request, initiate the necessary business operations, make model data available to WebMacro, and return the selected WebMacro Template to generate content. The request and response objects are available via the WebContext argument:

    public Template handle (WebContext context) throws HandlerException 

To use this approach, each request URL in our application must be mapped to a separate template servlet in web.xml. On the positive side, this means there's no need for a framework-specific approach to map request URLs to handlers. On the negative side, mapping in web.xml is verbose, and we'll need to define a new template servlet instance for each URL.

Note 

Note that we can apply this approach very easily using JSP as the templating technology; each servlet can simply use a RequestDispatcher object to dispatch to the appropriate JSP. There is no need for the kind of infrastructure provided by the WMServlet class.

This is a simple, effective approach, and a big advance on a servlet-only or JSP Model 1 strategy. However, it fails to deliver all of the goals we have set ourselves, and I don't recommend it. In particular:

  • Controllers (in this approach, implemented as template selection servlets) are dependent on a particular view technology. We're out of luck if we decide, for example, to change from WebMacro to XSLT or PDF views. This may prove a serious problem, for example, if we need to generate binary output.

  • We must work with request parameters directly. There's no overall framework providing a higher-level workflow than the Servlet API.

  • There's no application framework to help us achieve a thin web tier.

How Many Controller Servlets?

There is a debate among MVC proponents about how many controller servlets an application should have. Should we use one servlet for each type of request (as in the template selection servlet approach), or a single front controller that handles all requests to an application or subsystem? Core J2EE Patterns terms the latter approach the Multiplexed Resource Mapping Strategy. If we use a single front controller, it should be generic, and should forward requests to a number of sub-controllers.

Many of the arguments against using a single controller servlet are unsound - such as the idea that it introduces a single point of failure (threads can die, not objects); or that it means that the controller will be too large (we can use a generic controller with many application-specific delegates, rather than one huge, all-knowing controller). One valid criticism is the need to duplicate the standard mapping of URL to request-handling class. While we can map URLs directly onto servlets in web.xml, when we use one controller to front many URLs we will usually map all URLs onto the controller, which must then use its own mapping format to route URLs among its sub-controllers. However, this isn't a real problem in practice.

The advantages of using a single controller include that it ensures consistency of control flow - for example, ensuring that necessary resources are available to all sub-controllers. It also means that each sub-controller doesn't have to be a servlet; an important point as we can allow sub-controllers to implement a less complex interface.

It's natural to ask what the performance implications are of the additional overhead in funneling requests through a single controller. A typical controller servlet implementation will need to do hash table lookups based on the URL of each request. Some frameworks use reflection (although an efficient framework should cache the results of reflection). Fortunately, the performance impact is negligible. Pretty much anything we do once per request will have no measurable effect on the overall performance of a web application. In profiling applications running in MVC frameworks, I've consistently found the framework overhead to be insignificant - even undetectable.

All the real frameworks I know of use a single controller servlet for a whole application or a set of related use cases. The controller servlet then delegates work to helper classes that implement a framework-specific interface or extend a base class provided by the framework.

JSP or Servlet Controller?

Another key question is whether the controller should be a servlet or JSP. Core J2EE Patterns terms these the Servlet Front and JSP Front strategies respectively, and some Sun examples show the use of a JSP as a controller. (I was guilty of such a sample application myself in my first publication on JSP.)

The JSP Front approach is naÏve and misguided, and does not deserve to be dignified as a "strategy". JSP pages are poorly suited for use as controllers, for many reasons. For example, they are not first rate Java classes. (We can't subclass a JSP if we want to customize its behavior, for example.) They also don't foster code reuse.

The only argument in favor of using JSP controllers is that they're a little easier to implement, as we sidestep web.xml servlet mappings: a tiny saving that isn't worth compromising an architecture for.

Servlets are essential in J2EE web applications; JSP is one option for views. It's illogical to try to use JSP pages to perform a role that uses none of their strengths and displays many of their weaknesses, especially as Java classes are very good at this type of control flow.

Important 

Do not use JSP pages as controllers. The purpose of JSP is to generate markup, not handle control flow.

Should a Request Cause the Creation of a Command?

A common approach is for a request to result in the generation of a Command object: a business object containing data associated with the request, but not dependent on the Servlet API. This approach has the following advantages:

  • We can enjoy the advantages of the Command design pattern, such as the ability to queue, log, and possibly undo commands.

  • We may be able to use transparent data binding from request to command bean properties. This may free application code from the need to handle request parameters.

  • The command is not dependent on the Servlet API, so can be passed to business objects. This promotes clean interaction between servlets and business objects.

  • The command approach works well with the Observer design pattern; it's easy to publish events on receipt of commands.

The disadvantages are that a command approach may be overkill in simple cases. For example, if a request has no parameters associated with it, creating an "empty" command to represent it doesn't make sense.

Important 

A command should be generated on receipt of a request if data binding (discussed later in this chapter) will prove useful - for example, if there are many request parameters - or if there's a real business value in using the Command design pattern. In the latter case, the use of the Command design pattern should be driven by the relevant business interfaces; it is for business objects, not web interface, to determine the design patterns that will be used in intra-application communication.

Implementation Goals

Using a web application framework should simplify application code and promote clean interaction between web interface components and business objects.

A web application framework should allow the use of any view technology, without the need to modify controller code. For example, it should be possible to use XSLT stylesheets instead of JSP pages without changing any Java code.

Important 

There's a lot of fear about MVC. In particular, developers often think that MVC approaches add complexity, compared with JSP-centric solutions, and that MVC solutions run slower. The first fear is plain wrong; the second is irrelevant in real applications.

Using an MVC framework, the result should be simpler code that's easier to write and debug, in all but trivial applications. A good MVC framework should reduce the amount of code you need to write, and simplify application code. Even if you write your own framework (which you shouldn't need to), MVC frameworks aren't particularly complex to implement.

MVC applications do a little more work than JSP-centric solutions, but this is usually limited to a couple of hash table lookups, concealed within the framework, which won't produce any detectable change in application performance. Any successful MVC framework will be efficient.

Note 

Using an MVC framework may even improve performance, by giving us the opportunity to use view technologies that may perform better than JSP in a particular situation. In Chapter 15 we'll see that JSP isn't necessarily the fastest way to render content.



Expert One-on-One J2EE Design and Development
Microsoft Office PowerPoint 2007 On Demand
ISBN: B0085SG5O4
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net