by Neal Ford

Art of Java Web development: WebWork

news
Mar 29, 200416 mins

Build Model 2 applications with WebWork

WebWork is an open source Web development framework for building Model 2 applications. Philosophically, it is closer to Struts than Tapestry. It works within the existing Web APIs in Java rather than attempting to replace them completely. WebWork contains several concepts and constructs that are unique and that set it apart from other frameworks, such as the Pull Hierarchical Model-View-Controller design and the value stack, which supplies values to their custom JSP (Java Server Pages) tags. We begin with a history and background of the framework. We then explore some key concepts and constructs that are vital when you’re working with the framework. As always, the principles of Model 2 development provide the guidelines for the samples and dictate a firm separation of concerns.

Overview

WebWork is produced by the Open Symphony project. This project includes many embedded projects; WebWork is just one of them. The framework is currently at version 1.3, and you can download it from the Open Symphony Website. It is based on best practices and design patterns that have long-standing records of accomplishment-patterns such as Model-View-Controller, the J2EE Front Controller, and others. It is also based on a strong motivation to keep things as simple as possible while maintaining flexibility (which the creators acknowledge is a difficult balancing act).

WebWork implements what its documentation calls “Pull Hierarchical Model-View-Controller,” or “Pull HMVC.” This is the creators’ own take on the Model 2 design. The “pull” part of this definition indicates that the view component is responsible for pulling the model information from the controller on demand. This is different from the traditional Model 2, where the view accesses information that has been placed within the model and passed to it from the controller. In this case, the view understands what information it wants and accesses it without necessarily having to wait for a controller to make it available. This architecture requires the presence of a repository of data available to all views, which access it on a just-in-time (JIT) basis.

The “hierarchical” part of the description describes the repository of view data. In the case of WebWork, the “value stack” is used to provide information to the view. (We describe this construct in the “Expression Language” section below.) The rest of the architecture is Model-View-Controller. That means that WebWork enforces the normal semantics of Model 2, but with a different twist on how that model information is made available. WebWork is not the only project to use this approach. Turbine’s documentation and white papers also refer to this “pull” paradigm of Model 2.

WebWork takes advantage of the Web APIs in Java rather than hiding them. However, it doesn’t rely on them as much as unadorned Model 2 or Struts. Like Struts, WebWork includes a central controller, based on the J2EE Front Controller design pattern, which creates Action objects (thus using the Command design pattern). However, the return and packaging of model information is different in WebWork.

The architecture

The architecture of WebWork follows the common architecture of most Model 2 Web application frameworks. The figure below shows the overall architecture and flow.

You can configure WebWork through configuration files. When WebWork starts, the ServletDispatcher is invoked. This is the main controller for WebWork, similar to other Model 2 frameworks. The ServletDispatcher reads several configuration files (covered in “The Configuration” section below) and uses that information to create an Action object to handle the request. The action in turn creates model beans, which access data, mutate it, and otherwise perform the real work of the application. WebWork does not require the use of model beans. Properties can appear directly in the Action class but shouldn’t for most Model 2 applications because such an arrangement violates the separation of responsibilities.

The action returns a response code, used by the ServletDispatcher to look up the appropriate view. The ServletDispatcher dispatches control to the view component, which can be any view framework (for example, JSP or Velocity), or it can be an action chain, which will execute another action. The view extracts information needed from the value stack. The framework maintains this construct by caching information from both actions and model beans for use by the view. The value stack is discussed in “The Expression Language” section.

On the surface, WebWork looks just like the architectural diagram for Struts. However, the details of information flow are different. WebWork makes accessing information kept in actions and model objects easier by handling much of the information transport for you. WebWork includes numerous custom JSP tags that are savvy about the information flow within the framework.

Validation is also handled differently in WebWork. It allows you to create metadata classes around standard HTML controls to handle formatting, validation, and other facilities. In this regard, the HTML elements become much more like desktop user interface (UI) widgets.

The configuration

Configurability is one of the hallmarks of WebWork. The framework includes a default properties file and allows you to override these settings in additional application-specific properties files. WebWork uses the properties files shown in the table below to configure the application.

WebWork properties files

Properties FilePurpose
webworkThis is the primary configuration file for the framework. It includes package names, UI settings, and entries that point to the other configuration files.
defaultThis is the default properties file for WebWork. The framework reads all these values, then selectively overrides them from the webwork properties file.
viewsThis file contains all mappings for visible resources in the application. It includes mappings for actions and UI elements. The actions return values that reside in this file point to a destination resource.
log4jThis file is used to set up logging for WebWork. WebWork uses the Commons logging framework, whose default logging implementation is log4j. This properties file is a standard log4j file.
Action-specificInternationalization is handled through properties files mapped to actions. Each action can have a properties file that defines the visual elements exposed to that action’s view.

