Struts Action Mappings - configuring web application URLs
In the article on Struts URLs for Perfectionists I described one way of defining the action mappings for a Struts web application. One of Struts' double-edged swords is that there are several ways to do this, each with its pros and cons. This article considers a few alternatives.
Individual action mappings in struts-config.xml
Approach: manually add an action element to struts-config.xml
for each Action mapping.
Pros: simplicity, and the most examples online.
Cons: effort to maintain struts-config.xml
.
<action path='/search' type='myapp.web.SearchAction' name='SearchForm'> <forward name='search' path='/WEB-INF/jsp/Search.jspx' redirect='false'/> </action>
This approach is most straightforward, and is a good way to start. However, if the number of actions continually grows it becomes a hassle to keep struts-config.xml
in sync.
One thing that certainly helps is a strict convention for Action class names based on the URL path, so you do not have look at the configuration each time you want to look at a URL's action, or vice versa.
It also helps to keep the action mappings sorted by path, to make it easier to browse and to avoid duplicate entries.
Use DispatchAction
to group related actions
Approach: combine related actions as separate methods in a DispatchAction
subclass
Pros: simplified struts-config.xml with fewer entries
Cons: unpredictable action method names
<action path='/customer' type='myapp.web.CustomerDispatchAction' name='SearchForm' parameter='method'> <forward name='view' path='/WEB-INF/jsp/CustomerView.jspx' redirect='false'/> <forward name='edit' path='/WEB-INF/jsp/CustomerEdit.jspx' redirect='false'/> <forward name='delete' path='/WEB-INF/jsp/CustomerDelete.jspx' redirect='false'/> </action>
DispatchAction
was the first mechanism Struts provided for simplifying action mappings. It certainly makes sense for related URLs, such as /customer/view, /customer/edit, /customer/delete. Actions group naturally when they share an ActionForm
, since each DispatchAction
class is associated with one ActionForm
in a single action mapping. However, it is less obvious how to group actions that do not use a form.
A harder problem comes when naming DispatchAction
methods. It is useful to have a strict convention that simplifies the application's URL API, so there is less to remember. For example, if you group actions by domain type then you may have a CustomerDispatchAction
and a ProductDispatchAction
each with view
, edit
and delete
methods. The first problem is that not every DispatchAction
will use all of the possible method names, so you have to remember whether a URL like /product?method=delete
is valid. The second problem is that some actions are difficult to fit into this method name scheme, which makes consistency hard. These are relatively minor, though.
Wildcard action mappings in struts-config.xml
Approach: reduce the number of action mappings by using wildcards to define generic mappings
Pros: fewer action mappings and a strict mapping from URLs
Cons: wrong case in class names or upper-case in URLs
<action path="/*/*" type="com.example.web.{1}{2}Action"/>
A good way of avoiding the need to update struts-config.xml
every time you add a new page is to add an action mapping with wildcards that you can use for all of your pages, as described in Struts URLs for Perfectionists. This works pretty well, and when I tried this it turned out to be useful for just about all of the pages - there were no exceptions where I could not use the generic action mapping.
The huge disadvantage came when I realised that I could not have both lower-case URLs and camel-case action class names. With the example above, the URL /customer/edit
would require a class called customereditAction
instead of CustomerEditAction
, which is almost as ugly as upper-case letters in the URL.
XDoclet definitions in Action classes
Approach: use XDoclet tags in the action classes to define action mappings, instead of using struts-config.xml
Pros: flexible definition of URLs (action mapping paths) and actions (classes) in the same place
Cons: more complex build, requiring more Ant knowledge; no enforced mapping conventions
/** * @struts.action path="/customer/edit" name="CustomerForm" validate="false" * @struts.action-forward name="default" path="/WEB-INF/jsp/CustomerEdit.jspx" */ public class CustomerEditAction extends ActionMy current favourite approach is to use XDoclet annotations in the action class itself to specify the action mapping. Once the Ant build script is set-up to generate
struts-config.xml
you can then use this approach to keep all of the definitions together, without any duplication. The main advantage is that you can do everything here that you can do in the basic method, of defining action mappings manually. However, this also gives you the disadvantage that your naming conventions for the URL, action class, action form and JSP are not automatic or enforced; in the above example I have four opportunities to misspell 'customer'.
Generating struts-config.xml
another way
One thing that remains unsatisfying about the above Struts-based approaches is that action mappings are not usually a web application's only URL-specific meta-data. For example, you may wish to define a title for each page, to be used in a page template. It is therefore a shame, perhaps, that Struts does not provide a mechanism for extensible action meta-data. So far, at least, I have not come across an implementation of this idea.
Instead I have simply used a separate XML document containing a series of elements whose IDs match the Struts action mapping paths. It is then straightforward to parse this XML in a JSP page template, using the JSTL XML tag library, in order to fetch meta-data from the element whose ID matches the current URL.
In the end, the whole action mappings issue seems more like an unresolved design decision in Struts than useful flexibility. I would prefer to see a simpler model with a recommended way of mapping URLs to application code, and good support for making this work well. Perhaps it is time to revisit some of the alternatives to Struts that consider simplicity a virtue.