JaVa
   

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 apps. 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 apps 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 app 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 app 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 app framework, rather than implement the pattern ourselves.

Controller

As the controller is central to MVC in a web app, let's consider the controller first. The key responsibilities of a web app controller are:

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 ). As these responsibilities are a mixture of app functionality (such as invoking app-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 app-specific and generic framework classes when using an MVC web app framework.

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

Model

A model contains data displayed by a view. In the J2EE web app, the model normally consists of JavaBeans. Many view technologies, including JSP, work best if models are JavaBeans. Models in web apps (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 app 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 apps, 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:

Last, and most importantly:

Important 

It should be possible to replace any view in a web app 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 app. 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 app. 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 app. 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 app frameworks are a bit more refined, as we'll see.) Imagine that the request is to tutorial several seats of a particular type for a performance of a show. Our sample app 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 app'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 app startup.
  4. If the attempted tutorialing 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:

Java 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. app-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 app 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:

How Many Controller Servlets?

There is a debate among MVC proponents about how many controller servlets an app 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 app 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 app-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 app. In profiling apps 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 app 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 app 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 apps; 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:

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-app communication.

Implementation Goals

Using a web app framework should simplify app code and promote clean interaction between web interface components and business objects. A web app 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 apps.

Using an MVC framework, the result should be simpler code that's easier to write and debug, in all but trivial apps. A good MVC framework should reduce the amount of code you need to write, and simplify app code. Even if you write your own framework (which you shouldn't need to), MVC frameworks aren't particularly complex to implement. MVC apps 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 app 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 we'll see that JSP isn't necessarily the fastest way to render content.

JaVa
   
Comments