JaVa
   

Web app Frameworks

As the MVC Front Controller pattern is almost universally advocated, there are several implementations available.

Note 

Although it's by far the most popular, MVC isn't the only way to structure web apps. Some products, such as the Tapestry framework (see the overview at http://tapestry.sf.net/doc/api/overview-summary.html), take a different approach to solving the problems that the MVC patterns addresses. However, none has been widely adopted, so I won't discuss them here.

Although it's not particularly difficult to implement a controller framework, you're unlikely to have any reason to. Let's move from pattern theory to practice, looking first at what real frameworks do and then at some implementations.

Common Concepts

Most MVC web app frameworks use the following concepts:

Many frameworks add the role of command (discussed above) to those of model, view, and controller. However, these four roles are not necessarily played by separate objects. In Struts and the app framework used for the sample app, the four roles are usually separate, with controllers reused throughout the app lifecycle. In Web Work and Maverick, command, controller, and model are normally the same object, created to handle a single request.

Important 

Choosing a web app framework is a second order issue, compared with committing to an MVC approach: even a poor MVC implementation will do a much better job of separating presentation and business logic than a clever JSP Model 1 implementation. Alternative frameworks share so many common concepts that, having learned one, it's fairly easy to work with others.

Remember that the web tier should be thin. The code that uses a framework shouldn't be complex. If much app code depends on a particular framework, the framework is poorly designed or is being used incorrectly.

Available Frameworks

In this section, I'll briefly survey some leading MVC frameworks.

Struts

The most widely adopted MVC framework is the open source Apache Struts (http://jakarta.apache.org/struts/). Struts was originally written by Craig McClanahan, the main developer of the Tomcat servlet engine, and was released in mid 2000, making it the longest-established open source web app framework. Partly because of its relatively long history, Struts has achieved remarkable buy-in by developers, with many add-ons now available around it. At the time of writing, Struts version 1.1 is in late beta. Hence I'll discuss the capabilities of both version 1.0 and 1.1, but concentrate on 1.1, which contains significant enhancements and should be used in any new projects using Struts. There are several tutorials and many online articles devoted to Struts, for example Java Web Development With Struts from Manning () so I won't describe it in detail here, but will merely survey how it approaches the concepts we've discussed. Struts uses a single controller servlet for a whole web app or subset of a web app. Struts 1.0 only allows one controller servlet per app - a serious limitation as it means that the single controller's configuration file will become unmanageably large. Struts 1.1 allows multiple controllers, each with its own configuration file. A Struts controller is of the concrete framework class org.apache.struts.action.ActionServlet. The standard controller servlet is responsible for handling all incoming requests, and is configured by the /WEB-INF/struts-config.xml file. (As I noted above, many frameworks use XML for their configuration.) As in most frameworks, the controller servlet is generic. It doesn't contain app-specific code, and it isn't usually necessary to subclass it (at least in Struts 1.1; subclassing was often necessary in Struts 1.0). The controller uses its mapping information to choose an action that can handle each request. A Struts action extends the org.apache.struts.action.Action class. The most important method is the following, invoked on each request:

 public ActionForward execute (ActionMapping mapping,
 ActionForm form,
 HttpServletRequest request,
 HttpServletResponse response)
 throws Exception;


Note 

This method was named perform() in Struts 1.0; perform() is deprecated as of Struts 1.1.

A Struts action must be threadsafe: as actions essentially extend the functionality of the controller servlet, the threading issues are exactly the same as in coding a servlet. Struts documentation advises that actions shouldn't include business logic but "should invoke another object, usually a JavaBean, to perform the actual business logic. This lets the action focus on error handling and control flow, rather than business logic." However, Struts does not provide infrastructure for managing business objects and exposing them to web-tier actions. The two Struts-specific parameters to the action execute() method (the ActionMapping and ActionForm parameters) convey the core Struts functionality, so let's look at them in more detail. The ActionMapping parameter contains information about the mapping from the request to the ActionForm instance: most importantly, the definition of "forwards" which enables the ActionForm to return a named view at run time. The ActionForm parameter is a bean that extends the org.apache.struts.action.ActionForm class to expose bean properties corresponding to request parameters. ActionForm beans are automatically populated by Struts from request parameters. Struts uses the Apache Commons beans manipulation package, which has similar goals to the com.interface21.beans package I discussed in the last chapter. However, the Commons bean package is less sophisticated and, in particular, lacks good error handling. Struts ActionForms have several peculiarities that we must consider. As all ActionForms must extend the Struts superclass, they all depend on Struts and the Servlet API. This means that they can't be passed to business objects, as business objects shouldn't depend on a particular web app framework or the Servlet API. Secondly, any request parameters that may contain invalid data (such as numeric inputs, for which the user might enter non-numeric data) must be matched by ActionForm properties of type String. The bean manipulation code behind the scenes will attempt to convert string request parameters to the appropriate type, but any exceptions in attempting to set properties will result in the org.apache.struts.util.RequestUtils class failing the request by throwing a ServletException. Thus a Struts ActionForm is not a true domain object. It's a place to hold user data until it can be validated and transferred into domain objects such as commands. The advantage of this approach is that invalid data can be re-displayed easily; the disadvantage is that we will need to get this data into true domain objects at some point, so Struts has only taken us part of the way towards true data binding. The need to derive appForms from a single superclass has always seemed to me a design flaw. Not only does it tie commands to the Struts framework and Servlet API, it incorrectly exposes inherited framework properties to update via data binding from request parameters. For example, adding a servlet parameter with a string value will break just about any page generated by a Struts 1.0 app (with a failure to set a property of type ActionServlet to a string). Struts 1.1 introduces a workaround for this particular problem, but the root of the problem is the whole ActionForm concept. The ActionForm class also defines a validate() method with the following signature:

 public ActionErrors validate (ActionMapping mapping,
 HttpServletRequest request);


Subclasses may override this method to validate the state of an action form after population from request parameters. Struts also offers alternative approaches to validation. We'll discuss validation in general, and the Struts approach to validation, in detail later in this chapter.

Important 

In the Struts model a standard controller servlet (the Struts ActionServlet) delegates to a number of Action objects. Each Action object is given as a parameter an ActionForm object, which contains JavaBean properties pre-populated from the request using reflection. As ActionForm objects are tied the Struts framework, they cannot be used as commands in an app. Each Action object is responsible for initiating the business operations required to handle the request. After processing is complete, each action should return a mapped view or "action forward".

Struts also comes with several JSP tag libraries to handle data binding and other operations. Note that some of these tag libraries are superseded by the new JSP Standard Tag Library (JSTL), discussed in the next chapter. The key Struts tag libraries are:

Note 

Note that all Struts tags enable access to nested properties. For example, the property path spouse.age passed to a tag will cause an evaluation of getSpouse().getAge(). The JSTL Expression Language, discussed in the next chapter, removes the need for such support in web app frameworks.

There are also various add-on tag libraries available, although not all the tags depend on the Struts framework itself. The availability of a range of associated tag libraries is a major reason for Struts' success. Despite its popularity, I'm not a big fan of Struts. It's good enough, but far from an ideal J2EE web app framework:

Struts 1.1 corrects many (but not all) of the shortcomings of Struts 1.0: for example, by allowing the use of multiple controller servlets in an app. (The mechanism for this isn't very elegant, however - it's clear that it was an afterthought.) Other enhancements in Struts 1.1 include the introduction of the org.apache.struts.actions.DispatchAction superclass, allowing several actions to be performed by the same class. This is very useful in cases where many request types call for simple handling; it avoids the proliferation of many trivial action classes. We'll discuss this concept further below. Struts 1.1 also introduces declarative exception handling: another concept discussed later.

Maverick

Another open source framework is Maverick (http://mav.sf.net), which concentrates on delivering MVC workflow, and doesn't provide presentation support such as tag libraries (the name is a play on MVC). One of the two main Maverick developers, Jeff Schnitzer, is also the author of the JUnitEE test framework, which we discussed in . Maverick is presently in version 2.1, which is discussed here. Version 2.0 was a major version of the original model. Maverick version 1.0 was released in mid 2001. Like Struts and most other frameworks, Maverick uses a single controller servlet as the entry point. A Maverick controller servlet is of class org.infohazard.maverick.Dispatcher. All app URLs are mapped onto this servlet in web.xml. The controller servlet takes its configuration from the Maverick-specific /WEB-INF/maverick.xml config file. Maverick currently allows only a single controller servlet - and hence, configuration file - per web app. As with Struts 1.0, this can make configuration hard to manage in large apps.

Note 

It's difficult to provide a brief overview of Maverick, as Maverick is highly configurable (much more so than Struts). The basic workflow can be customized by the user, and Maverick provides several standard classes that web-tier app classes can extend to provide different workflows. The following discussion describes the model offered by Maverick 1.0, and which still appears to be advocated in Maverick 2.0, judging by its use in the "FriendBook" sample app. This model is based on the use of "throwaway" controllers.

Maverick differs from Struts in that a Maverick request controller is usually a JavaBean. In this model, a new "throwaway" controller instance is created to handle each request. (However, Maverick 2.x also supports reusable controllers, as used by Struts.) In this model, Maverick does not separate between controller (analogous to a Struts action) and command (analogous to a Struts ActionForm). This has the advantage that each new controller doesn't need to be threadsafe - it doesn't need to support concurrent invocation. However, it creates a proliferation of controller instances. A request is handled as follows:

When its processing is complete, a controller must set the model object to be displayed by views by invoking the setModel() method on the ControllerContext object. Thus the model is restricted to a single object. To return multiple objects, it's necessary to define a container object that includes all necessary data. The org.infohazard.maverick.ctl.ThrowawayBean2 superclass returns itself as the model, combining two more of the four object roles we discussed earlier. Each named view must implement the org.infohazard.maverick.flow.View interface, which provides a view technology-agnostic way of passing model data to any view in a consistent way. View resolution is handled by the org.infohazard.maverick.flow.Command class, which invokes controllers. The View interface contains the following single method, which must be implemented to render output given model data:

 public void go (ViewContext vctx) throws ServletException, IOException;


The ViewContext interface exposes model data via the getModel() method (which returns the model object exposed via the controller), and exposes request and response objects. Standard implementations of the View interface provided with Maverick include an implementation that adds the model to the request as an attribute before forwarding to a JSP, and a redirect view that passes model data (converted to string form) to an external URL in a GET query string. There is also support for the WebMacro and Velocity template engines. As the view interface is so simple, custom views can easily be implemented. Maverick's approach to view substitution is simple and elegant, and I've borrowed the concept in my framework, discussed below. The explicit separation of the model promotes good practice: it discourages the haphazard setting of request attributes to provide a messy, JSP-only model. Probably Maverick's most interesting concept is the transparent "domification" or conversion of JavaBean data to XML on the fly to support the use of XSLT stylesheets. (Jeff Schnitzer is a vigorous advocate of XSLT, rather than JSP, views). Maverick's domification library is now a separate open source offering. We've already discussed the concept of domification in ; this approach is surprisingly effective in practice. Maverick also supports a configurable "pipeline" of transformations, which can be useful with XSLT but is less relevant to other view technologies. Another interesting feature is the use of simple basic dispatcher functionality, on which different workflows can be grafted by extending a variety of superclasses provided with the framework. This allows Maverick to support a wide range of idioms without the need to subclass the controller servlet. On the negative side, the lack of separation between command and controller in the "throwaway controller" model is a different approach leading to the same problem that Struts action forms encounter: the command is dependent on the Servlet and Maverick APIs. This means that it's difficult to achieve a clean separation between web tier and business interface. For example, we can't validate command data without depending on the Maverick API, making it hard to implement validation that isn't web-specific. The creation of a new controller instance to handle each request also makes it hard to parameterize controllers; a single reusable object can expose as many configuration properties as required to be set once only.

Important 

Maverick is a capable alternative to Struts. Maverick is typically used to create a new controller to handle each request (although Maverick supports other models). Controllers are JavaBeans, and the framework transparently sets bean properties behind the scenes. Maverick is relatively easy to use (although the Maverick 2.x model is much more complex than Maverick 1.0), and has built-in support for transparent generation of XML nodes from JavaBean model.

WebWork

A more recent framework is WebWork, another open source offering, designed by distinguished J2EE developer Rickard Oberg, who has contributed to JBoss and other projects. WebWork 1.0 (the version discussed here) was released in mid 2002 (http://opensymphony.com/webwork/) WebWork, despite its name, isn't purely a web app framework. It adopts a clever approach that minimizes the dependency of app code on web concepts. WebWork is based on the Command design pattern. Unlike a Struts action, which is a reusable, threadsafe object analogous to a servlet, a WebWork action is a command, created to handle a specific request (the same basic approach as in Maverick). An action is a JavaBean, enabling its properties to be set automatically. A WebWork action must implement the webwork.action.Action interface, which contains the following method:

 public String execute() throws Exception;


The implicit inputs are the state of the action's bean properties. The return value is a string representing the result - usually the name of the view that should be used to render output. The data outputs (the model) are the state of action bean properties after the execute() method has been called. Thus WebWork combines the role of command, controller, and model in each action object. Control flow works as follows:

Like Struts, WebWork comes with its own JSP tag libraries. However, it's less closely tied to JSP than Struts. There's also support for the Velocity template engine (discussed in the next chapter), and WebWork offers an equivalent to Maverick's XML domification functionality. WebWork is more elegantly designed and implemented than Struts. Its workflow is more similar to that of Maverick. The ActionContext idea is original and clever. WebWork helps to minimize dependence of app code on the Servlet API and provides more incentive towards good design. However, its negatives include:

Important 

The WebWork framework is based on the Command design pattern, resulting in a basic model more similar to that of Maverick than Struts. WebWork tries to model user actions as interface-agnostic commands, without dependency on the Servlet API.

JaVa
   
Comments