Editor's Note: This is part two of a two-part series on Jakarta Struts 1.1. The first part is available here. Portions of this article are excerpted from Sue Spielman's book, The Struts Framework: Practical Guide for Java Programmers (Morgan-Kaufmann), one of the first books on the market covering Struts 1.1 in detail. You can reach Sue at .
The whole point of having nested tags is that the tags can relate to each other and describe the structure of the model they're managing. The assumptions made by the tags simplify the necessary coding. Struts 1.0 developers can heave a sigh of relief knowing that they won't have to mangle code any longer to render a display of a list within a list.
The Struts nested tag library was introduced in Struts 1.1. Almost all of the tags in this library extend the base Struts tags that we've already talked about. This includes tags from the
Logic libraries, except they are prefixed with a nested namespace. So, for example, if we are nesting an
<html:link> tag, we would use
<nested:link> instead. There are also tags new to the nested tag library. The new tags include
<nested:root> tag is used to indicate that you are starting a nested scope. This tag is used if
<html:form> is not being used. The
<html:form> (for backwards compatibility) or
<nested:form> tags will automatically start a scope for you.
The difference between using non-nested tags as opposed to those from the nested library is that using the nested version allows the tags to relate to each other in a nested hierarchy. The fundamental logic of the original tags doesn't change, except that all references to beans and bean properties will be managed in a nested context using the dot notation that we have already seen when using properties. When building complex pages, it is highly likely that you will want to use the nested features. This makes pages much easier to write and maintain, since it allows for the logical flow to be maintained without having to do work-arounds, as was required before the 1.1 release.
The Validator framework is now part of the Struts package structure and can be found in
org.apache.struts.validator. Why use the Validator framework? Validator makes life a bit easier when you have to deal with required fields; determining matches to a regular expression; email, credit card, and date validation; and server-side type checking.
This framework is based on the Commons Validator that can be found at jakarta.apache.org/commons.
The purpose is to perform server-side validations based on validation rules located in validation.xml. It is possible to add custom validations to this file. Rules can be defined for different locales. It's possible to store your specific Validator rules in a separate file. This is accomplished by setting the
config-rules parameter in the
ValidatorServlet contained in the web.xml file. The standard file is available in the Struts dist directory and is called validator-rules.xml.
Using this framework requires adding the
ValidatorServlet to your web.xml file with its appropriate configuration parameters and then extending
org.apache.struts.validator.action.ValidatorForm instead of
Validator is the first component to implement the new
PlugIn interface in Struts 1.1, which we'll talk about next.
You can also add pluggable Validators by adding a validation method signature to your
ValidatorAction class. For more details, see the
A new feature of Struts 1.1 is the ablity to define a
PlugIn is a configuration wrapper for an application-specific module or service that must be notified about application startup and application shutdown events. These events correspond to the container calls
destroy() on the corresponding
ActionServlet instance, which allows a module to be called without the need to subclass
ActionServlet for simple
Servlet lifecycle activities.
PlugIn modules can be configured in the struts-config.xml file, using the
<plug-in> element. Classes that implement the
PlugIn interface must supply a zero-argument constructor for use by
ActionServlet. Configuration can be accomplished by providing standard
JavaBeans property setter methods that will all have been called before the
init() method was invoked. An instance of the specified class is created for each element, and can be configured with nested
set-property elements. For example, in struts-config.xml we might have the last entry defined as:
<plug-in className="org.apache.struts.validator.action.ValidatorPlugIn"> <set-property property="pathname" value="/WEB-INF/validator-rules.xml"/> <set-property property="pathname" value="/WEB-INF/validation.xml"/> </plug-in>
This means that two instances of the
ValidatorPlugIn will be created, each setting the property pathname to the appropriate value. By supporting the
destroy() methods of
ValidatorPlugin is notified about application startup and shutdown events without having to be concerned with extending
PlugIns are configured in the struts-config.xml file by setting the
Declarative exception handling allows for Actions to propagate exceptions. This feature is the very reason that the
perform() method signature changed from throwing
ServletException to just
Exception. This is also why we see the
execute() method of an Action called by the
ActionServlet. One reason for going this route is so your Actions don't have to think about each and every exception that might be thrown from your business logic. It also allows exceptions to be configured within the struts-config.xml file.
Declarative exception handling is accomplished in two ways. One is by configuring
<global-exceptions> in the struts-config.xml file. The other is by using the
exception element of
<action>, which describes a mapping of an exception that may occur during Action delegation.
The way that the exceptions are declared is very similar to the way that
<forwards> are declared. A
<global-exceptions> configures the global handling of exceptions thrown by Actions to mappable resources using an application-relative URI path. This can be a specific page designed to handle this exception.
It is also possible to override an exception handler declared in the global setting by using the exception element in the action, which uses the same type attribute as defined in the global setting. You can specify the
className attribute, which indicates the implementation subclass of the standard configuration bean. The default class is:
The handler attribute is the fully-qualified Java class name of the exception handler that should handle this exception. The default is
key attribute is the message resources key specifying the error message associated with this exception. This is helpful in keeping your errors friendly when your application is internationalized.
path attribute is the application-relative path of the resource to forward to if this exception occurs. The
scope attribute can be set to either
session, and indicates where the
ActionError will be made available. Last but not least is the
type attribute. This is the fully-qualified Java class name of the exception to be handled. (What a mouthful.) Here's a sample:
In struts-config.xml, we first declare our
<global-exceptions> in the following format:
<global-exceptions> <exception key="error.required" type="org.apache.struts.util.AppException" path="/appError.jsp"/> </global-exceptions>
Here we are declaring that any Action (or business logic) that throws the
AppException will be sent to appError.jsp. We are associating the
error.required message string from our
ApplicationResource file with this exception. The
ApplicationResource file is discussed in detail in Chapter Nine of my book, The Struts Framework: Practical Guide for Java Programmers (2002, Morgan-Kaufman), when we talk about internationalization. If you view the mainMenu.jsp file in our sample application, there is a link to force an application exception. This makes it easier for you to quickly force an error exception and to follow the flow in the ForceErrorAction.java file. You can add as many
<exception> elements to the
<global-exceptions> as you like.
Next we'll look at a specific Action exception declaration.
<action path="/insert" ... <exception key="error.required" type="cdmanager.exceptions.MissingValueException" path="/insertError.jsp"/> </action>
In this case, we are declaring that if the
InsertAction (or business logic used by the
InsertAction) throws the application-specific exception
MissingValueException, then send the exception to insertError.jsp. Note that in this example, we have defined a specific application exception. This is helpful if you want to wrap other, less helpful exceptions. For example, you might want to catch
SQLExceptions and re-throw them as application-specific exceptions with a more meaningful message to the user.
It is also worth pointing out that matching exceptions to exception handlers takes inheritance into account (i.e., you can declare a handler for a superclass and handle all of the exceptions for subclasses of that exception class as well.) The matching algorithm works identically to the one used by the servlet container to select
<error-page> matches for exceptions.
If you are upgrading an existing Struts application to v1.1, you should not have that many worries. The development team did a very good job of keeping Struts backwards-compatible. For all intents and purposes, you should have little trouble upgrading to v1.1. While each application is different, here are some general rules of thumb to use when evaluating your application.
Many of the Struts
util packages have been moved to Jakarta Commons, so you may need to adjust some package names in your code. This should be as simple as a global find and replace.
In order for this sub-app feature to work, any request for a presentation page that uses elements from the configuration file, including
Action Mappings, must be routed through the controller. This allows the controller to make the appropriate configuration available for a given page. Having requests pass through the controller is not new to Struts 1.1. While it might have been possible to bypass this by having direct linking to pages, it is good design practice to make sure everything flows through the controller. When using the MVC model, this is the way it should be, anyway. Many other features in advanced applications, including security and logging, are easier to implement when everything passes through the controller.
In Struts 1.x,
Action.perform() is the method called by the
ActionServlet. This is typically where your business logic resides, or at least the flow control to your JavaBeans and EJBs that handle your business logic. As we already mentioned, to support declarative exception handling, the method signature changed in
execute just throws
Action.perform() is now deprecated; however, the Struts v1.1
ActionServlet is smart enough to know whether or not it should call
execute in the Action, depending on which one is available.
If you are building with ANT, you might need to adjust your build.xml to include the Jakarta Commons library files. To make sure you don't run into classloader issues, check the
struts-user mailing list if your container is having problems.
I've taken Struts v1.x applications and run them within minutes on Struts v1.1. However, there are a couple of things that you should be aware of if you've taken advantange of some of the more advanced features. For example, the
ApplicationConfig object is used now to handle Servlet context attributes per sub-application. This means that if you are using information from any of the various objects in Struts that handle your application collections, like
ActionMappings, you'll need to reference them through
ApplicationConfig. Also, if you have done any
ActionServlet-specific work for your
ActionServlet, you might want to take a look at the changes that have taken place regarding the
Struts v1.1 offers us some new and exciting features for Web application development. You should be able to start taking advantage of them as soon as you're done with this article.
Sue Spielman is an associate editor for ONJava.com, covering JSP and Servlets technologies. She is also President and Senior Consulting Engineer for Switchback Software LLC.
Read more JSP and Servlets columns.
Return to ONJava.com.
Copyright © 2009 O'Reilly Media, Inc.