
This hands-on lab assumes you have some basic knowledge or
programming experience on the following technologies.
Before you begin, you need to install the JDK and NetBeans IDE
software on your
computer as
described in
here.
In this exercise, you are going to build
and run "ready-to-build" Struts sample application. You will then
build the same application from scratch.
In Struts 1.3.8, which is the
version that is used in NetBeans 6.7.1, the ActionError class is
deprecated - so you will get an compile error. Instead, you
should use ActionMessage class. The sample codes are already
changed. (If you are using NetBeans 6.5.x, which uses Struts
1.2.x, the ActionError class is still supported.)0. Start NetBeans IDE.
1. Open struts-submit-solution
NetBeans project.
2. Build and run struts-submit-solution project.

Figure-1.12: Running struts-submit-solution project
3. Check input validation.


In this exercise, you have built
and run a ready-to-build-and-run "struts-submit-solution" sample
application to get a sense how the application works. In the next
exercise, you are going to build it from scratch.
In this exercise, you are going to build
the struts-submit-solution application
from scratch. The goal of this exercise is to let you get exposed
to
a step by step process of building the same application you built and
ran in the previous exercise. Even though the
"struts-submit" application is simple, it lets you explore most core
features of the Struts framework listed below:
Leaning poiints:
Tasks to be performed:



