WORK IN PROGRESS

Struts Use Cases

The Struts framework started out as an abstraction of a working application. The core framework did not evolve over time from its simplest state, but sprang fully formed from a 20-hour marathon programming session. The project gained critical momentum, but lost an evolutionary history.

Of course, hundreds of development hours have since gone into the framework. Dozens of developers have contributed code and documentation to the framework. Struts 1.1 is different in many, many ways from Struts 0.5. Still, learning Struts can be a difficult process for many developers. One reason is that it is not always easy to connect a feature with its use case. Until a developer appreciates the problems each feature solves, the pragmatic Struts architecture can seem obtuse.

This page, StrutsUseCases, is intended to re-introduce the framework, starting from the simplest useful state. These use cases are not an attempt to reconstruct the historic development of Struts. (Although that would be interesting!) Instead, with the full benefit of 20/20 hindsight, the progression of Use Cases is orchestrated to find the clearest path from a useful starting point to today's mature feature set.

Once the Use Cases are clearly defined, a mock application can be constructed that proves each case. Taken together, the use cases and the corresponding mock implementations will be a powerful way to teach Struts. Note that since the use cases start from the simplest first principles, some Struts hallmarks are introduced later on. These hallmarks include the singleton Action dispatcher classes and the custom tag libraries. Of course, this could change before the implementation phase, but I thought it might be interesting to imagine how much might be done with how little. Also note that these use cases are intended to be implementation neutral. They are based on the services provided by the Struts framework, but in final form should be applicable to any web application framework. For reference, existing Struts members that would be involved in the use case are given after each in dimmed text.


Provide a configuration service

In a layered application, restrictions are imposed upon which components can communicate directly. This makes it difficult for components to share essential implementation details. The framework must provide a common configuration service to be accessed by all components within an application (including presentation layer components). The service should be deployed at initialization and create a root configuration object.

The interface for the configuration service should rely on common types and avoid exposing types from specialized platforms, like HTTP or Swing.

For the best flexibility, implementation details should be loaded from an external file rather than a Java class, but components should be able to add to the configuration over the life of the application. If loaded from an external file, developers should be able to extend any configuration object and populate arbitrary properties on the object subclass.

{ActionServlet.init, struts-config.xml, Digester, Digester.setProperties}


Manage multiple configurations

Many components are developed independently and there can be naming conflicts if all application details are loaded into the same scope. The framework should be able to load multiple configurations and allow the components to work together without naming conflicts.

{config package}


Map URIs to commands

In a web application, all input arrives under a URI. Some URIs refer to files that the servlet container can deliver. Others can refer to servlets written by the application developer. The container invokes a servlet based on whether the URI matches a pattern registered for that servlet. In practice, the servlet URIs correspond to commands in the application's API. Many of these commands are closely related and can be handled by the same servlet or dispatched to the same helper object. The framework should provide a common service that allows a servlet to map URIs to API commands and efficiently call the method or object that realizes the command.

{ActionMapping.path,.parameter, DispatchAction}


Interpolate prefix or suffix

{This would be a reasonable place to justify the prefix and suffix features, but a practical use case eludes me.} {ActionMapping.prefix,.suffix}


Standard forward or include

Some commands may be temporarily or permanently delegated to other servlets or applications. The framework must be able to forward or include a request for an application servlet URI to an arbitrary resource.

{ActionMapping.forward,.include}


Unknown URI

In a web application, any URI can be requested by the client software. The web server has a mechanism for presenting a standard error page when resources are not found. (The standard 404 response.) If the URI matches a pattern registered to an application servlet, the framework should be able to intercept and process the request. This permits an individual application to redirect its URIs when appropriate or to present its own error page without reconfiguring the web server.

{ActionMapping.unknown}


Secure commands

Standard user authentification and authorization services are provided by the container based on a particular URI or URI pattern. When URIs are being mapped to commands, the framework should provide the same services at the command level. This allows developers to apply security protocols in terms of its own logical commands.

{ActionMapping.roles}


Populate transfer object from input

Input to a web application comes bundled in the request in a rarified format. The framework must convert the input into an extensible object that can be easily utilized by other objects in the application.

{ActionForm, BeanUtils}


Validate input

Input to a web application is string-based. The standard HTML controls do not validate input. Field that must contain numeric data can easily contain alphanumeric data. The framework must be able to perform its own validations on incoming data before accepting the transfer object for processing.

{ActionForm.validate}


Post error messages

When validations fail, the framework must be able to post messages explaining the cause of the failure. To permit localization, the messaging mechanism should utilize the standard Internationalization API.

{ActionForm.validate, ActionMessage(s), ActionError(s), MessageResources, application resources}


Pluggable Validations

There are a number of standard validations that recur through an application, including casting a String to another standard types (Integer, Float, et al). The framework should allow a standard validations to be specified for a incoming field. Developers should also be able to plug-in their own reusable validations. Ideally, the validations should have an optional JavaScript form for use with presentation pages.

{ActionForm.validate}


Route control

Some operations may have more than one outcome, most will at least succeed or fail. An operation must be able to route control based on a variety of outcome conditions. The conditions are expressed in logical terms, like "success" or "failure", and the framework must map this condition to the appropriate URI.

{ActionForwards,LookupDispatchAction}


Reset input properties

Some HTML form values, like checkboxes, must be reset to an initial value before populating a transfer object from the request. There are also many use cases that require standard environmental values be captured when a transfer object is populated. The framework must provide a pre-populate trigger that developers can use to reset or capture values.

{ActionForm.reset}


Map hyperlinks

The presentation layer of an application must use standard hyperlinks to expose the application's command set. The hyperlinks require a standard URI. Page authors should be able to code hyperlinks by referring to application commands rather than URIs. The framework must be able to map commands to URIs that can be inserted into a standard HTML hyperlink.

{ActionForwards, RequestUtils, html:link}


Document assembly

Servlets and JavaServer Pages can include the output of other servlets and pages when rendering a response. This allows page fragments to be reused throughout an application. Changing a single template can in turn update the output of hundreds of pages. The framework should provide a mechanism that helps developers deliver pages which are built from reusable fragments.

{tiles}


Dispatch to handlers

Many applications need to support a large API with hundreds of commands. Using standard servlet classes to handle a large command set can drain resources. The framework should support delegating processing of the commands to lightweight handlers that can act as the servlet's proxy.

{Actions}


Datasource management

Applications may need to access a variety of standard DataSource objects or may need to be configured to use different DataSource objects when moved from one server to another. The framework should provide a mechanism allowing multiple DataSources to be managed and reconfigured as needed.

{DataSources}


Route exceptions

One typical outcome of an operation will be that an exception is thrown. The framework must be able to route control to its own resources when an operation throws a given exception.

{ExceptionHandlers}


Submit-once forms

Many web forms should be input only once, but uses can press the submit button multiple times. The framework must provide a mechanism that can detect multiple submissions of the same form.

{Action.*Token; html tags}


Process MIME attachment to a request

A request from a web form may include a MIME attachment. The framework must facilitate handling of the MIME attachments when populating the data transfer object.

{file upload package}


... Other User Cases utilizing:

StrutsUseCases (last edited 2009-09-20 23:11:46 by localhost)