Each of these properties files is loaded from the classpath. You can place them anywhere on the classpath (including the classes directory under WEB-INF in the Web application). One of the properties in both the default and webwork properties files is webwork.configuration.properties, which points to all the other properties files. If you want to store most of the properties files in another location, you can point to that location from this property in the main configuration file. The framework looks for and tries to load both the default and webwork files upon startup. All the other files can load based on property settings in one of the two main files. Developers sometimes like to keep all configuration files together and away from the output classpath, so WebWork facilitates that design via its property-loading mechanism.

Some of these files may also be XML files. In particular, WebWork includes a document type definition (DTD) to use with an XML document for view mappings. If you prefer to keep view information in XML, WebWork can support that. The webwork.configuration.xml property in the webwork properties file allows you to specify an XML view file instead of the standard properties file.

To make it easier for you to set up the directory and configuration file structure WebWork expects, the framework install includes a “skeleton” project named skeleton-project.zip that resides in the /etc directory. This zip file includes a reasonable directory structure, libraries, an Ant build file, templates, and configuration files. This is an excellent starting point for building a WebWork project rather than setting it up on your own.

Key concepts

Like all nontrivial frameworks, WebWork contains some key concepts that you must understand before you can leverage the framework to its best potential. WebWork includes Action classes, which are an implementation of the Command design pattern; the ServletDispatcher, which is modeled after the Front Controller design pattern; and an innovative data structure called the value stack, which makes it easy to pass information from the controller to the view.

Actions

Like most frameworks that use the Command design pattern to create a central controller servlet, WebWork includes a base class for all actions. This class, named ActionSupport, is a convenience class that implements the ActionInterface. It provides access to result view mapping, error handling, and internationalization. The Action interface includes some handy mappings (implemented as constants) to standard views that frequently reside in the view configuration document (for example, the SUCCESS constant).

In WebWork, the developer creates action subclasses that handle the individual page code for the application. The actions reside in a package or packages specified in the webwork.properties file and map to a particular extension (the default is .action). The names of the actions themselves appear in the views.properties file, which maps the action back to the action package. To invoke an action, the user types a URL that includes the Web application name, followed by the mapping name of your action with the .action suffix. The suffix matches a URL pattern in the web.xml file, pointing it to the WebWork dispatch servlet. Once that servlet has the action, it follows the normal framework flow of events. This structure is similar to Struts, particularly the way the controller servlet acts, but with additional configuration files.

One key difference from other frameworks lies in the fact that WebWork actions are not coupled to the servlet API. Actions are essentially just JavaBeans, with no intrinsic knowledge of constructs like HttpServletRequest. The actions can be made aware of these objects via interfaces, but the action itself knows nothing about what context it is running within. This makes WebWork actions more loosely coupled than Struts actions. In fact, WebWork documentation warns you when you try to use the interfaces to couple your application to the Web API. While it is unlikely you would write a WebWork application that isn’t a Web application, a more loosely coupled application has greater flexibility.

Key interfaces

WebWork attaches behavior to actions and other classes via the use of interfaces. For example, if you need one of your actions to interact with HttpServletRequest, your action must implement the ServletRequestAware interface. This interface includes only a single method (setServletRequest()), which is called by the framework on any action that implements the interface. Your action can include a private request member variable that is set through this method. In WebWork 1.3, this interface has been replaced with a call to the static method ServletActionContext.getRequest().

An interesting variant on this theme is the SessionAware interface. It provides access to this user’s session. Like ServletRequestAware, it includes a single set method (setSession()). However, the type passed as the user’s session is a java.util.Map, not HttpSession. WebWork maintains the session information for you, keeping it in a Map instead of HttpSession. This means that you can support session tracking without being tied to the servlet API. In WebWork 1.3, this interface has been replaced with a call to the static method ActionContext.getContext().

Most of the behavior that isn’t intrinsic to the framework is attached to individual classes using interfaces. The interfaces are very cohesive, generally offering a single method. This provides an elegant means of enabling behavior without overburdening the framework with unneeded functionality.

The value stack

One of the built-in facilities of the framework is the value stack. This is a utility class that supports the access of information from the view. Semantically, it is a stack of values, implicitly placed there by actions in the course of execution. The WebWork expression language (covered next) uses the value stack heavily.

Regardless of the implementation, the value stack makes writing view elements (i.e., JSP) much easier than other frameworks do. For example, consider the use of localized properties for holding the label text for the fields on a page. As the page is loaded, the framework calls the getText() method on your action, passing the key, which looks up the message body from the ResourceBundle. It then places that value on the value stack. For example, to populate the label for a textfield (one of WebWork’s UI components) with the value from the properties file, you can use the value stack method text:

<ww:textfield label="text('input.duration')"
   name="'duration'"
   value='<webwork:property value="duration"/>' />

As a developer, you can use the value stack as a JIT repository of data, provided by either your actions or by the framework itself. This primary underlying class supports the “pull” and “hierarchical” parts of “Pull HMVC.” Generally, it is invisible, providing information through its methods just as it is needed.

