Section 4: Handling Request Parameters with Form Beans

Automatically Creating Java Objects to Represent Query Data

Alert: the HTML version of this tutorial is no longer being actively maintained, and still covers Struts 1.1. For additional topics, updates for Struts 1.2, and complete source code, please see Jakarta Struts Tutorial: Using Form Beans (in PDF).

4.1 The Six Basic Steps: Recap 4.2 Example: Form and Result Beans
4.3 Example: Properties Files 4.4 Source Code
Struts Tutorial Table of Contents More Info

This section explains how to use form beans to represent the request parameters that are part of the form submission. It also shows how to use regular beans (as in regular MVC implemented with RequestDispatcher) to represent results.

4.1 The Six Basic Steps in Using Jakara Struts: Recap

With Struts, the normal processing flow is that a form submits data to a URL of the form blah.do. That address is mapped by struts-config.xml to an Action object, whose execute method handles the request. One of the arguments to execute is a form bean that is automatically created and whose properties are automatically populated with the incoming form data. The Action object then invokes business logic and data-access logic, placing the results in normal beans stored in request, session, or application scope. The Action uses mapping.findForward to return a condition, and the conditions are mapped by struts-config.xml to various JSP pages. The system forwards the request to the appropriate JSP page, where the bean:write tag is used to output properties of the form bean and results beans. Optionally, both the input form and the final JSP pages can use bean:message to output standard messages and labels as defined in a system properties file.

The previous section outlined the six basic steps needed to implement this process, and that section gave examples that illustrated four of the steps (the ones not involving beans). This section provides two additional examples that illustrate more complete versions of each of the steps. Here is a summary of the steps once beans are added to the process.

  1. Modify struts-config.xml.
    In addition to the action and forward elements used to specify the Action object and destination URLs, we use the form-bean element to declare form beans. We also add name and scope attributes to the action element to tie the form bean to the Action. Optionally, we add a message-resources element to declare a properties file containing standard messages, names, and labels.
  2. Define a form bean.
    This bean normally extends ActionForm and has properties (i.e., getter and setter methods) corresponding to each of the incoming request parameters. Alternatively, the bean can extend DynaActionForm, in which case it will contain a Map representing the request parameters.
  3. Create results beans.
    These are normal beans of the sort used in MVC when implemented directly with RequestDispatcher. That is, they represent the results of the business logic and data access code. These beans are stored in request, session, or application scope with the setAttribute method of HttpServletRequest, HttpSession, or ServletContext, just as in normal non-Struts applications.
  4. Create an Action object to handle requests.
    Rather than calling request.getParameter explicitly as in the previous examples, the execute method casts the ActionForm argument to the specific form bean class, then uses getter methods to access the properties of the object.
  5. Create form that invokes blah.do.
    This form can use the bean:message tag to output standard messages and text labels that are defined in the properties file that is declared with message-resources in struts-config.xml.
  6. Display results in JSP.
    The JSP page uses the bean:write tag to output properties of the form and result beans. It may also use the bean:message tag to output standard messages and text labels that are defined in the standard properties file.

4.2 Example: Form and Result Beans

In this example, the URL http://hostname/struts-test/actions/register3.do should be handled by the class RegisterAction3. Instead of reading form data explicitly with request.getParameter, the execute method of RegisterAction3 uses a bean that is automatically populated from the request data. As in the previous example, this method returns "success", "bad-address", or "bad-password", and these return values result in the JSP pages /WEB-INF/results/result3.jsp, /WEB-INF/results/bad-address3.jsp, and /WEB-INF/results/bad-password3.jsp, respectively. However, instead of displaying static HTML as before, these pages display specific values that RegisterAction3 creates and stores in beans.