Learning
points:
Because a Struts application is a Web application, it has to follow the
same rules that any Web application has to follow. Every Web
application must have a web.xml configuration file. The web.xml
file
should define the ActionServlet, which functions as a controller
from
the standpoint of the MVC (Model-View-Controller) framework. In
other
words, every request to a Struts application has to go through the
ActionServlet. The ActionServlet is provided by the Struts framework. | <?xml version="1.0"
encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>2</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list> <jsp-config> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/struts-logic.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri> <taglib-location>/WEB-INF/struts-nested.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri> <taglib-location>/WEB-INF/struts-tiles.tld</taglib-location> </taglib> </jsp-config> </web-app> |
<Learning
points>
A Struts application has to have a Struts application configuration
file (hereafter configuration file). As is shown in the web.xml file
above, the name and location of the configuration file can be user
definable. In this example and in most other simple Struts
applications, the default name of the configuration file is
struts-config.xml and it typically resides under application's
./WEB-INF directory. The configuration information is then read
by
Struts framework when the application gets started. | <?xml version="1.0"
encoding="UTF-8" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"> <struts-config> <form-beans> <form-bean name="submitForm" type="submit.SubmitForm"/> </form-beans> <global-exceptions> </global-exceptions> <global-forwards> <forward name="welcome" path="/Welcome.do"/> </global-forwards> <action-mappings> <action path="/Welcome" forward="/welcomeStruts.jsp"/> <action path="/submit" type="submit.SubmitAction" name="submitForm" input="/submit.jsp" scope="request" validate="true"> <forward name="success" path="/submit.jsp"/> <forward name="failure" path="/submit.jsp"/> </action> </action-mappings> <controller processorClass="org.apache.struts.tiles.TilesRequestProcessor"/> <message-resources parameter="com/myapp/struts/ApplicationResource"/> <!-- ========================= Tiles plugin ===============================--> <!-- This plugin initialize Tiles definition factory. This later can takes some parameters explained here after. The plugin first read parameters from web.xml, thenoverload them with parameters defined here. All parameters are optional. The plugin should be declared in each struts-config file. - definitions-config: (optional) Specify configuration file names. There can be several comma separated file names (default: ?? ) - moduleAware: (optional - struts1.1) Specify if the Tiles definition factory is module aware. If true (default), there will be one factory for each Struts module. If false, there will be one common factory for all module. In this later case, it is still needed to declare one plugin per module. The factory will be initialized with parameters found in the first initialized plugin (generally the one associated with the default module). true : One factory per module. (default) false : one single shared factory for all modules - definitions-parser-validate: (optional) Specify if xml parser should validate the Tiles configuration file. true : validate. DTD should be specified in file header (default) false : no validation Paths found in Tiles definitions are relative to the main context. --> <plug-in className="org.apache.struts.tiles.TilesPlugin" > <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" /> <set-property property="moduleAware" value="true" /> </plug-in> <!-- ========================= 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> </struts-config> |
<Learning
points>
An ActionForm class captures input form data that is entered by an end
user. An ActionForm class is a JavaBean and each input form data
field is mapped to a property of the ActionForm class. Consequently you
write getter and setter methods for each of the properties. 
| package submit; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.*; public final class SubmitForm extends ActionForm { /* Getter and Setter methods of properties */ /* Getter and Setter methods of Last Name property */ private String lastName = "Your last name"; // default value public String getLastName() { return (this.lastName); } public void setLastName(String lastName) { this.lastName = lastName; } /* Getter and Setter methods of Address property */ private String address = null; public String getAddress() { return (this.address); } public void setAddress(String address) { this.address = address; } /* Getter and Setter methods of Sex property */ private String sex = null; public String getSex() { return (this.sex); } public void setSex(String sex) { this.sex = sex; } /* Getter and Setter methods of Married status property */ private String married = null; public String getMarried() { return (this.married); } public void setMarried(String married) { this.married = married; } /* Getter and Setter methods of Age property */ private String age = null; public String getAge() { return (this.age); } public void setAge(String age) { this.age = age; } /* Input validation */ public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { // Log the forms data servlet.log("Lastname:" + lastName); servlet.log("Address:" + address); servlet.log("Sex:" + sex); servlet.log("Married:" + married); servlet.log("Age:" + age); // Check for mandatory data ActionErrors errors = new ActionErrors(); if (lastName == null || lastName.equals("")) { errors.add("Last Name", new ActionMessage("error.lastName")); } if (address == null || address.equals("")) { errors.add("Address", new ActionMessage("error.address")); } if (sex == null || sex.equals("")) { errors.add("Sex", new ActionMessage("error.sex")); } if (age == null || age.equals("")) { errors.add("Age", new ActionMessage("error.age")); } return errors; } } |
| package submit; import javax.servlet.http.*; import org.apache.struts.action.*; public final class SubmitAction extends Action { // The execute() method is where you provide your business logic public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { // Cast ActionForm object to SubmitForm type SubmitForm f = (SubmitForm) form; // Retrieve the value of lastname field String lastName = f.getLastName(); // Translate the lastname to upper case and save it Request scope request.setAttribute("lastName", lastName.toUpperCase()); // Create and return ActionForward object with "success" outcome return (mapping.findForward("success")); } } |



| # Sample ResourceBundle
properties file errors.header=<h4>Validation Error(s)</h4><ul> errors.footer=</ul><hr> error.lastName=<li>Enter your last name error.address=<li>Enter your address error.sex=<li>Enter your sex error.age=<li>Enter your age error.birthYear=<li>Enter the year you were born between 1900 and 2004 inclusive |

| <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html> <head><title>Submit example</title></head> <body> <h3>Example Submit Page</h3> <html:errors/> <html:form action="submit.do"> Last Name: <html:text property="lastName"/><br> Address: <html:textarea property="address"/><br> Sex: <html:radio property="sex" value="M"/>Male <html:radio property="sex" value="F"/>Female<br> Married: <html:checkbox property="married"/><br> Age: <html:select property="age"> <html:option value="a">0-19</html:option> <html:option value="b">20-49</html:option> <html:option value="c">50-</html:option> </html:select><br> <html:submit/> </html:form> <logic:present name="lastName" scope="request"> Hello <logic:equal name="submitForm" property="age" value="a"> young </logic:equal> <logic:equal name="submitForm" property="age" value="c"> old </logic:equal> <bean:write name="lastName" scope="request"/> </logic:present> </body> </html> |

In this exercise, you have built and run
"struts-submit" sample application step by step.
return
to the top
In this exercise, you will add two new
display pages (*.jsp files) to the struts-submit
application you built
in the previous exercise and also modify the page navigation rules as
following.
* For the sake of the this exercise, the execute()
method of the submitAction class returns two different outcomes as
following (as opposed to a single "success" outcome in the original
application)
o "success":
when the lastname starts with "success"
o "failure": all
the other cases
* For "success" outcome, the submitSuccess.jsp gets
displayed next (as opposed to submit.jsp in the original application)
* For "failure" outcome, the submitFailure.jsp gets
displayed next
| ... <action-mappings> <action path="/Welcome" forward="/welcomeStruts.jsp"/> <action path="/submit" type="submit.SubmitAction" name="submitForm" input="/submit.jsp" scope="request" validate="true"> <forward name="success" path="/submitSuccess.jsp"/> <forward name="failure" path="/submitFailure.jsp"/> </action> </action-mappings> ... |

| <!-- Replace the contents of
this file from the one in Code-15 --> <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html> <head><title>Submit example</title></head> <body> <h3>Example Submit Success Page</h3> <html:errors/> <html:form action="submit.do"> Last Name: <html:text property="lastName"/><br> Address: <html:textarea property="address"/><br> Sex: <html:radio property="sex" value="M"/>Male <html:radio property="sex" value="F"/>Female<br> Married: <html:checkbox property="married"/><br> Age: <html:select property="age"> <html:option value="a">0-19</html:option> <html:option value="b">20-49</html:option> <html:option value="c">50-</html:option> </html:select><br> <html:submit/> </html:form> <logic:present name="lastName" scope="request"> Hello <logic:equal name="submitForm" property="age" value="a"> young </logic:equal> <logic:equal name="submitForm" property="age" value="c"> old </logic:equal> <bean:write name="lastName" scope="request"/> </logic:present> </body> </html> |
| <!-- Replace the contents of
this file from the one in Code-15 --> <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html> <head><title>Submit example</title></head> <body> <h3>Example Submit Failure Page</h3> <html:errors/> <html:form action="submit.do"> Last Name: <html:text property="lastName"/><br> Address: <html:textarea property="address"/><br> Sex: <html:radio property="sex" value="M"/>Male <html:radio property="sex" value="F"/>Female<br> Married: <html:checkbox property="married"/><br> Age: <html:select property="age"> <html:option value="a">0-19</html:option> <html:option value="b">20-49</html:option> <html:option value="c">50-</html:option> </html:select><br> <html:submit/> </html:form> <logic:present name="lastName" scope="request"> Hello <logic:equal name="submitForm" property="age" value="a"> young </logic:equal> <logic:equal name="submitForm" property="age" value="c"> old </logic:equal> <bean:write name="lastName" scope="request"/> </logic:present> </body> </html> |
| package submit; import javax.servlet.http.*; import org.apache.struts.action.*; public final class SubmitAction extends Action { // The execute() method is where you provide your business logic public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { // Cast ActionForm object to SubmitForm type SubmitForm f = (SubmitForm) form; // Retrieve the value of lastname field String lastName = f.getLastName(); /*************** commented out ************************ // Translate the lastname to upper case and save it Request scope request.setAttribute("lastName", lastName.toUpperCase()); // Create and return ActionForward object with "success" outcome return (mapping.findForward("success")); **************** end of the commented out code **********/ // If the lastName starts with "success", translate the name into uppercase and then // save it in the request scope object (as it does right now in the original code) and return success. // Otherwise, do not save it in the request scope object and return failure. if (lastName.startsWith("success") ){ // Translate the lastname to upper case and save it Request scope request.setAttribute("lastName", lastName.toUpperCase()); // Create and return ActionForward object with "success" outcome return (mapping.findForward("success")); } else{ return (mapping.findForward("failure")); } } } |



In this exercise, you will add another
field using Struts tag and add an input validation logic for that field
as following:
* Add "Year you were born" field to the submit.jsp
page
* Perform input validation such that the number has
to be between 1900 and 2004 inclusive
| <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html> <head><title>Submit example</title></head> <body> <h3>Example Submit Page</h3> <html:errors/> <html:form action="submit.do"> Last Name: <html:text property="lastName"/><br> Address: <html:textarea property="address"/><br> Sex: <html:radio property="sex" value="M"/>Male <html:radio property="sex" value="F"/>Female<br> Married: <html:checkbox property="married"/><br> Age: <html:select property="age"> <html:option value="a">0-19</html:option> <html:option value="b">20-49</html:option> <html:option value="c">50-</html:option> </html:select><br> Year you were born: <html:text property="birthYear"/><br> <html:submit/> </html:form> <logic:present name="lastName" scope="request"> Hello <logic:equal name="submitForm" property="age" value="a"> young </logic:equal> <logic:equal name="submitForm" property="age" value="c"> old </logic:equal> <bean:write name="lastName" scope="request"/> </logic:present> </body> </html> |
| package submit; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.*; public final class SubmitForm extends ActionForm { /* Getter and Setter methods of properties */ /* Getter and Setter methods of Last Name property */ private String lastName = "Your last name"; // default value public String getLastName() { return (this.lastName); } public void setLastName(String lastName) { this.lastName = lastName; } /* Getter and Setter methods of Address property */ private String address = null; public String getAddress() { return (this.address); } public void setAddress(String address) { this.address = address; } /* Getter and Setter methods of Sex property */ private String sex = null; public String getSex() { return (this.sex); } public void setSex(String sex) { this.sex = sex; } /* Getter and Setter methods of Married status property */ private String married = null; public String getMarried() { return (this.married); } public void setMarried(String married) { this.married = married; } /* Getter and Setter methods of Age property */ private String age = null; public String getAge() { return (this.age); } public void setAge(String age) { this.age = age; } /* Getter and Setter methods of BirthYear property */ private String birthYear = null; public String getBirthYear(){ return (this.birthYear); } public void setBirthYear(String birthYear){ this.birthYear = birthYear; } /* Input validation */ public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { // Log the forms data servlet.log("Lastname:" + lastName); servlet.log("Address:" + address); servlet.log("Sex:" + sex); servlet.log("Married:" + married); servlet.log("Age:" + age); servlet.log("BirthYear:" + birthYear); // Check for mandatory data ActionErrors errors = new ActionErrors(); if (lastName == null || lastName.equals("")) { errors.add("Last Name", new ActionMessage("error.lastName")); } if (address == null || address.equals("")) { errors.add("Address", new ActionMessage("error.address")); } if (sex == null || sex.equals("")) { errors.add("Sex", new ActionMessage("error.sex")); } if (age == null || age.equals("")) { errors.add("Age", new ActionMessage("error.age")); } if (birthYear == null || birthYear.equals("")) { errors.add("BirthYear", new ActionMessage("error.birthYear")); } else{ int year = Integer.parseInt(birthYear); if ((year < 1900) || (year > 2004)){ errors.add("BirthYear", new ActionMessage("error.birthYear")); } } return errors; } } |
| errors.header=<h4>Validation
Error(s)</h4><ul> errors.footer=</ul><hr> error.lastName=<li>Enter your last name error.address=<li>Enter your address error.sex=<li>Enter your sex error.age=<li>Enter your age error.birthYear=<li>Enter the year you were born between 1900 and 2004 inclusive |
| <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html> <head><title>Submit example</title></head> <body> <h3>Example Submit Input Validation Error Page</h3> <html:errors/> <html:form action="submit.do"> Last Name: <html:text property="lastName"/><br> Address: <html:textarea property="address"/><br> Sex: <html:radio property="sex" value="M"/>Male <html:radio property="sex" value="F"/>Female<br> Married: <html:checkbox property="married"/><br> Age: <html:select property="age"> <html:option value="a">0-19</html:option> <html:option value="b">20-49</html:option> <html:option value="c">50-</html:option> </html:select><br> Year you were born: <html:text property="birthYear"/><br> <html:submit/> </html:form> <logic:present name="lastName" scope="request"> Hello <logic:equal name="submitForm" property="age" value="a"> young </logic:equal> <logic:equal name="submitForm" property="age" value="c"> old </logic:equal> <bean:write name="lastName" scope="request"/> </logic:present> </body> </html> |
| <action
path="/submit" type="submit.SubmitAction" name="submitForm" input="/submitInputValidationError.jsp" scope="request" validate="true"> <forward name="success" path="/submitSuccess.jsp"/> <forward name="failure" path="/submitFailure.jsp"/> </action> |


The goal of this exercise is to implement DynaActionForm.
DynaActionForm is introduced in Struts 1.1 and can be used in
replacement of ActionForm. Now let's talk about why you want to
use DynaActionForm instead of ActionForm.
<to be provided in the future>