Introduction to Validator
Framework
Struts Framework provides the functionality to
validate the form data. It can be use to validate the data on the users
browser as well as on the server side. Struts Framework emits the java
scripts and it can be used to validate the form data on the client
browser. Server side validation of the form can be accomplished by sub
classing your From Bean with DynaValidatorForm
class.
The
Validator framework comes integrated with the Struts Framework and can be
used without doing any extra settings.
Using Validator
Framework
Validator uses the XML file to pickup the validation
rules to be applied to an form. In XML validation requirements are defined
applied to a form. In case we need special validation rules not provided
by the validator framework, we can plug in our own custom validations into
Validator.
The Validator
Framework uses two XML configuration files validator-rules.xml and
validation.xml. The validator-rules.xml defines the standard
validation routines, these are reusable and used in validation.xml.
to define the form specific validations. The validation.xml defines
the validations applied to a form bean.
Structure of
validator-rule.xml
The
validation-rules.xml is provided with the Validator Framework and
it declares and assigns the logical names to the validation routines. It
also contains the client-side javascript code for each validation routine.
The validation routines are java methods plugged into the system to
perform specific validations.
Following table contains the
details of the elements in this file:
Element | Attributes and Description |
form-validation | This is the root node. It
contains nested elements for all of the other configuration
settings. |
global | The validator details
specified within this, are global and are accessed by all
forms. |
validator | The validator element
defines what validators objects can be used with the fields
referenced by the formset elements. The attributes are: ·
name:
Contains a logical name for the validation routine ·
classname: Name of the Form Bean class that extends the
subclass of ActionForm class ·
method:
Name of the method of the Form Bean class ·
methodParams: parameters passed to the method ·
msg:Validator uses Struts' Resource Bundle
mechanism for externalizing error messages. Instead of having
hard-coded error messages in the framework, Validator allows you to
specify a key to a message in the ApplicationResources.properties
file that should be returned if a validation fails. Each validation
routine in the validator-rules.xml file specifies an error message
key as value for this attribute. ·
depends:
If validation is requiblak, the value here is specified as 'requiblak'
for this attribute. ·
jsFunctionName: Name of the javascript function is specified
here. |
javascript | Contains the code of the
javascript function used for client-side validation. Starting in
Struts 1.2.0 the default javascript definitions have been
consolidated to commons-validator. The default can be
overridden by supplying a <javascript> element with a CDATA
section, just as in struts 1.1. |
The Validator plug-in (validator-rules.xml) is
supplied with a pblakefined set of commonly used validation rules such as
Requiblak, Minimum Length, Maximum length, Date Validation, Email Address
validation and more. This basic set of rules can also be extended with
custom validators if requiblak.
Structure of
validation.xml
This validation.xml configuration file defines
which validation routines that is used to validate Form Beans. You can
define validation logic for any number of Form Beans in this
configuration file. Inside that definition, you specify the validations
you want to apply to the Form Bean's fields. The definitions in this file
use the logical names of Form Beans from the struts-config.xml file along
with the logical names of validation routines from the validator-rules.xml
file to tie the two together.
Element | Attributes and Description |
form-validation | This is the root node. It
contains nested elements for all of the other configuration
settings |
global | The constant details are
specified in <constant> element within this
element. |
constant | Constant properties are
specified within this element for pattern matching. |
constant-name | Name of the constant
property is specified here |
constant-value | Value of the constant
property is specified here. |
formset | This element contains
multiple <form> elements |
form | This element contains the
form details. The attributes are: name:Contains the
form name. Validator uses this logical name to map the validations
to a Form Bean defined in the struts-config.xml file |
field | This element is inside the
form element, and it defines the validations to apply to specified
Form Bean fields.
The attributes are: ·
property:
Contains the name of a field in the specified Form Bean ·
depends:
Specifies the logical names of validation routines from the
validator-rules.xml file that should be applied to the
field. |
arg | A key for the error message
to be thrown incase the validation fails, is specified
here |
var | Contains the variable names
and their values as nested elements within this element. |
var-name | The name of the criteria
against which a field is validated is specified here as a
variable |
var-value | The value of the field is
specified here |
Example of form in the validation.xml
file:
<!-- An example form --> <form
name="logonForm"> <field property="username" depends="requiblak"> <arg
key="logonForm.username"/>
</field> <field
property="password"
depends="requiblak,mask"> <arg
key="logonForm.password"/> <var>
<var-name>mask</var-name>
<var-value>^[0-9a-zA-Z]*$</var-value> </var> </field>
</form> |
The <html:javascript> tag to allow front-end
validation based on the xml in validation.xml. For example the code:
<html:javascript formName="logonForm"
dynamicJavascript="true" staticJavascript="true" /> generates the client side java script for the form
"logonForm" as defined in the validation.xml file. The
<html:javascript> when added in the jsp file generates the client
site validation script.
In the next lesson we will create a new form for
entering the address and enable the client side java script with the
Validator Framework.
In
this lesson we will create JSP page for entering the address and use the
functionality provided by Validator Framework to validate the user data on
the browser. Validator Framework emits the JavaScript code which validates
the user input on the browser. To accomplish this we have to follow the
following steps:
- Enabling the Validator plug-in: This makes the
Validator available to the system.
- Create Message Resources for the displaying the
error message to the user.
- Developing the Validation rules We have to
define the validation rules in the validation.xml for the address form.
Struts Validator Framework uses this rule for generating the JavaScript
for validation.
- Applying the rules: We are requiblak to add the
appropriate tag to the JSP for generation of JavaScript.
- Build and test: We are requiblak to build the
application once the above steps are done before testing.
Enabling the
Validator plug- in
To enable the
validator plug-in open the file struts-config.xml and make sure that
following line is present in the file.
<!-- Validator plugin --> <plug-in
className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/> </plug-in> |
Creating Message Resources
Message resources are used by the Validator Framework
to generate the validation error messages. In our application we need to
define the messages for name, Address and E-mail address. Open the
Struts\strutstutorial\web\WEB-INF\MessageResources.properties file and add
the following lines:
AddressForm.name=Name
AddressForm.address=Address
AddressForm.emailAddress=E-mail
address
Developing Validation rules
In this application we are adding only one validation
that the fields on the form should not be blank. Add the following
code in the validation.xml.
<!-- Address form Validation--> <form name="AddressForm">
<field property="name"
depends="requiblak"> <arg
key="AddressForm.name"/>
</field> <field
property="address"
depends="requiblak"> <arg
key="AddressForm.address"/>
</field> <field
property="emailAddress"
depends="requiblak"> <arg
key="AddressForm.emailAddress"/>
</field> </form> |
The above definition defines the validation for the
form fields name, address and emailAddress. The
attribute depends="requiblak" instructs the Validator Framework to
generate the JavaScript that checks that the fields are not left blank. If
the fields are left blank then JavaScript shows the error message. In the
error message the message are taken from the key defined in the <arg
key=".."/> tag. The value of key is taken from the message
resources
(Struts\strutstutorial\web\WEB-INF\MessageResources.properties).
Applying Validation rules to JSP
Now create the AddressJavascriptValidation.jsp
file to test the application. The code for
AddressJavascriptValidation.jsp is as
follows:
<%@ taglib uri="/tags/struts-bean"
prefix="bean" %> <%@ taglib uri="/tags/struts-html"
prefix="html" %>
<html:html locale="true"> <head> <title><bean:message
key="welcome.title"/></title> <html:base/> </head> <body bgcolor="white"> <html:form
action="/AddressJavascriptValidation" method="post" onsubmit="return
validateAddressForm(this);">
<div
align="left"> <p> This application shows the use
of Struts Validator.<br> The following form contains
fields that are processed by Struts Validator.<br> Fill in the form and see how JavaScript generated by Validator
Framework validates the form. </p>
<p> <html:errors/> </p> <table>
<tr> <td align="center"
colspan="2"> <font size="4"><b>Please Enter the
Following Details</b></font> </tr> <tr> <td align="right"> <b>Name</b> </td> <td
align="left"> <html:text property="name" size="30"
maxlength="30"/> </td> </tr> <tr> <td align="right"> <b>Address</b> </td> <td
align="left"> <html:text property="address" size="30"
maxlength="30"/> </td> </tr>
<tr> <td align="right"> <b>E-mail
address</b> </td> <td align="left"> <html:text property="emailAddress" size="30"
maxlength="30"/> </td> </tr>
<tr> <td align="right"> <html:submit>Save</html:submit> </td> <td align="left"> <html:cancel>Cancel</html:cancel> </td> </tr> </table> </div>
<!-- Begin Validator Javascript Function--> <html:javascript formName="AddressForm"/> <!-- End of Validator Javascript Function-->
</html:form> </body> </html:html> |
The code <html:javascript
formName="AddressForm"/> is used to plug-in the Validator
JavaScript.
Create the
following entry in the struts-config.xml for the mapping the
/AddressJavascriptValidation url for handling the form submission
through
AddressJavascriptValidation.jsp.
<action
path="/AddressJavascriptValidation"
type="roseindia.net.AddressAction"
name="AddressForm" scope="request" validate="true"
input="/pages/AddressJavascriptValidation.jsp"> <forward name="success"
path="/pages/success.jsp"/> </action> |
Add the following line in the index.jsp to call the
form.
<li>
<html:link
page="/pages/AddressJavascriptValidation.jsp">Client Side
Validation for Address Form</html:link>
<br>
The
Address Form that validates the data on the client side using Stuts
Validator generated JavaScript.
</li>
Building Example and Testing
To
build and deploy the application go to Struts\strutstutorial directory and
type ant on the command prompt. This will deploy the application. Open the
browser and navigate to the AddressJavascriptValidation.jsp page. Your browser should show the following out
put.
If the fields are left blank and Save button is
clicked, browser shows the error message.
In this lesson you learned how to use Struts Validator
Framework to validate the form on client browser.
What is Struts Tiles?
Tiles is a framework for the development user
interface. Tiles is enables the developers to develop the web applications
by assembling the reusable tiles (jsp, html, etc..). Tiles uses the
concept of reuse and enables the developers to define a template for the
web site and then use this layout to populate the content of the web site.
For example, if you have to develop a web site having more that 500 page
of static content and many dynamically generated pages. The layout of the
web site often changes according to the business requirement. In this case
you can use the Tiles framework to design the template for the web site
and use this template to populate the contents. In future if there is any
requirement of site layout change then you have to change the layout in
one page. This will change the layout of you whole web
site.
Steps To Create Tiles
Application
Tiles is very useful
framework for the development of web applications. Here are the steps
necessary for adding Tiles to your Struts
application:
- Add the Tiles Tag Library Descriptor (TLD) file to the
web.xml.
- Create layout JSPs.
- Develop the web pages using layouts.
- Repackage, run and test application.
Add
the Tiles TLD to web.xml file
Tiles
can can be used with or without Struts. Following entry is requiblak in the
web.xml file before you can use the tiles tags in your
application.
<taglib>
<taglib-uri>/tags/struts-tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location> </taglib> |
Create
layout JSPs.
Our web application
layout is divided into four parts: To Banner, Left Navigation Bar, Content
Area and Bottom of the page for copy right information. Here is the code
for out template (template.jsp):
<%@ page language="java" %> <%@ taglib
uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <html>
<head> <title><tiles:getAsString name="title"
ignore="true"/></title> </head>
<body>
<table border="1" cellpadding="0"
cellspacing="0" width="100%" bordercolor="#000000"
bgcolor="#E7FDFE"> <tr> <td width="100%"
colspan="2" valign="top"><tiles:insert
attribute="header"/></td> </tr> <tr> <td width="23%"><tiles:insert
attribute="menu"/></td> <td width="77%" valign="top"
valign="top"><tiles:insert
attribute="body"/></td> </tr> <tr> <td width="100%" colspan="2" valign="top"><tiles:insert
attribute="bottom"/></td> </tr> </table>
</body>
</html> |
We have defined the structure for web application
using the appropriate html and did the following
things:
- Referenced the /WEB-INF/struts-tiles.tld TLD.
- Used the string parameters to display title using the
tiles:getAsString tag. If the attribute ignore="true" then Tiles
ignore the missing parameter. If this is true then the Tiles framework
will through the exception in case the parameter is missing.
- To insert the content JSP, the tiles:insert tag
is used, which inserts any page or web resources that framework refers
to as a title. For Example <tiles:insert attribute="header"/>
inserts the header web page.
Develop the web pages using
layouts
Now we will use tile layout
create a page to display the content page in the in our application. For
every content page there is additional jsp file for inserting the content
in the Layout, so we have to create two jsp files one for content and
another for displaying the content. In our example these file are
example.jsp and content.jsp. Here is the code for both the
files:
content.jsp
<p align="left"><font color="#000080"
size="5">Welcome to the Title Tutorial</font></p> <p align="left"><font color="#000080" size="5">This is
the content
page</font></p> |
The content.jsp simply define the content of the page.
The content may be dynamic or static depending on the
requirements.
example.jsp
<%@ page language="java" %> <%@
taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<tiles:insert page="/tiles/template.jsp" flush="true"> <tiles:put name="title" type="string"
value="Welcome" /> <tiles:put name="header"
value="/tiles/top.jsp" /> <tiles:put
name="menu" value="/tiles/left.jsp" />
<tiles:put name="body" value="/tiles/content.jsp" /> <tiles:put name="bottom" value="/tiles/bottom.jsp"
/> </tiles:insert> |
The code <tiles:insert
page="/tiles/template.jsp" flush="true"> specifies the tiles layout
page to be used. We have set the flush attribute to true, this makes the
tile file to be written to browser before the rest of the page. To specify
the title of the page <tiles:put name="title" type="string"
value="Welcome" /> is used. The following code is used to insert
the actual pages in the template.:
<tiles:put name="header"
value="/tiles/top.jsp" />
<tiles:put name="menu"
value="/tiles/left.jsp" />
<tiles:put name="body"
value="/tiles/content.jsp" />
<tiles:put
name="bottom" value="/tiles/bottom.jsp" />
The
top.jsp will be inserted in the layout's header region. The left.jsp will
be inserted in the layout's menu region. The content.jsp wil be inserted
in the layout's body region and the bottom.jsp will be inserted in the
bottom region.
Repackage, run and test
application
Add the following code
in the index.jsp to test the this tile
example:
<li>
<html:link page="/tiles/example.jsp">Tiles
Example</html:link>
<br>
Example of creating first
tile application.
</li>
Use
the ant tool to build the application and deploy on the server. To test
the application go to the index.jps and click on the Tiles Example
link.
In the last
section we studied how to forward the request (call) to a jsp page which
specifies which tiles layout definition should be used to apply to the
content. In this section I will show you how to use the a definition in
the tiles-defs.xml for generating the
content.
In Tiles
we can define the definition in the tiles-defs.xml which specifies the
different components to "plugin" to generate the output. This eliminates
the need to define extra jsp file for each content file. For example in
the last section we defined example.jsp to display the content of
content.jsp file. In this section I will show you how to eliminate the
need of extra jsp file using tiles-defs.xml
file.
Steps to Use the
tiles-defs.xml
Setup the Tiles plugin in struts-config.xml
file.
Add the following
code in the struts-config.xml (If not present). This enables the
TilesPlugin to use the /WEB-INF/tiles-defs.xml
file.
<plug-in
className="org.apache.struts.tiles.TilesPlugin" > <!-- Path to XML
definition file -->
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml" /> <!-- Set Module-awareness
to true -->
<set-property property="moduleAware" value="true" /> </plug-in> |
Defining the tiles-defs.xml
In this file we are defining the different components
to "plugin". Here is the code:
<definition
name="Tiles.Example" page="/tiles/template.jsp">
<put
name="title" type="string" value="Welcome" />
<put
name="header" value="/tiles/top.jsp" />
<put
name="menu" value="/tiles/left.jsp" />
<put
name="body" value="/tiles/content.jsp" />
<put
name="bottom" value="/tiles/bottom.jsp" />
</definition>
The
name of the definition is Tiles.Example, we will use this in
struts-config.xml (While creating forwards in struts-config.xml file)
file. The page attribute defines the template file to be used and the put
tag specifies the different components to "plugin". Your tiles-defs.xml
should look like:
<?xml version="1.0" encoding="ISO-8859-1"
?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration
1.1//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
<tiles-definitions>
<definition
name="Tiles.Example" page="/tiles/template.jsp"> <put
name="title" type="string" value="Welcome" /> <put
name="header" value="/tiles/top.jsp" /> <put
name="menu" value="/tiles/left.jsp" /> <put
name="body" value="/tiles/content.jsp" /> <put
name="bottom" value="/tiles/bottom.jsp" /> </definition>
<definition
name="${YOUR_DEFINITION_HERE}"> </definition>
</tiles-definitions> |
Configure the Struts Action to use Tiles
Definition
Open the struts-config.xml file and add
the following code:
<action path="/Tiles/Example"
forward="Tiles.Example"/> |
With Tiles, the action points to the Tiles definition,
as shown in the above code. In this code we are using the
Tiles.Example definition which we have defined in the
tiles-defs.xml file. Without Tiles, forward and
action definitions point directly to JSPs. With Tiles, they point to the
page's definition in the Tiles configuration
file.
Testing
the Application
Create a link in index.jsp to call the
Example. Code added are:
<li> <html:link
page="/Tiles/Example.do">Using
tiles-defs.xml</html:link> <br> Example shows
you how to use tiles-defs.xml file. </li> |
To test the application build it using ant and deploy
on the JBoss server. Type http://localhost:8080/strutstutorial/index.jsp
in the bowser and select the Using tiles-defs.xml link. Your browser
should show the page.
In this tutorial you will learn how to create Struts
DynaActionForm. We will recreate our address form with Struts
DynaActionForm. DynaActionForm is specialized subclass of ActionForm that
allows the creation of form beans with dynamic sets of properties, without
requiring the developer to create a Java class for each type of form bean.
DynaActionForm eliminates the need of FormBean class and now the form bean
definition can be written into the struts-config.xml file. So, it makes
the FormBean declarative and this helps the programmer to blakuce the
development time.
In
this tutorial we will recreate the add form with the help of
DynaActionForm. It also shows you how you can validate use input in the
action class.
Adding DynaActionForm Entry in
struts-config.xml
First we will add the necessary entry in the
struts-config.xml file. Add the following entry in the
struts-config.xml file. The form bean is of
org.apache.struts.action.DynaActionForm type. The
<form-property/> tag is used to define the property for the
form bean. We have defined three properties for our dynamic form
bean.
<form-bean
name="DynaAddressForm"
type="org.apache.struts.action.DynaActionForm"> <form-property
name="name" type="java.lang.String"/> <form-property
name="address" type="java.lang.String"/> <form-property
name="email" type="java.lang.String" /> </form-bean> |
Adding action mapping
Add
the following action mapping in the struts-config.xml
file:
<action path="/DynaAddress"
type="roseindia.net.AddressDynaAction"
name="DynaAddressForm"
scope="request" validate="true" input="/pages/DynaAddress.jsp">
<forward name="success"
path="/pages/success.jsp"/> <forward
name="invalid" path="/pages/DynaAddress.jsp" />
</action> |
Creating Action Class
Code for action class is as
follows:
package roseindia.net;
/**
* @author Deepak Kumar
* @Web http://www.roseindia.net
* @Email roseindia_net@yahoo.com
*/
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.action.ActionMessage;
public class AddressDynaAction extends Action
{
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception{
DynaActionForm addressForm = (DynaActionForm)form;
//Create object of ActionMesssages
ActionMessages errors = new ActionMessages();
//Check and collect errors
if(((String)addressForm.get("name")).equals("")) {
errors.add("name",new ActionMessage("error.name.requiblak"));
}
if(((String)addressForm.get("address")).equals("")) {
errors.add("address",new ActionMessage("error.address.requiblak"));
}
if(((String)addressForm.get("email")).equals("")) {
errors.add("email",new ActionMessage("error.emailaddress.requiblak"));
}
//Saves the error
saveErrors(request,errors);
//Forward the page
if(errors.isEmpty()){
return mapping.findForward("success");
}else{
return mapping.findForward("invalid");
}
}
}
|
Creating the JSP file
We
will use the Dyna Form DynaAddressForm created above in the jsp
file. Here is the code of the jsp(DynaAddress.jsp)
file.
<%@ taglib uri="/tags/struts-bean"
prefix="bean" %> <%@ taglib uri="/tags/struts-html"
prefix="html" %>
<html:html locale="true"> <head> <title><bean:message
key="welcome.title"/></title> <html:base/> </head> <body bgcolor="white"> <html:form action="/DynaAddress" method="post"> <table> <tr> <td align="center"
colspan="2"> <font size="4">Please Enter the Following
Details</font> </tr>
<tr> <td align="left" colspan="2"> <font
color="blak"><html:errors/></font> </tr>
<tr> <td align="right"> Name </td> <td align="left"> <html:text property="name" size="30" maxlength="30"/> </td> </tr> <tr> <td
align="right"> Address </td> <td
align="left"> <html:text property="address" size="30"
maxlength="30"/> </td> </tr>
<tr> <td align="right"> E-mail address </td> <td align="left"> <html:text
property="email" size="30" maxlength="30"/> </td> </tr>
<tr> <td align="right"> <html:submit>Save</html:submit> </td> <td align="left"> <html:cancel>Cancel</html:cancel> </td> </tr> </table>
</html:form> </body> </html:html> |
Add the following line in the index.jsp to call the
form.
<li>
<html:link page="/pages/DynaAddress.jsp">Dyna Action Form
Example</html:link>
<br>
Example shows you how to
use DynaActionForm.
</li>
Building Example and Testing
To
build and deploy the application go to Struts\strutstutorial directory and
type ant on the command prompt. This will deploy the application. Open the
browser and navigate to the DynaAddress.jsp page. Without entering anything in the form and
submitting the submit button, your browser should show the following out
put.
Struts
File Upload Example
In this tutorial you will learn how to use Struts to
write program to upload files. The interface
org.apache.struts.upload.FormFile is the heart of the struts file
upload application. This interface represents a file that has been
uploaded by a client. It is the only interface or class in upload package
which is typically referenced directly by a Struts
application.
Creating
Form Bean
Our
form bean class contains only one property theFile, which is
of type org.apache.struts.upload.FormFile. Here is the code of
FormBean (StrutsUploadForm.java):
import org.apache.struts.action.*;
import org.apache.struts.upload.FormFile;
/**
* Form bean for Struts File Upload.
*
*/
public class StrutsUploadForm extends ActionForm
{
private FormFile theFile;
/**
* @return Returns the theFile.
*/
public FormFile getTheFile() {
return theFile;
}
/**
* @param theFile The FormFile to set.
*/
public void setTheFile(FormFile theFile) {
this.theFile = theFile;
}
}
Creating Action Class
Our
action class simply calls the getTheFile() function on the
FormBean object to retrieve the reference of the uploaded file.
Then the reference of the FormFile is used to get the uploaded file and
its information. Here is the code of our action
class(StrutsUploadAction.java):
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
/**
* Struts File Upload Action Form.
*
*/
public class StrutsUploadAction extends Action
{
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception{
StrutsUploadForm myForm = (StrutsUploadForm)form;
// Process the FormFile
FormFile myFile = myForm.getTheFile();
String contentType = myFile.getContentType();
String fileName = myFile.getFileName();
int fileSize = myFile.getFileSize();
byte[] fileData = myFile.getFileData();
System.out.println("contentType: " + contentType);
System.out.println("File Name: " + fileName);
System.out.println("File Size: " + fileSize);
return mapping.findForward("success");
}
}
Defining form Bean in struts-config.xml
file
Add the following entry in the struts-config.xml file
for defining the form bean:
<form-bean
name="FileUpload"
type="roseindia.net.StrutsUploadForm"/> |
Defining Action Mapping
Add
the following action mapping entry in the struts-config.xml
file:
<action
path="/FileUpload"
type="roseindia.net.StrutsUploadAction" name="FileUpload" scope="request" validate="true" input="/pages/FileUpload.jsp"> <forward name="success"
path="/pages/uploadsuccess.jsp"/> </action> |
Developing jsp page
Code of the jsp (FileUpload.jsp) file to upload is as
follows:
<%@ taglib uri="/tags/struts-bean"
prefix="bean" %> <%@ taglib uri="/tags/struts-html"
prefix="html" %>
<html:html locale="true"> <head> <title>Struts File Upload
Example</title> <html:base/> </head> <body bgcolor="white"> <html:form
action="/FileUpload" method="post"
enctype="multipart/form-data"> <table> <tr> <td align="center" colspan="2"> <font size="4">Please Enter the Following
Details</font> </tr>
<tr> <td align="left" colspan="2"> <font
color="blak"><html:errors/></font> </tr>
<tr> <td align="right"> File Name </td> <td align="left"> <html:file property="theFile"/> </td> </tr>
<tr> <td align="center"
colspan="2"> <html:submit>Upload
File</html:submit> </td> </tr> </table>
</html:form> </body> </html:html> |
Note that we are setting the encrypt property of the
form to
enctype="multipart/form-data".
code for the success page (uploadsuccess.jsp)
is:
<html>
<head> <title>Success</title> </head>
<body>
<p align="center"><font size="5"
color="#000080">File Successfully
Received</font></p>
</body>
</html> |
Add the following line in the index.jsp to call the
form.
<li>
<html:link page="/pages/FileUpload.jsp">Struts File
Upload</html:link>
<br>
Example shows you how to
Upload File with Struts.
</li>
Building Example and Testing
To
build and deploy the application go to Struts\strutstutorial directory and
type ant on the command prompt. This will deploy the application. Open the
browser and navigate to the FileUpload.jsp page. Your browser should display the file upload
form:
In this tutorial we shall see how to implement
Internationalization (abbreviated as I18N) in Struts.
The
Multinational Corporations have their branches in various parts of the
world. so, they must provide products and services to their clients and
customers in their traditional way. The customers will expect the product
to work in their native languages especially the date, time, currency
etc.,. So, the we should not make any assumptions about their clients
region or language. If such assumptions become invalid, we have to
re-engineer the applications.
Internationalization or I18N is
the process of designing the software to support multiple languages and
regions, so that we don't need to re-engineer the applications every
language or country needs to be supported.
Struts provides
various locale sensitive JSP tags which can be used to make the
applications simpler. With this short introduction we shall see how to
implement i18n in a Simple JSP file of
Struts.
g:\>md
localedemo
g:\>cd localedemo
g:\localedemo>edit
localedemo.jsp
// g:\localedemo\localedemo.jsp
<%@ page language="java" %>
<%@ taglib
uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@
taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"
%>
<html:html locale="true">
<body bgcolor=pink>
<bean:message
key="index.info" />
</body>
</html:html
Next copy struts-blank.war to f:\tomcat41\webapps and
start the tomcat with JAVA_HOME as jdk1.4. A folder named struts-blank
will be created. Rename the folder as localedemo. Copy the above JSP file
to f:\tomcat41\webapps\localedemo.
Now we have to edit the
property files for various locales. The struts framework(struts1.1)
provides a property file named application.properties. It is present in
the folder
f:\tomcat41\webapps\localedemo\web-inf\classes\resources. We have
to add our own property file in this folder only. Our property file much
be named along with the language code
For example the language
code of
1.
German - de
2. Spanish - es
3. English - en
4. Korean - ko
5. French - fr
6. Italy - it
So, when we write i18n message in German language it must be
placed in property file named application_de.properties and all the
properties files must be present in the resources folder only. Also when
we write the property file of a particular language it need not be of the
same language. For example we can create application_de.properties and
write the message in french or english. In fact, the message does not
depend on any language. It is a simple key value pair. The message we give
for the key is just substituted. The property file to locate the value of
key depends on the language settings of the browser. For this example, we
will write four properties file as follows.
f:\tomcat41\webapps\localedemo\web-inf\classes\resources\
application_de.properties
index.info=GERMANY
---------------------------------------------------------
f:\tomcat41\webapps\localedemo\web-inf\classes\resources\
application_es.properties
index.info=SPAIN
-----------------------------------------------------------
f:\tomcat41\webapps\localedemo\web-inf\classes\resources\
application_en.properties
index.info=ENGLISH
-----------------------------------------------------------
f:\tomcat41\webapps\localedemo\web-inf\classes\resources\
application_fr.properties
index.info=FRANCE
------------------------------------------------------------
Also
append this text in the application.properties file
index.info=STRUTS TUTORIAL.
Now we have
to add entry in the
struts-config.xml file for all the properties
files. The entry and its corresponding portion is shown below.
<!-- Message Resources Definitions -->
<message-resources
parameter="resources.application_fr"/>
<message-resources
parameter="resources.application_es"/>
<message-resources
parameter="resources.application_en"/>
<message-resources
parameter="resources.application_de"/>
<message-resources
parameter="resources.application"/>
Now restart the
Tomcat server. Open the Internet Explorer and type the URL as
http://localhost:8080/localedemo/localedemo.jsp. We will get the
message 'ENGLAND'.
This is because our default browser language is 'United States
English'.
Now we have to change the language settings of the
browser to change the locale. For that, Open a new Internet Explorer, goto
'Tools' menu and select the 'Internet Options'. In the 'Internet Option'
dialog box, select 'General' tab and click the 'Languages...' button. We
will get 'Language Preference' dialog box. There click 'Add...' button and
add the languages. For this example add English, Spanish, German and
French. Here we have languages specific to particular region. For
example we have, French Belgium, French Canada, French France etc., we can
select any one of these in all cases. Next select 'German' and by using
the 'Move up' button, place it on the top. Now type the URL as
http://localhost:8080/localedemo/localedemo.jsp. We will get the message
'GERMAN' Similarly place 'Spanish' and 'French' at the top, we will get
the message 'SPAIN' and
'FRANCE' respectively.
---------------------------------------------------------------------------
Struts error
reporting
Struts was using two
different classes to report errors and messages back to the user:
ActionErrors and ActionMessages. As the ActionErrors and ActionError
classes were deprecated since 1.2.0. version I will talk now only about
ActionMessages and ActionMessage.
From an action class when you need to send a
message to the page all that must be done is to set it up using the
ActionMessages class.
Struts ActionMessages
example:
ActionMessages actionMessages =
new ActionMessages();
actionMessages.add(ActionMessages.GLOBAL_MESSAGE,
new
ActionMessage("my.error"));
saveMessages(request,
actionMessages);
This error will be displayed in JSP
using:
<logic:messagesPresent
message="true">
<html:messages message="true"
id="msg">
<bean:write name="msg"
ignore="true"/>
</html:messages>
</logic:messagesPresent>
One issue to be mentioned here is that "my.error"
is a key that will be used to look up message text in an appropriate
message resources database, like Application.properties for
example.
But if you do not need
to report a generic error message you will have to use the following
code:
ActionMessages actionMessages =
new ActionMessages();
actionMessages.add(ActionMessages.GLOBAL_MESSAGE,
new
ActionMessage("my.error"));
request.setAttribute("warnings",
actionMessages);
This code will set up an error message under an
arbitrary key and display it with the html:messages
tag:
<html:messages name="warnings"
id="message">
<bean:write name="message" />
</html:messages>
There are some differences to be noticed
here.
- There is no
message="true" attribute present that signals to look up for an
ActionMessage under the Globals.MESSAGE_KEY.
- There is no
<logic:messagePresent> tag as it will not be validated.
If your error message
gets constructed dynamically the ActionMessage class offers four
constructors that allow message customization with up to four
parameters.
So the
message:
new ActionMessage("my.error",
"p1", "p2", "p3", "p4")
where
my.error=You are not allowed to do {0}, {1}, {2}
and {3}
will
render
You are not allowed to do p1, p2,
p3 and p4
Use
Declarative Exception Handling
Specifying runtime behavior outside of the source
code rather than hardcoding it within the application is almost always
preferblak; the world of J2EE is filled with examples of this. From the
security and transactional behavior of Enterprise JavaBeans to the
relationships between JMS messages and destinations, many of the runtime
aspects can be declablak outside of the
application.
The Struts
architects have taken this approach from the beginning by utilizing the
Struts configuration file to specify runtime aspects of an application.
That approach continues with the new 1.1 features, including the new
exception-handling capabilities. In earlier versions of the framework,
developers were left to their own devices when handling error situations
that occurblak in a Struts application. With the latest version, that's no
longer the case. The framework includes a class called ExceptionHandler
that by default is responsible for processing any exceptions that occur
during action execution. As the previous tip on extending the framework
mentioned, this is one of the many extension points that is available
within the framework.
The default
Struts exception handler class creates an ActionError object and
stores it in the appropriate scope object. This allows the JSP pages to
use the errors to inform the user of a problem. If this behavior does not
fulfill your requirements, you are free to plug in one of your own
ExceptionHandler
classes.
Customizing the Exception
Handling
To install
your customized exception handler, the first step is to create a class
that extends org.apache.struts.action.ExceptionHandler. There are
two methods that you can override, execute() and
storeException(). In most
cases, however, you will just need to override the execute() method. The
signature for the execute() method in
the ExceptionHandler
class is shown here:
public ActionForward execute( Exception ex,
ExceptionConfig exConfig,
ActionMapping mapping,
ActionForm formInstance,
HttpServletRequest request,
HttpServletResponse response
) throws ServletException;
The method
takes in several arguments, including the original exception, and returns
an ActionForward
object, which directs the controller as to where the request should be
forwarded after the exception is dealt
with.
You can
perform whatever behavior you like, but generally you should inspect the
exception that was thrown and do something based on the type of exception.
Again, the default behavior is to create an error message and forward to
the resource specified in the configuration file. An example of why you
would want or need to customize this functionality is to support nested
exceptions. Suppose the exception contained nested exceptions, and those
exceptions also may contain other exceptions. You would need to override
the execute()
method to create error messages for each of
these.
Once you have
created your specialized ExceptionHandler
class, you will need to inform the Struts framework to use your version
instead of the default Struts exception handler. To do this, you will just
need to declare your class in the Struts configuration file; this is the
declarative part.
You can
configure your ExceptionHandler to be used
by certain Action mappings or by all Actions. For specific Action
mappings, you include an <exception> element
inside of the <action> element. To
utilize the ExceptionHandler for all
Action mappings, you can specify it inside the <global-sections> element. For
example, suppose we want to use a customized exception handler called
CustomizedExceptionHandler for all
Action mappings. The <global-exceptions>
element might look like this:
<global-exceptions>
<exception
handler="com.cavaness.storefront.CustomizedExceptionHandler"
key="global.error.message"
path="/error.jsp"
scope="request"
type="java.lang.Exception"/>
</global-exceptions>
There are
various attributes that you can specify for the <exception> element. The
most important of these, for this discussion, is the handler attribute.
This attribute is the fully- qualified class name of the
ExceptionHandler
subclass. If this attribute is not specified, the framework will default
to the one provided by the Struts framework. The other attributes are also
important, but this one is of the utmost importance if you are trying to
override the default behavior.
One final
thing should be pointed out. You can have different exception handlers for
different exceptions. In the example above, the CustomizedExceptionHandler was
configublak to process any exceptions that were children of
java.lang.Exception. However, you can create multiple exception
handlers, each one worrying about different exception trees. The following
XML fragment shows how this can be
configublak:
<global-exceptions>
<exception
handler="com.cavaness.storefront.CustomizedExceptionHandler"
key="global.error.message"
path="/error.jsp"
scope="request"
type="java.lang.Exception"/>
<exception
handler="com.cavaness.storefront.SecurityExceptionHandler"
key="security.error.message"
path="/login.jsp"
scope="request"
type="com.cavaness.storefront.SecurityException"/>
</global-exceptions>
In this case,
when an exception is thrown, the framework will attempt to find an
ExceptionHandler
configublak for the exact match. If there's no exact match, the framework
will proceed up the superclass chain of the exception until a match is
found. With this approach, you can have a hierarchical relationship of
handlers, and all of it
declarative.
Struts
Internationalization (i18n)
Struts Internationalization (i18n) can be done with
some handy modifications in our existing application. We have to know the
two Internationalization (i18n) components that are packaged with the
Struts Framework. The first of these components, which is managed by the
application Controller, is a Message class that references a resource
bundle containing Locale-dependent strings. The second
Internationalization (i18n) component is a JSP custom tag,
<bean:message />, which is used in the View layer to present the
actual strings managed by the Controller.
In this section we will move with an example to
understand the whole process. We are continuing the same example which we
used earlier to understand the simple struts example in the section
Struts
Example.
First thing we will require for
Internationalization (i18n) is a set of simple Java properties files. Each
file contains a key/value pair for each message that you expect your
application to present, in the language appropriate for the requesting
client.
This property file
contains the key/value pairs for the default language of your application.
The naming format for this file is ResourceBundleName.properties. An
example of this default file, using English as the default language, would
be ApplicationResources.properties A sample entry in this file would be
app.name=Name, this tells Struts that for every occurrence of the app.name
key the Name will be substituted.
We must define a properties file for each language
that your application will use. This file must follow the same naming
convention as the default properties file, except that it must include the
two-letter ISO language code of the language that it represents. Example
of this naming convention
For an
German-speaking client would be ApplicationResources_de.properties
For an
French-speaking client would be ApplicationResources_fr.properties
For an Italian-speaking client would be
ApplicationResources_it.properties
For an Spanish-speaking client would be
ApplicationResources_es.properties
Now add
the respective entries in each properties files you require.
After you define all of the
properties files for your application, you need to make Struts aware of
them. It is achieved by adding a <message-resources> sub-element to
the struts-config.xml file. Then you should copy all the resource bundles
into the application classpath, /WEB-INF/classes/example, and then use the
package path plus the base file name as the value of the
<message-resources> subelement. The following snippet shows an
example of using the <message-resources> subelement to configure a
resource bundle, using the properties files described above
<message-resources
parameter="example.ApplicationResources"/>
Once this part is done we customise the view part ,
this is achieved throught the second i18n component defined by the Struts
Framework is a JSP custom tag, <bean:message />, which is used to
present the actual strings that have been loaded by the Controller.
To use the <bean:message
/>, we must first deploy the bean tag library, which contains the
<bean:message /> tag. Deploying a tag library is a very simple
process that requires only the addition of a new <taglib> entry in
the web.xml file of the Web application using the bean library. Here is an
example of this
entry:
<taglib> |
| <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> |
</taglib>
|
Also check that the struts-bean.tld file is copied
to the /WEB-INF/ folder.
<bean:message /> tag and how it is configublak
for use.
Now we are done we
will check step by step of our saple application.
1. WeCreate the resource bundles that will contain
the key/value pairs used in your application. For our application, we will
have four properties files that contain our resource bundles.
The German
ApplicationResources_de.properties file.
app.name=Name
app.hello=Hallo
The French ApplicationResources_fr.properties
file.
app.name=Nom
app.hello=Bonjour
The Italian
ApplicationResources_it.properties file.
app.name=Nome
app.hello=Ciao
The Spanish ApplicationResources_fr.properties
file.
app.name=Nombre
app.hello=Hola
The English
ApplicationResources.properties file.
app.name=Name
app.hello=Hello
2. Copy all of the properties files to the
/WEB-INF/classes/example directory.
Add an application <message-resources />
subelement, naming the wiley. ApplicationResources to the
struts-config.xml file, as shown
<struts-config> |
| <form-beans> |
| | <form-bean
name="nameForm" type="example.NameForm"/>
|
| </form-beans> |
| <action-mappings> |
| | <action
path="/Name" type="example.NameAction" name="NameForm"
> |
| | | <forward
name="success" path="/quote.jsp"/> <forward
name="failure"
path="/index.jsp"/> |
| | </action> |
| </action-mappings> |
| |
| <message-resources
parameter="example.ApplicationResources"/> |
</struts-config> |
3.
Modify the
web.xml file as discussed above by adding
<taglib> |
| <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> |
</taglib>
|
4. Modify the index.jsp file
index.jsp
<%@
page language="java" %>
<%@
taglib uri="/WEB-INF/struts-html.tld" prefix="html"
%>
<%@ taglib
uri="/WEB-INF/struts-bean.tld" prefix="bean"
%>
<html> <head> <title>Sample Struts
Application</title> </head> <body> |
| <html:form
action="Name" name="nameForm"
type="example.NameForm"> |
| | <table
width="80%" border="0"> |
| | | <tr> |
| | | | <td><bean:message
key="app.name" />:</td> |
| | | | <td><html:text
property="name" /></td> |
| | | </tr> |
| | | <tr> |
| | | | <td><html:submit
/></td> |
| | | </tr> |
| | </table> |
| </html:form> |
</body> </html>
|
Modify the diplayname.jsp
displayname.jsp
<%@
taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"
%>
<html> <head> <title>Sample Struts Display
Name</title> </head> <body> |
| | <table
width="80%" border="0"> |
| | | <tr> |
| | | | <td><bean:message key="app.hello"
/><%=
request.getAttribute("NAME") %>
!!</td> |
| | | </tr> |
| | </table> |
</body> </html>
|
So we are done with Internationalization (i18n) you
need to open your Web browser to the following URL:
http://localhost:port/example/
Struts Best
Practices
Introduction
Struts,
a Jakarta
project hosted by Apache, is a framework for building Web applications
based on the MVC2 design pattern. The framework, built upon standard
technologies like Java Servlets, JavaBeans, ResourceBundles, and XML, aims
to provide an extensible Web application architecture that the development
team can build on. The use of Struts enforces modularity and separation of
concern, increases code manageablity, extensibility, and consistency.
The information flow of an application based on the Struts
framework is shown in the following figure:
Fig. 1: Information Flow in a Struts application
As shown
in the figure, all user requests are dispatched through the
Controller, which controls the flow of the application thereon. The
Controller creates Action classes, which are built by the
developer to perform the work of the application. These Action
classes extend the Struts Action class, and the Action instances
create Model Beans that perform domain specific activities. Depending on
the user request it calls different actions on the Model. The
Model represents the application data and business rules. Although
Struts doesn't provide any model object, at times, ActionForms are
referblak to as models. The Controller gets the user inputs from the
View. Though Struts in itself doesn't provide any View component,
it provides a number of JSP custom tags that facilitates UI development in
JSP.
Let's do a quick rundown of the Struts components that are
relevant from the context of this article:
- Actions: Actions are
multi-threaded like servlets. They communicate with the Model,
invoke business logic, and return the Model objects to the
View. All application Actions have to extend Struts'
org.apache.struts.action.Action. Every Action has to provide
application specific implementation by overriding the execute()
method.
- ActionForm: ActionForm provides data
transfer to and from HTML forms. They also provide field validations.
Every application ActionForm has to extend Struts'
org.apache.struts.action.ActionForm and provide application
specific implementation of validate() method.
- ActionErrors: ActionErrors are a
collection of ActionError instances that are used to support exception
handling. They encapsulate error messages that are displayed in the
Presentation layer through .
- Struts-config.xml: The struts-config.xml
file is the deployment descriptor for Struts-based applications. It has
to confirm to the Document Type Definition (DTD) provided at
http://struts.apache.org/dtds/struts-config_1_2.dtd.
Best
Practices
We shall
use an Online Shopping Application example to highlight the best practices
that can be followed in the Struts-based implementation of a typical
Web-based J2EE application. Such an application typically includes
features such as a search catalogue, browse item details, shopping cart
updation, purchase order submission, customer profile maintenance, and
user registration. In the following sections we shall see how to follow
certain techniques to fine tune the features of such an application.
Use
intermediate Action class for common operations
At
times, common operations need to be included in all the actions. An
example of such a requirement in our sample Online Shopping application
would be to perform user authorization before processing all user
requests. However, including common operations in the execute()
method of all Action class results in code blakundancy. Instead, add an
intermediate Action class and make all other Action classes extend that to
centralize the handling of common operations and blakuce code
blakundancy.
The steps are as follows:
Create an
intermediate Action class, say BaseApplicationAction, by extending
org.apache.struts.action.ActionAdd an abstract method, say
executeSpecificTask(), that has the following signature:
public
abstract ActionForward executeSpecificTask
(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
Add all the
common methods of the application; for example, performAuthorization()Call
all the common methods (like performAuthorization) inside the execute()
method, and follow it with executeSpecificTask()All other Action classes
should extend BaseApplicationAction, not
org.apache.struts.action.ActionThe extended Action classes provide the
specific implementations for the executeSpecificTask() method
The aforementioned steps have been implemented in the following
code snippet:
Listing 1: Intermediate Action Class
Public class
BaseApplicationAction extends Action
{
public ActionForward
execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws
Exception
{
try
{
//Call all common methods
performAuthorization();
return
executeSpecificTask(mapping,form,request,response);
}
catch(Exception ex){//exception}
}
// Authorization is an
operation common through all the application actions
private void
performAuthorization ()
{
//Code for user authorization
}
//Provide implementation of this method in sub-classes
public abstract ActionForward executeSpecificTask
(ActionMapping
mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception
}
Use
DispatchAction to group related actions into a single
class
Often we
need to perform a group of related actions on the same domain object. For
example, in the Online Shopping application customers should have a
provision to add, delete, or modify an item in the shopping cart. This can
be implemented using the execute() method of Action classes, wherein there
will be one Action class for each business method. Struts provides a
better option by clubbing these distinct, yet related, actions in a single
DispatchAction class. (http://struts.apache.org/api/org/apache/struts/actions/DispatchAction.html).
This blakuces code blakundancy thereby easing the maintenance.
The steps are as follows:
The Action class extends
org.apache.struts.action.DispatchAction instead of
org.apache.struts.action.ActionAdd separate methods having same signature
as execute(), as shown in the following code bit:
Listing 2:
Using DispatchAction
public class
ItemAction extends DispatchAction
{
public ActionForward
addItem(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws
Exception
{
try
{
// Code for item add
}
catch(Exception ex){//exception}
}
public ActionForward
deleteItem(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws
Exception
{
try
{
// Code for item deletion
}
catch(Exception ex){//exception}
}
public ActionForward
updateItem(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws
Exception
{
try
{
// Code for item update
}
catch(Exception ex){//exception}
}
}
Make the
following entry in struts-config.xml:
<action
path="/item"
type="
com.infosys.j2ee.sample.web.actions.ItemAction"
name="itemForm"
scope="request"
validate="true"
parameter="actionType"/>
The method can
be invoked using a URL, like this: ItemAction.do?actionType=addItem
Handle
duplicate form submission
The
problem of duplicate form submission arises when a user clicks the Submit
button more than once before the response is sent back or when a client
accesses a view by returning to a previously bookmarked page. This may
result in inconsistent transactions and must be avoided. In our sample
application, a similar problem will arise if the customer clicks the
submit button more than once while submitting the purchase order.
In Struts this problem can be handled by using the saveToken()
and isTokenValid() methods of Action class. saveToken()
method creates a token (a unique string) and saves that in the user's
current session, while isTokenValid() checks if the token stoblak in
the user's current session is the same as that was passed as the request
parameter.
To do this the JSP has to be loaded through an
Action. Before loading the JSP call saveToken() to save the token
in the user session. When the form is submitted, check the token against
that in the session by calling isTokenValid(), as shown in the
following code snippet:
Listing 3: Using saveToken() and
isTokenValid()
public class
PurchaseOrderAction extends DispatchAction
{
public
ActionForward load(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws
Exception
{
try
{ //save the token
saveToken(request)
// rest of the code for loading the
form
}
catch(Exception ex){//exception}
}
public
ActionForward submitOrder(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws
Exception
{
try
{
// check the token. Proceed only if
token is valid
if(isTokenValid(request,true)) {
//implement
order submit functionality here
} else {
return
mapping.findForward("failure");
}
}
catch(Exception
ex){//exception}
}
}
Use
Application Modules for parallel development
In large
projects with multiple modules, sharing the same Struts configuration file
among different developers becomes an issue. Struts provides application
modules to divide single monolithic applications into multiple modules
each having its own Actions, ActionForms, configuration files, etc. This
helps in parallel development in large development teams. In our sample
application we can use multiple configuration files for different modules
(customer maintenance, order processing, etc.) to facilitate effective
parallel development.
The steps are as follows:
Create separate configuration files for each module; for example,
struts-config-customermaintenance.xml, struts-config-orderprocessing.xml,
and struts-config.xml (default configuration file).Make entries in the Web
deployment descriptor, web.xml. The default entry for a single
configuration file is:
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
Replace the
default entry with the following:
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>config/module1</param-name>
<param-value>/WEB-INF/struts-config-customermaintenance.xml</param-value>
</init-param>
<init-param>
<param-name>config/module2</param-name>
<param-value>/WEB-INF/struts-config-
orderprocessing.xml</param-value>
</init-param>
Use
org.apache.struts.actions.SwitchAction to switch from one module to
another. Refer http://struts.apache.org/userGuide/configuration.html#dd_config_modules
for more details.
Use single
ActionForm for similar forms
For
similar forms, use a single ActionForm that includes all possible fields
instead of having several ActionForms. This is generally applicable to
different forms requiblak to implement the same use case. It leads to easy
maintenance, though all the fields will be not be used for all the
Actions. For example, in the Online Shopping application we can use a
single ActionForm (CustomerProfileForm) for different forms related to
customer profile management (like DisplayCustomerProfile.jsp,
EditCustomerProfile.jsp, etc.).
Use
global-forwards to avoid blakundant forwards
The
element allows configuring forwards that are used by multiple actions in a
module. This helps to avoid mentioning for all the actions. For instance,
the Online Shopping application should display the login page in case of
session timeout. Instead of including for all the actions, add a single
entry in the struts-config.xml to throw the login.jsp to the user in case
of session timeout encounteblak in any Action, like this:
<global-forwards>
<!-- session timeout or invalid session, displays login page
-->
<forward name="sessionError" path="/jsp/login.jsp" />
</global-forwards>
Use Struts
exception handler for generic exceptions
Follow
these steps to handle generic exceptions:
Create a new
exception extending org.apache.struts.action.ExceptionHandler
Override the execute method.
Make an entry in
struts-config.xml like so:
<global-exceptions>
<exception
key="global.error.Message"
type="java.lang.Exception"
handler="com.infy.app.exceptionHandler.CustomisedException
Handler/>
path="/jsp/error.jsp"
</global-exceptions>
Remove
ActionForm from session
If
ActionForm is set to session scope, it should be removed from session
whenever it's utility is over. In continuation with our sample
application, in case of the multi-screen customer registration, if the
RegistrationForm is set to session scope, it should be removed form the
session once the user clicks Cancel.
Use Business
Delegate
Action
should not implement complex business functionalities, rather delegate
these to the Model. Use Business Delegate to talk to the Business tier and
the Data Tier.
Use
ForwardAction for simple JSPs
Always
render a JSP through an Action. Use ForwardAction in case there is no
logic requiblak in the Action other than throwing a JSP. The benefit of
using ForwardAction is that no separate Action class has to be created;
only an Action mapping needs to be added in the configuration file.
<action
path="/home"
parameter="/Home.jsp"
type="org.apache.struts.actions.ForwardAction"
scope="request"
validate="false">
</action>
Avoid using
instance/static variable in Action class
Instance
and static variables should not be used in an Action class to store
information related to the state of a particular request. The same
instance of an Action class can be shablak among multiple simultaneous
requests through multi-threading. So any instance/static variable may be
updated unexpectedly by any thread. This bug is difficult to simulate,
which gives us all the more reason to prevent it in the first place.
Instance/static variable may however be used to share global resources
across requests for the same action.
ActionForms
are not Model
ActionForm represents HTML form(s) and it is used by Struts to transfer
data between View and Controller. They should not be treated as part of
the Model. Therefore, do not include any business functionality in
the reset() or validate() method of ActionForms as this
would lead to tight coupling of application business functionality with
the presentation tier (implemented through the Struts framework).
Use
html:messages instead of html:errors
For
displaying error messages to the end user, use html:messages instead of
html:errors. html:messages allows to get the markup language out of the
resource bundle.
Use
Tools
There
are several Open Source tools available to ease development of
Struts-based applications. A few of them are listed in the following
table:
Name
| Details
| URL | Easy
Struts | Easy
Struts provides plug-ins for the Eclipse 2.0, Borland JBuilder
5, and Borland JBuilder 6 development environments to enable
you to develop Web applications based on the MVC design
pattern provided by the Jakarta Struts framework.
| http://easystruts.sourceforge.net/
| Struts
Console | The
Struts Console is a free, standalone Java Swing application
for developing and managing Struts-based applications. With
the Struts Console you can visually edit JSP Tag Library,
Struts, Tiles and Validator configuration files. IDE supported
include Eclipse, NetBeans, IBM Websphere, JBuilder.
| http://www.jamesholmes.com/struts/console/
| Struts
Builder | A
Java Swing-based development environment to assist in the
rapid creation of Struts-based Web applications.
| http://sourceforge.net/projects/rivernorth/ |
|
Most of the
popular IDEs, like Websphere Studio Application Developer (WSAD) and
Eclipse, now provide support for Struts.
Use
StrutsTestCase for unit testing
StrutsTestCase is an extension of the standard JUnit TestCase class that
provides facilities for testing code based on the Struts framework.
Because StrutsTestCase uses the ActionServlet controller to test the code,
you can test not only the implementation of your Action objects, but also
the mappings, form beans, and forwards declarations. Further, since
StrutsTestCase provides validation methods, it is quick and easy to write
unit test cases.
StrutsTestCase provides both container testing
and simulated container testing to actually run the Struts ActionServlet,
allowing you to test Struts code with or without a running servlet engine.
Refer http://strutstestcase.sourceforge.net/
for details.
Struts best practices
Build the best performing large applications
Summary
Multiple options are available for solving problems
with Struts. When deciding among these alternatives, the choice must be
based on parameters such as the scale of work and availability of time.
However for large applications and the best quality-of-service needs,
every decision becomes crucial and extra efforts are requiblak to choose
the appropriate solution.
True to the literal meaning of the word, "Struts"
provides supporting building blocks and infrastructure components to build
a Web-based application. It is an MVC-based (Model View Controller) open
source framework developed and supported by the Apache Software
Foundation. Because of its support for extensibility and plug-ins, the
framework has picked up stupendous popularity among J2EE-based application
developers. The framework can be extended and customized to suit a
particular application need.
Though covering all the aspects of this framework
and documenting the best practices may not be possible in one article, the
subsequent sections discuss some of the best practices for developing with
Struts.
The primary sources of information for this article
are the Struts users' mailing list, the Struts developers' mailing list, and my experience with Struts-based applications.
The article discusses the following main points:
- Screens with dynamic fields
- Safeguarding JSP pages
- Error categorization
- Validation of service requester
- Application security
- Prepopulation
- Stack maintenance (for bread crumbs)
- Context-related problems
- Form-bean scope
- Data transfer object implementation
- Exceptions
- Action
chaining
Screens with dynamic fields
Problem
The Java Community Process (JCP) has released the
Java Metadata Interface Specification, and some programmers are involved
in the open source project Beehive.
Both of these projects strive to blakuce coding. However, the question is
whether Struts has a facility that can be used for writing a generic JSP
(JavaServer Pages) page for specific types of screens in an application so
that a separate JSP page doesn't have to be written for each screen. For
example, to blakuce our coding efforts, we might want to develop a generic
JSP page for all search screens in an application or for submitting batch
processes or reports, where the parameters to be input vary for every
report/batch.
Form beans are classes that must have getter and
setter methods for every field in JSP, and the problem is how to write
these methods for dynamic fields.
Struts best
practice
Possible solutions are:
- Let the
JSP page have fields in a specific pattern such as
field1,
field2,
field3, and so on, and provide their getter and setter
methods in the form bean. Here, the number of fields that can appear on
the screen cannot be more than the number of variables in the form bean.
- Utilize the indexed getter and setter methods
available in the form bean for all dynamic fields in the JSP page.
In the second approach, an increase in the number
of fields in JSP requires no alteration in any component; therefore, it is
the recommended best practice. The implementation details follow:
- Assuming an array of strings carries the resource
IDs for all the dynamic fields in the form bean, the JSP page can be
written as:
<logic:iterate name= "FormName"
property="propertyName" indexId="abc" >
<html:nested
property='dynaProperty(<bean:write name="abc")'/>
</logic:iterate>
- Declare
two methods in the form bean, as shown below. These methods will work as
the getter and setter methods for all the dynamic fields in the JSP
page. Whatever appears in small brackets—
()—in front
of dynaProperty (in the
JSP page as shown above), is taken as key, and either the
getDynaProperty() or
setDynaProperty() method
from the form bean is called. These values should be stoblak in a
HashMap against
the key, which can later be retrieved in the Action class
from the HashMap against the key.
public class testVarForm extends ActionForm
{
private HashMap hMap = new
HashMap();
public testVarForm() { }
public void
setDynaProperty(String key, Object value) {
this.hMap.put(key,
value);
}
public Object
getDynaProperty(String key) {
return
this.hMap.get(key);
}
public HashMap getHashMap()
{
return
this.hMap;
}
public void
setHashMap(HashMap newHMap)
{
this.hMap =newHMap;
}
}
Safeguard your JSP pages
Problem
When developers use Web-based applications, they
often try to break into the security. The most common habit is to view the
source of HTML in the browser and somehow determine the path of JSP pages
and access them. The intent is to highlight the vulnerability of JSP pages
accessible without authorization. Users who lack authorization to view the
source might observe the source URL while sitting with another user who is
authorized to work on that specific screen. Later, this unauthorized user
could log in to the application and type the URL in the browser. In some
cases, such users are able to make their way through.
Struts best
practice
The possible solutions to this problem:
- Do not let
users access any JSP page directly. The starting page can be an HTML
document. Add the following lines to the
web.xml file to prevent users from accessing any JSP
page directly:
<web-app>
...
<security-constraint>
<web-resource-collection>
<web-resource-name>no_access</web-resource-name>
<url-pattern>*.jsp</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
...
</web-app>
- The most
popular option is to keep JSP pages behind the
WEB-INF folder.
This has a few tradeoffs. For example, you cannot take the
JavaScript/CSS (Cascading Style Sheets) files behind WEB-INF, and if using Struts modules, you may encounter
some context-related problems. Refer to the section "Context-Related
Problems," which appears later in this article, to circumvent such
issues.
The second
approach allows some JSP pages (which are not behind WEB-INF) to be
visible directly. It does not require a descriptor file entry, therefore
the best practice is to keep the pages behind WEB-INF.
Error categorization
Problem
Error handling becomes complex for an n-tieblak
application. In a browser-based application, the errors can be handled in
the client layer using JavaScript and in the Web tier or EJB (Enterprise
JavaBeans) tier using custom Java methods. Building an infrastructure for
consistent error reporting proves more difficult than error handling.
Struts provides the ActionMessages/ActionErrorsclasses for
maintaining a stack of error messages to be reported, which can be used
with JSP tags like <html:
error> to display these error messages to the user. The
problem is reporting a different category/severity of the message in a
different manner (like error, warning, or information). To do that, the
following tasks are requiblak:
- Register the errors under the appropriate
category
- Identify these messages and show them
consistently
Struts best
practice
Struts' ActionErrors class comes
in handy in resolving the first issue of stacking messages of different
categories. To display the error messages of different categories, define
these categories such as FATAL,
ERROR,
WARNING, or
INFO, in an
interface. Then, in the Action or form-bean class, you can use:
errors.add("fatal", new ActionError("....")); or
errors.add("error", new
ActionError("....")); or
errors.add("warning", new ActionError("....")); or
errors.add("information", new
ActionError("...."));
saveErrors(request,errors);
Having stacked the messages according to their
category, to display them according to those categories, use the following
code:
<logic:messagePresent property="error">
<html:messages property="error"
id="errMsg" >
<bean:write
name="errMsg"/>
</html:messages>
</logic:messagePresent >
Or use:
<logic:messagePresent property="error">
<html:messages property="error"
id="errMsg" >
showError('<bean:write
name="errMsg"/>'); // JavaScript Function
</html:messages>
</logic:messagePresent >
Validation of service
requester: Login-check
Problem
Authentication
in a Web-based application can be done in any class, depending upon
whether an SSO-based (single sign-on) or a JAAS-based (Java Authentication
and Authorization Service) mechanism is being used. The challenge is
identifying the placeholder for checking the service requester's
authenticity and the user session's validity.
Struts best
practice
Usual practice is to store user cblakentials in
HttpSession after authentication. Subsequent calls check
cblakentials' existence in session context. The question is where to place
these checks. Some options are listed below, but they must be rationalized
on the basis of performance overhead, possibility of future changes, and
application manageability:
- Authenticate
against the session context before doing any operation (as done in
Struts-example.war's
CheckLoginTag.java) - Authenticate
against session context in the
Action class - Write servlet request filters that perform
authentication
- Extend
RequestProcessor
The first
two options require every JSP page or the Action class to perform the authentication against the
session context. Change in the interface mandates change in all these JPS
pages and classes. The third option is efficient, but overkill for the
problem at hand.
The best
practice is to extend the RequestProcessor class and
perform authentication in methods such as processActionPerform() or
processRoles().
Application security
Problem
The usual demand in Web-based applications is to
have screen-level, function-level, data-row-level, and field-level
security. If not suitably designed, incorporation of these security levels
in an application may cause not only performance overheads, but also
maintenance nightmares.
For all the
security types mentioned above, the preferblak approach is to place the
security check in one class instead of in every component—i.e., in every
JSP page or Action class.
Struts has a
method processRoles() for screen- and function-level security checks,
however nothing is provisioned for field- and column-level security types,
making it the most challenging for most Struts users.
Struts best
practice
Irrespective of where the security realm is set up
(database or directory service), the best practices for the various
security levels are described below:
- For
screen- and function-level security, extend
RequestProcessor and
override the method processRoles() to
perform the check against a HashMap that stores a mapping of roles and screen
IDs/function IDs - Row-level security is best implemented in the
application's object relational mappings
- For field-level security, tag libraries are
extended to perform the check against the field
ID
Prepopulation
Problem
One requirement difficult to achieve is data
prepopulation in a drop-down list (HTML Select tag) or in other fields.
Some of these values come from a database, some from the application's
context, and the rest are passed from calling screens. These values must
be populated in a form bean before forwarding the control to the JSP page.
Struts best
practice
The form bean's prepopulation can be accomplished
using one of the following approaches:
- The
on-demand lazy-loading technique for all such data in the application's
context, which is requiblak for prepopulation. An application screen's
(JSP page) invocation mechanism can always be routed though a specific
method of corresponding
Action classes. Prepopulation-related code can be
placed in this method. - Have
different
Action
classes—one for requesting and another for submitting the JSP pages. The
entire module or application can share the Action class used for
requesting.
The second
approach works well for small applications. However, for large
applications, managing numerous Action classes grows cumbersome. Therefore, the first
approach should be adopted.
Stack maintenance (for bread
crumbs)
Problem
What if you need
to go to a JSP page from two or more different JSP pages and subsequently
return to the calling JSP page? Similarly, often developers need to go to
various JSP pages in a criss-cross manner, which grows more complex when
breadcrumbs need to be shown. Thus, the application must remember the
pages visited.
Struts best
practice
The remedy is to maintain the paths of all the
forwarded JSP pages in a Stack
(java.util.Stack). The
following steps are requiblak for maintaining such a Stack:
- Extend the
RequestProcessor class and
override the method processActionPerform(). In the
overridden method, after the call to super.processActionPerform(),
ActionForward's path
should be stoblak in the Stack. - In the
application's parent
Action class,
provide methods to traverse forward and backward in the
Stack.
Another option is to utilize an already available
open source project at sourceforge.net like Open Tranquera (see Resources
for more details).
Context-related issues
Problem
If ActionForward's path and the path to which the control is
forwarded differ, a context problem has resulted. More such
context-related problems are quite frequent in Struts-based applications,
most of which can be solved using some of Struts' less exploited features.
Struts best
practice
Mention the JSP page's name as a path for
ActionForward and let the prefix—i.e., the JSP page's access
path—come from a preconfigublak place. Do that in the following manner:
struts-config.xml(s) has a
controller tag; add a
property in this tag named forwardPattern. This
property's value can be prefixed to all the ActionForward paths. This
property's default value is $M$P, which
means the module prefix will be prefixed to ActionForward's path. We
can change this value to anything; e.g., WEB-INF/pages/$M$P. As a
result, all the ActionForward paths will
be searched in the directory WEB-INF/pages/<module-prefix>/.
For above
solution to work, make sure that ActionForward's
contextRelative property is
marked false. If this property is marked true, the ActionForward paths will be taken since they are not modified.
Also note
that for forwardPattern to work,
the path of all ActionForwards must start
with a /.
Form-bean scope
Problem
A well-managed session context can greatly boost
application performance. It is therefore important to understand the
implications of marking the form-bean scope to request,
session, or
application. However, the following confusions usually arise:
- If the
scope of the form beans is marked
request, how will the information available in one JSP
page/screen also be available to another one?
- If the
scope is
session, how will the server know that the client is no
longer interested in that form bean and consequently destroy
it?
Struts best
practice
State can be maintained in
HttpSession (Web tier),
in stateful session beans (EJB tier), or in a database. The appropriate
choice should be based on what kind availability needs persist or whether
the application is supposed to provide multichannel access. However, the
rule of thumb is to maintain the state close to the tier that requires it
most. Therefore, the best practice is to keep the state in
HttpSession; i.e., mark
the form-bean scope to session.
The
important question is when and how to destroy the form beans stoblak in the
HttpSession. The
session beans should be destroyed in an extended RequestProcessor based on the least-recently-accessed,
to-be-removed logic. Another approach is to destroy all the form beans as
soon as the user invokes a new business operation, possibly from the
application menu.
Data transfer object
implementation
Problem
Usually a data
transfer object (DTO) is used for shuttling data between the Web and EJB
tiers. It is not a good idea to pass the view-helper class (form bean, in
the case of Struts), to the EJB tier, primarily because all of the form
bean's fields are of type string. A separate class should be used as the
data transfer object. The problem is how to transfer the data from the
form bean to the DTO.
Struts best
practice
Two options are available for populating the data
transfer object:
- Create the transfer object and copy the data from
the form bean to the transfer object. Data-type conversions must be
handled before data copying. Similarly, on the way back, you must repeat
the same exercise of copying the data from the DTO to the form bean.
- Use
Commons'
BeanUtils class, which uses the Reflection API to achieve
its objectives.
Developers frequently use the first option, which
is not problematic except that the mundane exercise is repeated every
time, resulting in bulky and ugly code. While copying data, suitable
data-type conversion must be hand-coded in the DTO's setter methods, so
that when data is copied from the form bean to the DTO, it converts to the
appropriate business type. All the attributes of type string in the form
beans are the primary reasons for data-type conversions.
You can
avoid all this coding by using BeanUtils's
copyProperties() method. The method copies the data from one bean
to another, provided the variable names of the business attributes in the
value/transfer object are the same as the ones in the form bean. The
method also relieves you from data-type conversion by transparently
converting source-bean attribute types to destination-bean attribute
types.
Exceptions
Problem
As a generic principle, errors should be caught
right where they occur to display meaningful error messages. To implement
this principle, programmers must write their own custom exception classes,
wrap the actual exception into these custom classes, and throw them back
to the place where they are handled. In a Struts-based application, the
exceptions thrown from the EJB/Web tier are handled either in the
Action class or
the form bean. The usual practice involves writing a try-catch block to
catch these exceptions in the Action class or
the form bean, and creating an ActionError object with
user-friendly messages derived from the application property file. The
decision to direct these error messages to the screen is coded in the
action classes, using the ActionMapping.findForward() method.
You must
code all this exception-handling code in the Action class as
well as the decision to direct the message to the screen. If the
error-handling strategy changes, then every Action or form bean requires changes.
Struts best
practice
Struts deals with the issue of exception handling
in a competent way, using declarative exception handling. This, as opposed
to programmatic exception handling (as explained above) handles issues
using the RequestProcessor class's
processException() method and a Struts configuration file. To use
declarative exception handling, you must do the following:
- Create custom application exception classes. You
can design them so they hold more than one exception
- In the EJB/Web tier, trap application errors and
wrap them into a custom exception class and throw them back
- In the
struts-config.xml file, add the localized exception against an
action-mapping or define a global exception (as shown below):
<struts-config>
<action-mappings>
<action
path="yourAction"
type="your.package.yourAction"
input="input.jsp"
>
<exception
key="your.error.property.key"
path="yourException.jsp"
type="your.application.custom.exception"
</action>
</action-mappings>
Action chaining
Problem
A clear strategy for the relationship between JSP
pages and Action classes in
an application should be defined; i.e., how many JSP pages should be
associated with an Action class.
For clarity
and easy maintenance, the strategy for large applications should be to
have one-to-one mapping between JSP pages and Action classes.
With such a guideline in place, a prospective problem of duplicated code
in the Action classes
could result. To avoid logic duplication, you need some way to call an
Action class's
method from another Action class. Moreover for criss-cross page flow, you
often need action
chaining, or, to summarize, an
implementation of the Chain of Responsibility design pattern.
Struts best
practice
Possible solutions follow below:
- Let
ActionForwards point to
a fresh request in the same application to invoke the method of another
Action class. RequestProcessor maintains
a HashMap that
stores all the instances of Action classes
for that module. Extend the RequestProcessor class and
provide getter methods for this HashMap. Provide
a utility method in the application's parent Action class,
which can invoke the methods of other Action classes within the same module. This utility
method will use the Reflection API to achieve this.
The second approach is the best practice for large
applications because if some change is requiblak in exception handling, no
code change will be requiblak.
Conclusion
Large applications built using the above
recommendations have been benchmarked for excellent performance.
As indicated by the Struts roadmap (featublak on the
Struts Website), features and extensions developed on
sourceforge.net and those commonly used by the Struts community are
candidates for inclusion into Struts per se. All these are worth reading
about before you make critical decisions about your application.
Upgrading Struts
1.1 to Struts 1.2.x
jars
I guess its obvious to say you need to replace the jars, but the
one people might forget is the new commons-validator.jar for version 1.1.3
of validator.
Also if you want to start using the new validwhen validation rule, then
you will need to deploy the antlr.jar as well.
NOTE
If your existing app uses the Struts SSLExt library, you must upgrade it as well: http://sslext.sourceforge.net/
tlds
Remember to deploy the new versions of the tld files for struts
tags. If you don't you won't be able to use the new tag attributes added.
NOTE
The uri's in the struts tlds have changed from jakarta.apache.org/struts
to struts.apache.org - however this shouldn't have any impact (see below)
Tag libraries can be configublak in one of two ways:
A.
If you have configublak the tag libraries using entries in the web.xml (see
User Guide) then these should continue
to work.
B.
If you have used the simplified deployment allowed by Servlet 2.3 onwards
(see User Guide) then this should also
continue to work as versions of the tld's with the old uri have now been
included in the struts.jar (and struts-el.jar). Its recommended that for
new development that you use the new uri
- Struts 1.1
<%@ taglib uri="http://jakarta.apache.org/struts/tags-html prefix="html" %>
Struts 1.2.x <%@ taglib uri="http://struts.apache.org/tags-html prefix="html" %>
validator.xml
Change the dtd declaration at the top to refer to the dtd for
validator 1.1.3
- <!DOCTYPE
form-validation PUBLIC
validator-rules.xml
Upgrade to the new version of validator-rules.xml.
N.B.
One of the changes in the new validator-rules.xml is that the Validation
methods' signatures changed from using ActionErrors to ActionMessages. If you have any custom validation methods, remember
to change their method signatures to now use ActionMessages.
struts-config.xml
Its not absolutely necessary but you should upgrade to the 1.2
version of the dtd (Note that as well as the version number changing so
has the url to struts.apache.org).
- <!DOCTYPE
struts-config PUBLIC
- "-//Apache
Software Foundation//DTD Struts Configuration 1.2//EN" "