The stack in value stack has meaning. It is implemented as a stack, with elements needed for a particular page or component pushed on the stack and then popped off when they are no longer needed. For example, consider the common case of iterating over a list of values in a JSP. In WebWork, the iterator tag places the value at the top of the value stack, and the property tag pops the value off the stack. Behind the scenes, the property tag looks up the value attribute passed to it (which defaults to .) using the stack, then pushes it back:

<webwork:iterator value="columns">
   <th><webwork:property/></th>
</webwork:iterator>

The property tag inside the iterator doesn’t need any additional information about the property to access it. When you use the property tag with no attributes, it automatically pops the topmost value off the stack. In this case, the iterator pushes the current value of the columns collection (either a List, Array, or Map) on the stack, and the property tag pops the topmost value on the stack. Of course, the property tag also supports attributes for qualifying the property name, but they aren’t needed in this case.

The value stack is an innovative structure, and it is used heavily and naturally throughout WebWork. As a stack, it can readily supply JIT values on the top of the stack, ready to be popped off. The developer also has more direct access to the value stack if needed, testing for the presence of values and explicitly manipulating it via push and pop methods.

Expression language

WebWork’s expression language is designed to enhance the syntax of JSP to make it more programmable. It interacts with the value stack, possessing the ability to traverse the stack and flatten out objects to retrieve the desired data. It makes JSPs more readable by compressing the sometimes-verbose JSP syntax into terser, more expressive constructs. For example, it allows developers to use simpler notation when accessing a property from a JavaBean:

<webwork:property value="guessBean/numGuesses"/>

In this example, the getGuessBean() method of the underlying action is called, followed by a call to the getNumGuesses() method on the bean. The corresponding JSP syntax for this code would be:

<jsp:getProperty name="guessBean" property="numGuesses" />

The expression language also has special support for standard collections in the Web API. Consider this example:

<webwork:property value="@timer/total"/>

The @ symbol indicates that this object resides in page, request, or session scope. The expression language will find the most tightly scoped instance of this object, retrieve it from the scope, and call the getTotal() method on it.

The expression language may be used within any property of a WebWork custom taglib except the id property. The interaction of the expression language and the value stack create JSP pages that have much less “plumbing” code in them because the framework handles so much of it.

BeanInfo classes

WebWork borrows a convention from the JavaBeans API to add support for formatting and validation. The Action classes in WebWork are the primary active element. Each Action class can have a BeanInfo class associated with it to handle meta-property information (i.e., property information about properties). BeanInfo classes are part of the JavaBeans specification to supply meta-component information to development environments. For example, if you create a UI widget for a Swing application, you must provide support for a development environment to set and get properties. For complex properties, you can create property editors to assist the user in correctly setting the values of the properties. These editors aren’t included in the code for the widget itself because they have no purpose at runtime—they are design-time-only artifacts. The JavaBeans API uses the convention for a BeanInfo class to supply property editors and other design-time metadata for the component. If you create Widget.class, you can create WidgetBeanInfo.class and the development environment will automatically use the metadata class.

WebWork uses this mechanism to handle type conversions and validations for public properties of actions. The public properties (accessor and mutator pairs) for actions are made available via the value stack to the expression language elements on JSP pages. To handle validations, you can create BeanInfo classes for your actions and register editor classes for the properties. WebWork will automatically apply the editors for your fields to the input elements on the page.

Templates

WebWork by default is designed to create HTML-based Web applications. However, the UI element rendering is governed by a set of templates. The skeleton project contains JSP pages to handle the output for Cascading Style Sheets, “normal” HTML, and Extensible Hypertext Markup Language (XHTML). These templates are used when WebWork renders the controls for display. Configuration parameters allow you to specify a different format via a set of templates. For example, you could create a set of templates for XML generation, place them in your Web project, and tell WebWork to use them instead. None of the code in the application would need to change, but the output would become XML instead of HTML. The templates used by the framework are themselves WebWork pages and make heavy use of the expression language to determine property settings and internationalization options.

Summary

This article introduced you to OpenSymphony’s WebWork, a “Pull Hierarchical MVC” framework for building Web applications. We discussed the value stack, the expression language, and the custom properties. The interaction of these three elements is the primary distinguishing characteristic of this framework.

Neal Ford is the chief technology officer at the DSW Group in Atlanta. He is an architect, designer, and developer of applications, instructional materials, magazine articles, and video presentations. Neal is also the author of Developing with Delphi: Object-Oriented Techniques (Prentice Hall PTR, 1996) and JBuilder 3 Unleashed (SAMS Publishing, 1999). His language proficiencies include Java, C#/.Net, Ruby, Object Pascal, C++, and C. Neal’s primary consulting focus is the building of large-scale enterprise applications. He has taught on-site classes nationally and internationally to all phases of the military and many Fortune 500 companies. He is also an internationally acclaimed speaker, having spoken at numerous developers’ conferences worldwide.