There are three major new features in this example:


  1. Modify struts-config.xml.
  2. Define a form bean.
  3. Create results beans.
  4. Create an Action object to handle requests.
  5. Create form that invokes blah.do.
  6. Display results in JSP.
  7. Final results for this example.

  1. Modify struts-config.xml.
    Use WEB-INF/struts-config.xml to:
    • Designate Action classes to handle requests for blah.do. As before, we use the action element.
    • Specify the URLs that apply in various situations. As before, we use multiple forward elements within the action element.
    • Declare any form beans that are being used. First, we need a form-bean entry (within the form-beans entry) with the following two attributes:
      • name: a name that will match the name attribute of the action element.
      • type: the fully qualified classname of the bean.
      Here is an example:
      <form-beans>
        <form-bean name="userFormBean"              type="coreservlets.UserFormBean"/> </form-beans>

      After declaring the bean with form-bean, we need to add two new attributes to the action element. In addition to the path and type attributes used in the earlier examples, we use:
      • name: a bean name matching the name in the form-bean declaration.
      • scope: one of the standard MVC scopes (request or session -- application scope is not supported) to be used for storing the bean. Since we will not be redisplaying the input form or populating the form bean in multiple steps (both of which would require session scope), request scope is most appropriate in this example.
      Here is an example.
      <action path="/actions/register3"
              type="coreservlets.RegisterAction3"
              name="userFormBean"
              scope="request">

    Here is the resultant struts-config.xml (download the source code here).
    struts-config.xml (Excerpt)
    <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC ... > <struts-config> <form-beans> <form-bean name="userFormBean" type="coreservlets.UserFormBean"/> </form-beans> <action-mappings> ... <action path="/actions/register3" type="coreservlets.RegisterAction3" name="userFormBean" scope="request"> <forward name="bad-address" path="/WEB-INF/results/bad-address3.jsp"/> <forward name="bad-password" path="/WEB-INF/results/bad-password3.jsp"/> <forward name="success" path="/WEB-INF/results/result3.jsp"/> </action> </action-mappings> </struts-config>
  2. Define a form bean.
    A form bean is a Java object that will be automatically filled in with the incoming form parameters and passed to the execute method. A form bean has the following characteristics.
    • It must extend ActionForm. The argument to execute will be of type ActionForm You should cast the value to your real type, and then each bean property has the value of the request parameter with the matching name. Instead of extending ActionForm directly, you can extend DynaActionForm, which results in a Map of incoming names and values.
    • It must have a zero argument constructor. The system will automatically call this default constructor.
    • It must have settable bean properties that correspond to the request parameter names. That is, it must have a setBlah method corresponding to each incoming request parameter that you want inserted automatically. For example, if the incoming request parameters are called foo and bar, the system will automatically call setFoo and setBar. Note that the bean properties should be of type String (i.e., each setBlah method should take a String as an argument).
    • It must have gettable bean properties for each property that you want to output in the JSP page. That is, it must have a getBlah method corresponding to each bean property that you want to display in JSP without Java syntax. In practice, you usually have both getter and setter methods corresponding to each request parameter.
    For example, here is a form bean corresponding to the request parameters email and password (download the source code here).
    UserFormBean.java
    package coreservlets; import org.apache.struts.action.*; public class UserFormBean extends ActionForm { private String email = "Missing address"; private String password = "Missing password"; public String getEmail() { return(email); } public void setEmail(String email) { this.email = email; } public String getPassword() { return(password); } public void setPassword(String password) { this.password = password; } }
  3. Create results beans.
    The form bean represents the input data: the data that came from the HTML form. In most applications, the more important type of data is the result data: the data created by the business logic to represent the results of the computation or database lookup. In MVC parlance, that data is the "M" in MVC -- the Model.

    Results beans need to have getter and setter methods like normal JavaBeans, but need not extend any particular class or be declared in struts-config.xml. They are stored in request, session, or application scope with the setAttribute method of HttpServletRequest, HttpSession, or ServletContext, respectively. This is the same approach used with normal beans when the MVC architecture is implemented manually with RequestDispatcher.

    Following is a bean (SuggestionBean) that will be used to store suggested email addresses and passwords. When the user supplies an illegal email address or password, the bean will be populated by SuggestionUtils, stored in the request object, and then displayed in the error page that reports the illegal address or password. (Download the source code here.)
    SuggestionBean.java
    package coreservlets; public class SuggestionBean { private String email; private String password; public SuggestionBean(String email, String password) { this.email = email; this.password = password; } public String getEmail() { return(email); } public String getPassword() { return(password); } }
    SuggestionUtils.java
    package coreservlets; public class SuggestionUtils { private static String[] suggestedAddresses = { "president@whitehouse.gov", "gates@microsoft.com", "palmisano@ibm.com", "ellison@oracle.com" }; private static String chars = "abcdefghijklmnopqrstuvwxyz0123456789#@$%^&*?!"; public static SuggestionBean getSuggestionBean() { String address = randomString(suggestedAddresses); String password = randomString(chars, 8); return(new SuggestionBean(address, password)); } public static int randomInt(int range) { return((int)(Math.random() * range)); } public static String randomString(String[] strings) { return(strings[randomInt(strings.length)]); } public static char randomChar(String string) { return(string.charAt(randomInt(string.length()))); } public static String randomString(String string, int length) { StringBuffer result = new StringBuffer(); for(int i=0; i<length; i++) { result.append(randomChar(string)); } return(result.toString()); } }
  4. Create an Action object to handle requests.
    This example is similar to the previous one except that we do do not call request.getParameter explicitly. Instead, we extract the request parameters from the already populated form bean. Specifically, we take the ActionForm argument supplied to execute, cast it to UserFormBean (our concrete class that extends ActionForm), and then call getter methods on that bean. Also, we create a SuggestionBean and store it in request scope for later display in JSP. Here is the code (download the source code here).
    RegisterAction3.java
    package coreservlets; import javax.servlet.http.*; import org.apache.struts.action.*; public class RegisterAction3 extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { UserFormBean userBean = (UserFormBean)form; String email = userBean.getEmail(); String password = userBean.getPassword(); SuggestionBean suggestionBean = SuggestionUtils.getSuggestionBean(); request.setAttribute("suggestionBean", suggestionBean); if ((email == null) || (email.trim().length() < 3) || (email.indexOf("@") == -1)) { return(mapping.findForward("bad-address")); } else if ((password == null) || (password.trim().length() < 6)) { return(mapping.findForward("bad-password")); } else { return(mapping.findForward("success")); } } }
  5. Create form that invokes blah.do.
    This form is just like the ones in the previous examples; we need an HTML form that invokes http://hostname/struts-test/actions/register3.do. We place the HTML form in the forms subdirectory and use a relative URL for the ACTION as follows (download the source code here).
    register3.jsp
    <!DOCTYPE ...> <HTML> <HEAD><TITLE>New Account Registration</TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1>New Account Registration</H1> <FORM ACTION="../actions/register3.do" METHOD="POST"> Email address: <INPUT TYPE="TEXT" NAME="email"><BR> Password: <INPUT TYPE="PASSWORD" NAME="password"><BR> <INPUT TYPE="SUBMIT" VALUE="Sign Me Up!"> </FORM> </CENTER> </BODY></HTML>
  6. Display results in JSP.
    As in the previous example, there are three possible JSP pages: one for a bad email address, one for a bad password, and one for success. However, in this example, the JSP pages need to output properties of the UserFormBean (representing the incoming form data) and the SuggestionBean (representing suggested email addresses and passwords). What are the alternatives for doing so?
    • Use JSP scripting elements. This approach is out of the question; it is precisely what Struts is designed to avoid.
    • Use jsp:useBean and jsp:getProperty. This approach is possible, but these tags are a bit clumsy and verbose.
    • Use the JSTL c:out tag. This approach is not a bad idea, but it is hardly worth the bother loading JSTL just for this situation. So, unless you are using JSTL elsewhere in your application anyhow, don't bother with c:out.
    • Use the JSP 2.0 expression language. This is perhaps the best option if the server supports JSP 2.0. In these examples, we will assume that the application needs to run on multiple servers, some of which suport only JSP 1.2.
    • Use the Struts bean:write tag. This is by far the most common approach when using Struts. Note that, unlike c:out and the JSP 2.0 expression language, bean:write automatically filters special HTML characters, replacing < with &lt; and > with &gt;. You can disable this behavior by specifying
      <bean:write
        name="beanName"
        property="beanProperty"
        filter="false">
      .
    So, in this example we use bean:write. Before we do so, however, we have to import the "bean" tag library as follows.
    <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
    Here is the code (download the source code here).
    bad-address3.jsp
    <!DOCTYPE ...> <HTML> <HEAD><TITLE>Illegal Email Address</TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1>Illegal Email Address</H1> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> The address "<bean:write name="userFormBean" property="email"/>" is not of the form username@hostname (e.g., <bean:write name="suggestionBean" property="email"/>). <P> Please <A HREF="../forms/register3.jsp">try again</A>. </CENTER> </BODY></HTML>
    bad-password3.jsp
    <!DOCTYPE ...> <HTML> <HEAD><TITLE>Illegal Password</TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1>Illegal Password</H1> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> The password "<bean:write name="userFormBean" property="password"/>" is too short; it must contain at least six characters. Here is a possible password: <bean:write name="suggestionBean" property="password"/>. <P> Please <A HREF="../forms/register3.jsp">try again</A>. </CENTER> </BODY></HTML>
    result3.jsp
    <!DOCTYPE ...> <HTML> <HEAD><TITLE>Success</TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1>You have registered successfully.</H1> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <UL> <LI>Email Address: <bean:write name="userFormBean" property="email"/> <LI>Password: <bean:write name="userFormBean" property="password"/> </UL> (Version 3) </CENTER> </BODY></HTML>
  7. Final Results.
    First, the HTML form is invoked with the URL http://localhost/struts-test/forms/register3.jsp, as follows.

    Input form 3

    In the first case, this form is given "Bill Gates" as an email address and is submitted.

    Form with bad email address

    The form's ACTION results in the URL http://localhost/struts-test/actions/register3.do. This address is mapped by struts-config.xml to the RegisterAction3 class, whose execute method is invoked. This method examines the email address, determines it is illegal because it lacks an "@" sign, and returns mapping.findForward with a value of "bad-address". That value is mapped by struts-config.xml to /WEB-INF/results/bad-address3.jsp, which is the final result displayed to the user for this input. Unlike the previous example, this JSP page shows both the illegal email address (taken from the form bean) and a suggested legal one (taken from the SuggestionBean).

    Result for bad email address

    In the second case, the form is given a legal email address (balmer@microsoft.com) but a password that is only five characters long (steve).

    Form with bad password

    The form's ACTION results in the URL http://localhost/struts-test/actions/register3.do. This address is mapped by struts-config.xml to the RegisterAction3 class, whose execute method is invoked. This method examines the password, determines it is too short, and returns mapping.findForward with a value of "bad-password". That value is mapped by struts-config.xml to /WEB-INF/results/bad-password3.jsp, which is the final result displayed to the user for this input. Unlike the previous example, this JSP page shows both the illegal email address (taken from the form bean) and a suggested legal one (taken from the SuggestionBean).

    Result for bad password

    In the third case, the form is given a legal email address and password. The form's ACTION results in the URL http://localhost/struts-test/actions/register3.do. This address is mapped by struts-config.xml to the RegisterAction3 class, whose execute method is invoked. This method finds no problem with either the email address or the password, so returns mapping.findForward with a value of "success". That value is mapped by struts-config.xml to /WEB-INF/results/result3.jsp, which is the final result displayed to the user for this input.

    Result 3

