Struts 1.1

by Thomas Paul

Introduction

Last year I wrote an article about Struts 1.0. With the release of Struts 1.1 beta, it is time for an update to that article. First we will look at how to change last year's simple logon application to run correctly in Struts 1.1. Then we will look at one of the interesting new additions to Struts, the validator framework, and show how it can simplify our application.

Installing Struts 1.1

The installation of Struts has not changed much except that there are more files to install. The struts.jar file has had many of the common classes pulled out and placed into separate jar files. There are now thirteen jar files to place in your applications lib directory. Also, the TLD files have been changed a little bit. The struts.tld file has been eliminated and its functionality moved into other TLD files. There are two additional TLD files to add to your WEB-INF directory. You need to update the web.xml file for the changes to the tag library descriptors but no other changes are required.

Upgrading Our Application

Our Struts 1.0 application won't work as it was written. At the very minimum, we need to make changes to our two JSP files because we no longer have the struts.tld file. The change is simple. We first remove the taglb directive that contains struts.tld and then we change struts:message to bean:message everywhere it appears.

At this point we can run our application but it still contains some deprecated code. The LoginAction class needs to be changed because of deprecated code. First, the key method of our Action class, perform() has been deprecated. The reason for doing this was because the perform() could only throw IOException and ServletException and it was realized that Action classes should be able to throw any Exception required by the programmer. Since it was desired that code still be backward compatible, a new method was added in place the perform() method. The new method is execute(). Here is the method signature:

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

As you can see, the only difference is that execute() is declared to throw Exception rather than IOException and ServletException.

The second change was to the Action.ERROR_KEY constant. It was decided to move the constants into their own class called Globals. So the original statement needs to be changed to Globals.ERROR_KEY. Since Globals is in the org.apache.struts package, we need to add an import statement for that package.

And that is it. Recompile your code and it should work exactly the same as the original version. The listing of the revised LoginAction class is here:


import javax.servlet.http.*;
import org.apache.struts.action.*;
import org.apache.struts.*;

public class LoginAction extends Action {

	public LoginAction() {}

	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws java.lang.Exception {

		LoginBean lb = new LoginBean();
		request.setAttribute("LoginBean", lb);
		lb.setParameters(request);
		ActionErrors ae = lb.validate();
		request.setAttribute(Globals.ERROR_KEY, ae);

		if (ae == null || ae.size() == 0) {
			return mapping.findForward("valid");
		} else {
			return mapping.findForward("invalid");
		}
	}
}

The Validator Framework

Most ActionForm classes (and our example one is no exception) are fairly simple, containing little more than field level validations. In our example, we are simply testing to insure that the user actually filled in the fields. Imagine a large application with dozens of forms and you get the idea that you can wind up maintaining a large number of extremely simple and similar classes.

The solution is provided in Struts 1.1 with the addition of the ValidatorForm and DynaValidatorForm classes. Using the validator framework is actually very simple to do and eliminates needing to code ActionForm classes. To set up the validator framework you need to do two things. First, copy the file validator-rules.xml from the Struts installation files to the WEB-INF directory of your application. Second, update the struts-config.xml with the plug in information required for the ValidatorPlugIn class. The required element looks like this:

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
     <set-property property="pathnames"
               value="/WEB-INF/validator-rules.xml,
                      /WEB-INF/validation.xml"/>
</plug-in>

Note that the plug-in tag is new in Struts 1.1. As such, it's not described in the Struts 1.0 Configuration Document Type Definition (DTD). So, you also need to update the DOCTYPE tag at the top of the struts-config.xml file to use the Struts 1.1 Configuration DTD.

< !DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

Notice that there are two validation files listed. The first, validator-rules.xml was included as part of the Struts installation. The second, validation.xml, will be created by you. This file contains the rules that link fields in the form with the rules in the validator-rules.xml file. The validator framework comes with a basic set of validations. If these are insufficient for your needs, you can write your own validations and either add them to the validator-rules.xml file or put them in a separate file and add the file to the <plug-in> tag.

The basic validations included in the validation framework are:


