| Section 4: Handling Request Parameters with Form Beans |
|---|
| 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 |
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.
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.
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.
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.
Action object to handle requests.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.
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.
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:
ActionForm class, is declared
in struts-config.xml
with the form-bean tag, and
is referenced in struts-config.xml with name
and scope attributes in the action element.
jsp:useBean,
this bean need not extend any
particular class and requires no special
struts-config.xml declarations.
bean:write tags to output bean properties
in the JSP page that displays the final results.jsp:getProperty tag. Before we use bean:write,
we have to import the "bean" tag library as follows.<%@ taglib
uri="/WEB-INF/struts-bean.tld"
prefix="bean" %>
Action object to handle requests.
Action classes to handle requests for blah.do.
As before, we use the action element.
forward elements
within the action element.
form-bean entry (within the
form-beans entry) with the following two
attributes:
name attribute of
the action element.
<form-beans>
<form-bean name="userFormBean"
type="coreservlets.UserFormBean"/>
</form-beans>
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 in the
form-bean declaration.
<action path="/actions/register3"
type="coreservlets.RegisterAction3"
name="userFormBean"
scope="request">
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>
execute method. A form bean
has the following characteristics.
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.
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).
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.
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; } }
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.
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()); } }
Action
object to handle requests.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")); } } }
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>
UserFormBean (representing the incoming form data)
and the SuggestionBean (representing suggested
email addresses and passwords). What are the alternatives for
doing so?
jsp:useBean and jsp:getProperty.
This approach is possible, but these tags
are a bit clumsy and verbose.
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.
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 <
and > with >. You can
disable this behavior by specifying<bean:write
name="beanName"
property="beanProperty"
filter="false">.
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>
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).
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).
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.
| 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.
message-resources entry in
struts-config.xml.
bean:message to output the messages that
are contained in the properties file.
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.
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.
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
UserFormBean as the previous example
(download the source code here).
SuggestionBean and
SuggestionUtil classes as the previous example
(download the source code here).
Action
object to handle requests.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.
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>
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>
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.
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.
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.
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.
| 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.
Action subclass.
Action subclass. Note that this is the same Action class
as the previous example. However, the struts-config.xml file
assigns a different incoming URL and different resultant JSP pages.
| More Information |
|---|
|
|