4.3 Example: Properties Files

This example is very similar to the previous one. However, some of the standard prompts and messages are taken from a properties file instead of being hardcoded in the JSP pages. To accomplish this, two new Struts features are used.

There are two advantages to using messages from properties files.


  1. Modify struts-config.xml.
  2. Define a form bean.
  3. Create results beans.
  4. Create an Action object to handle requests.
  5. Create form that invokes blah.do.
  6. Display results in JSP.
  7. Final results for this example.
  1. Modify struts-config.xml.
    The Action object and JSP URLs are declared with action and forward in the same manner as in the previous examples. In addition, the message-resources element is used to refer to a properties file as follows.
    <message-resources parameter="resources.application"
                       null="false"/>
    
    The parameter attribute refers to the location of the properties file, relative to WEB-INF/classes and with the .properties file extension implied. So, for example, "resources.application" refers to WEB-INF/classes/resources/application.properties, and "foo.bar.baz" refers to WEB-INF/classes/foo/bar/baz.properties.

    The null attribute determines whether missing messages should be flagged. If the value is true (or the null attribute is omitted), references to nonexistent messages result in empty strings. If the value is false, references to nonexistent messages result in warning messages like ???keyName???. We recommend you use false, at least in the early stages of your development, so that you do not overlook typographical errors and incorrectly named messages.

    Here is the complete struts-config.xml file and the part of the properties file that is relevant to this example (download the source code here).
    struts-config.xml (Excerpt)
    <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC ... > <struts-config> <form-beans> <form-bean name="userFormBean" type="coreservlets.UserFormBean"/> </form-beans> <action-mappings> ... <action path="/actions/register4" type="coreservlets.RegisterAction3" name="userFormBean" scope="request"> <forward name="bad-address" path="/WEB-INF/results/bad-address4.jsp"/> <forward name="bad-password" path="/WEB-INF/results/bad-password4.jsp"/> <forward name="success" path="/WEB-INF/results/result4.jsp"/> </action> </action-mappings> <message-resources parameter="resources.application" null="false"/> </struts-config>
    application.properties (Excerpt)
    form.title=New Account Registration form.emailPrompt=Email Address form.passwordPrompt=Password form.buttonLabel=Sign Me Up! form.emailError=Illegal Email Address form.passwordError=Illegal Password form.passwordLength=six
  2. Define a form bean.
    This example uses the same UserFormBean as the previous example (download the source code here).
  3. Create results beans.
    This example uses the same SuggestionBean and SuggestionUtil classes as the previous example (download the source code here).
  4. Create an Action object to handle requests.
    This example uses the same RegisterAction3 class as the previous example (download the source code here). However, the struts-config.xml file associates the Action with a different incoming URL (register4.do instead of register3.do), and associates the return values with different JSP pages.
  5. Create form that invokes blah.do.
    Instead of directly listing the "Email Address" and "Password" prompts, the form takes them from the properties file. That way, if the prompts change (or if you have multiple versions in different languages), the prompts can be updated without modifying the actual JSP page. Also, as we will see later, the same prompts are used in the JSP page that shows the results, so extracting the prompts from the properties file limits changes to one location, even though the prompts are used in multiple locations.

    Here is the code (download the source code here).
    register4.jsp
    <!DOCTYPE ...> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <HTML> <HEAD><TITLE><bean:message key="form.title"/></TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1><bean:message key="form.title"/></H1> <FORM ACTION="../actions/register4.do" METHOD="POST"> <bean:message key="form.emailPrompt"/>: <INPUT TYPE="TEXT" NAME="email"><BR> <bean:message key="form.passwordPrompt"/>: <INPUT TYPE="PASSWORD" NAME="password"><BR> <INPUT TYPE="SUBMIT" VALUE="<bean:message key="form.buttonLabel"/>"> </FORM> </CENTER> </BODY></HTML>
  6. Display results in JSP.
    As in the previous example, there are three possible JSP pages: one for a bad email address, one for a bad password, and one for success. However, in addition to using bean:write to output bean properties, the JSP pages use bean:message to output standard prompts and messages taken from the properties file. Here is the code (download the source code here).
    bad-address4.jsp
    <!DOCTYPE ...> <HTML> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <HEAD><TITLE><bean:message key="form.emailError"/></TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1><bean:message key="form.emailError"/></H1> The address "<bean:write name="userFormBean" property="email"/>" is not of the form username@hostname (e.g., <bean:write name="suggestionBean" property="email"/>). <P> Please <A HREF="../forms/register4.jsp">try again</A>. </CENTER> </BODY></HTML>
    bad-password4.jsp
    <!DOCTYPE ...> <HTML> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <HEAD><TITLE><bean:message key="form.passwordError"/></TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1><bean:message key="form.passwordError"/></H1> The password "<bean:write name="userFormBean" property="password"/>" is too short; it must contain at least <bean:message key="form.passwordLength"/> characters. Here is a possible password: <bean:write name="suggestionBean" property="password"/>. <P> Please <A HREF="../forms/register4.jsp">try again</A>. </CENTER> </BODY></HTML>
    result4.jsp
    <!DOCTYPE ...> <HTML> <HEAD><TITLE>Success</TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <CENTER> <H1>You have registered successfully.</H1> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <UL> <LI><bean:message key="form.emailPrompt"/>: <bean:write name="userFormBean" property="email"/> <LI><bean:message key="form.passwordPrompt"/>: <bean:write name="userFormBean" property="password"/> </UL> (Version 4) </CENTER> </BODY></HTML>
  7. Final results.
    First, the HTML form that uses bean:message is invoked with the URL http://localhost/struts-test/forms/register4.jsp. Given the messages in WEB-INF/classes/resources/application.properties, this yields the following result.

    Input form 4

    In the first case, this form is given "Bill Gates" as an email address and is submitted.

    Form with bad email address

    The form's ACTION results in the URL http://localhost/struts-test/actions/register4.do. This address is mapped by struts-config.xml to the RegisterAction3 class, whose execute method is invoked. This method examines the email address, determines it is illegal because it lacks an "@" sign, and returns mapping.findForward with a value of "bad-address". That value is mapped by struts-config.xml to /WEB-INF/results/bad-address4.jsp, which is the final result displayed to the user for this input. Note that this example uses the same Action object (RegisterAction3) as the previous example, yet it uses a different incoming URL and different final JSP pages. This illustrates the flexibility that the struts-config.xml file gives over standard MVC where the JSP page addresses are hard-coded into the servlet code.

    Given the messages in WEB-INF/classes/resources/application.properties, this yields the following result.

    Result for bad email address

    In the second case, the form is given a legal email address (balmer@microsoft.com) but a password that is only five characters long (steve).

    Form with bad password

    The form's ACTION results in the URL http://localhost/struts-test/actions/register4.do. This address is mapped by struts-config.xml to the RegisterAction3 class, whose execute method is invoked. This method examines the password, determines it is too short, and returns mapping.findForward with a value of "bad-password". That value is mapped by struts-config.xml to /WEB-INF/results/bad-password4.jsp, which is the final result displayed to the user for this input. Given the messages in WEB-INF/classes/resources/application.properties, this yields the following result.

    Result for bad password

    In the third case, the form is given a legal email address and password. The form's ACTION results in the URL http://localhost/struts-test/actions/register4.do. This address is mapped by struts-config.xml to the RegisterAction3 class, whose execute method is invoked. This method finds no problem with either the email address or the password, so returns mapping.findForward with a value of "success". That value is mapped by struts-config.xml to /WEB-INF/results/result4.jsp, which is the final result displayed to the user for this input. Given the messages in WEB-INF/classes/resources/application.properties, this yields the following result.

    Result 4

4.4 Source Code for this Section

The following list gives the source code to all files described in this section, listed in the order that they appear. Note that the two example of this section are part of the same struts-test Web app as the examples of the previous section.

To download the code, right-click on the links and choose "Save Target As". Be sure to reproduce the same directory structure as given here. The easiest approach is to download and unjar the entire struts-test WAR file, but this takes some time since this WAR file includes the Struts JAR files in addition to the sample code.

Next section: Prepopulating and Redisplaying Input Forms

More Information

J2EE Short Courses
Personally developed and taught by the author of Core Servlets & JSP, More Servlets & JSP, and this Jakarta Struts tutorial.
Contact Marty
Send email to hall@coreservlets.com to report errors and omissions in this writeup, or to inquire about training courses on Struts, JSP, servlets, JSF, AJAX, or Java 5 programming.
References
Jakarta Struts Books
Servlet & JSP Books