We use our validation.xml file to tell Struts which rules to run against which fields in our form. Here is the entry for our form from the validation.xml file:

<!DOCTYPE form-validation PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/validation_1_1.dtd">

<form-validation>
   <formset>
      <form name="login">
         <field property="userId" depends="required,minlength">
            <arg0 key="label.userId" />
            <arg1 name="minlength" key="${var:minlength}" resource="false"/>
            <var>
               <var-name>minlength</var-name>
               <var-value>4</var-value>
            </var>
         </field>
         
         <field property="passWord" depends="required">
           <arg0 key="label.passWord" />
         </field>
     </form>
  </formset>
</form-validation>


These commands tell the validation framework that user id and password are both required fields and that the user id must be at least 4 characters in length. Let's look at the main pieces of the validation.xml.

<form name="login"> - identifies the form-bean that this validation is associated with.

<field property="userId" depends="required,minlength"> - identifies the field from the form and the validation types to apply to that field.

<arg0 key="label.userId" /> - identifies an argument that will be used to replace a place holder in the error message associated with this field. The argument is a message resource property.

<arg1 name="minlength" key="${var:minlength}" resource="false"/> - this is also an argument place holder. The difference is that this will use a variable (identied below it) instead of a message resource entry. Indirection is permitted here, so the variable can be a string pointing to a message resource entry. Specifying resource=false prevents Struts from looking for "4" in the message resources.

<var> - identifies that a variable is to follow.

<var-name>minlength</var-name> - identifies the name of the variable. Since we identified that we are using the "minlength" validator, we are required to supply this variable.

<var-value>4</var-value> - the value of the minlength variable. The "minlength" validator requires that this field must be an int.

Some of the validators provide JavaScript validation that can be included in your JSP page if you wish. If you use JavaScript validation, the form will be validated in the browser and if it fails validation an alert box will appear and the form will not be sent to the browser. To invoke JavaScript validation first add the html:javascript tag to the page specifying the form to be validated. Then add an onsubmit event handler to the html:form tag and you are finished. The finished LoginView.jsp looks like this with the changes highlighted:


<!-- LoginView.jsp -->

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>


<HTML>
<HEAD><TITLE><bean:message key="title.login" /></TITLE>
<html:javascript formName="login" />
</HEAD>
<BODY>
<bean:message key="heading.login" />
<html:errors />
<html:form action="/login" onsubmit="return validateLogin(this)" >
	<p>
	<bean:message key="label.userId" />:
	<html:text property="userId" size="10" />
	<br>
	<bean:message key="label.passWord" />:
	<html:password property="passWord" size="10" />
	<br><br>
	<html:submit>
		<bean:message key="button.submit" />
	</html:submit>
</html:form>
</BODY>
</HTML>


We need to add two new entries to our MessageResource.properties file for the error messages generated by the automatic validation.

errors.required={0} is required.<br>
errors.minlength={0} cannot be less than {1} characters.<br>

There is only one change left to make. We need to update the struts-config.xml to change the form to use the DynaValidatorForm instead of the custom form we used in Struts 1.0. Here is the change we need to make:

<form-bean name="login"
           type="org.apache.struts.validator.DynaValidatorForm" >
      <form-property name="userId" type="java.lang.String" />
      <form-property name="passWord" type="java.lang.String" />
</form-bean>

Each of the fields on the form must be described by giving its name and type.

We are now finished. The ActionForm class can be deleted and our application will run. Any changes to the validation for our form can be done by making changes to our validation.xml file.

Conclusion

Struts 1.1 is still in beta and the documentation is far from complete. Information on running the validators included with Struts is difficult to find. A lot of the code you work with will be trial and error to get it to run correctly. If you need help getting a feature of Struts to work there are quite a few resources listed on the Struts web site, there is the Struts mailing list, and of course the JavaRanch Framework forum.

When will 1.1 become a release? As with all Apache projects it is difficult to say. This version, beta 3, has been very stable in my testing and provides many improved features to make Struts easier to use. There are a few known issues and once those are fixed we can look forward to a release candidate.