Conclusions
The Tiles tag library enables you to totally abstract your page layout by defining logical
To use Tiles effectively with Struts, you must use the Tiles plug-in. This enables you to use a logical Tiles layout
|
Chapter 17. DynaForm s and the ValidatorIN THIS CHAPTER
It might sound like a comic book superhero, but a DynaForm can't leap a tall building in a single method invocation. On the other hand, it can reduce a lot of the drudgework of developing Struts applications.
Similarly, the Struts Validator framework can eliminate many common form validation
By the end of this chapter, you'll have seen a few examples of how to create and use DynaForm s, and how to integrate them with the Validator to create practically Java-less validating forms. |
DynaForm s: Forms Without Java
DynaForm
s are an extension of the Apache Commons Beanutils project. As part of the
org.apache.commons.beanutils
package, an interface called
DynaBean
was created. Unlike normal JavaBeans, which require explicit
get
XXX
()
and
set
XXX
()
A
For example, in a traditional JavaBean, you would say
myBean.setType("kidneybean");
Using DynaBean s, you would say
myBean.set("type", "kidneybean");
This approach has both advantages and disadvantages. The major advantage is that you don't have to declare all your properties explicitly at compile-time; they can be loaded dynamically during execution. This can be very handy to quickly create new beans or new properties of beans.
The
myBean.set("tpye", "kidneybean");
The compiler won't catch this. As far as it's
DynaBeans and StrutsOne place that DynaBeans make a lot of sense is in Struts. If you use DynaBeans correctly, you can reduce the size of your ActionForm s by 80 “90%. This is because you can use DynaBeans to eliminate all of your form getters and setters. To see how this works, you'll rewrite the two ActionForm s for the new account pages of the StockTrack application. To begin, take a look at one of the existing ActionForm s and the same form rewritten with DynaForm s (Listing 17.1 and 17.2). Listing 17.1 NewUserAddressForm.java
package stocktrack.struts.form;
import javax.servlet.http.*;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionForm;
import stocktrack.struts.form.BaseForm;
/**
* stocktrack.struts.form.NewUserAddressForm class.
* this class used by Struts Framework to store data from newUserAddressForm
*
* struts-config declaration:
* <form-bean name="newUserAddressForm"
* type="stocktrack.struts.form.NewUserAddressForm" />
*
* @see org.apache.struts.action.ActionForm org.apache.struts.action.ActionForm
* Generated by StrutsWizard.
*/
public class NewUserAddressForm extends BaseForm {
public void reset(ActionMapping mapping, HttpServletRequest request) {
streetAddress1 = "";
streetAddress2 = "";
city = "";
state = "";
postalCode = "";
homePhone = "";
workPhone = "";
workExt = "";
}
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if (this.isBlankString(streetAddress1)) {
errors.add("streetAddress1",
new ActionError("stocktrack.newuser.required"));
}
if (this.isBlankString(city)) {
errors.add("city", new ActionError("stocktrack.newuser.required"));
}
if (this.isBlankString(state)) {
errors.add("state", new ActionError("stocktrack.newuser.required"));
} else {
if (!this.isValidState(state)) {
errors.add("state",
new ActionError("stocktrack.newuser.invalid.state"));
}
}
if (this.isBlankString(postalCode)) {
errors.add("postalCode",
new ActionError("stocktrack.newuser.required"));
} else {
if (!this.isValidPostalCode(postalCode)) {
errors.add("postalCode",
new ActionError("stocktrack.newuser.invalid.postalCode"));
}
}
if (this.isBlankString(homePhone)) {
errors.add("homePhone", new ActionError("stocktrack.newuser.required"));
} else {
if (!this.isValidPhone(homePhone)) {
errors.add("homePhone",
new ActionError("stocktrack.newuser.invalid.phone"));
}
}
if (this.isBlankString(workPhone)) {
errors.add("workPhone", new ActionError("stocktrack.newuser.required"));
} else {
if (!this.isValidPhone(workPhone)) {
errors.add("workPhone",
new ActionError("stocktrack.newuser.invalid.phone"));
}
}
return errors;
}
private String streetAddress1;
private String streetAddress2;
private String city;
private String state;
private String postalCode;
private String homePhone;
private String workPhone;
private String workExt;
public String getStreetAddress1() {
return streetAddress1;
}
public void setStreetAddress1(String streetAddress1) {
this.streetAddress1 = streetAddress1;
}
public String getStreetAddress2() {
return streetAddress2;
}
public void setStreetAddress2(String streetAddress2) {
this.streetAddress2 = streetAddress2;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getHomePhone() {
return homePhone;
}
public void setHomePhone(String homePhone) {
this.homePhone = homePhone;
}
public String getWorkPhone() {
return workPhone;
}
public void setWorkPhone(String workPhone) {
this.workPhone = workPhone;
}
public String getWorkExt() {
return workExt;
}
public void setWorkExt(String workExt) {
this.workExt = workExt;
}
}
Listing 17.2 NewUserAddressForm.java as a DynaForm
package stocktrack.struts.form;
import javax.servlet.http.*;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionForm;
import stocktrack.struts.form.BaseForm;
import stocktrack.struts.form.BaseDynaForm;
/**
* stocktrack.struts.form.NewUserAddressForm class.
* this class used by Struts Framework to store data from newUserAddressForm
*
* struts-config declaration:
* <form-bean name="newUserAddressForm"
* type="stocktrack.struts.form.NewUserAddressForm" />
*
* @see org.apache.struts.action.ActionForm org.apache.struts.action.ActionForm
* Generated by StrutsWizard.
*/
public class NewUserAddressForm extends BaseDynaForm {
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if (BaseForm.isBlankString(this.getString("streetAddress1"))) {
errors.add("streetAddress1",
new ActionError("stocktrack.newuser.required"));
}
if (BaseForm.isBlankString(this.getString("city"))) {
errors.add("city",
new ActionError("stocktrack.newuser.required"));
}
if (BaseForm.isBlankString(this.getString("state"))) {
errors.add("state", new ActionError("stocktrack.newuser.required"));
} else {
if (!BaseForm.isValidState(this.getString("state"))) {
errors.add("state",
new ActionError("stocktrack.newuser.invalid.state"));
}
}
if (BaseForm.isBlankString(this.getString("postalCode"))) {
errors.add("postalCode",
new ActionError("stocktrack.newuser.required"));
} else {
if (!BaseForm.isValidPostalCode(this.getString("postalCode"))) {
errors.add("postalCode",
new ActionError("stocktrack.newuser.invalid.postalCode"));
}
}
if (BaseForm.isBlankString(this.getString("homePhone"))) {
errors.add("homePhone", new ActionError("stocktrack.newuser.required"));
} else {
if (!BaseForm.isValidPhone(this.getString("homePhone"))) {
errors.add("homePhone",
new ActionError("stocktrack.newuser.invalid.phone"));
}
}
if (BaseForm.isBlankString(this.getString("workPhone"))) {
errors.add("workPhone", new ActionError("stocktrack.newuser.required"));
} else {
if (!BaseForm.isValidPhone(this.getString("workPhone"))) {
errors.add("workPhone",
new ActionError("stocktrack.newuser.invalid.phone"));
}
}
return errors;
}
}
The first thing to notice here is that the class has been changed from
BaseForm
(which, as you might recall, is simply
ActionForm
with a few helper functions added for validation) to
BaseDynaForm
, which is a different helper class that extends
DynaActionForm
instead. All the getters and setters have been removed, and the
validate
function now uses
this.getString
to get the values of the properties rather than accessing the local
getString() is in fact not a part of DynaActionForm , which only defines generic get() and set() methods that take and return Object s. However, rather than cast to String all the time, I've created BaseDynaForm , which adds the getString() versions. Listing 17.3 shows this simple class. Listing 17.3 BaseDynaForm.java
package stocktrack.struts.form;
import org.apache.struts.action.DynaActionForm;
public class BaseDynaForm extends DynaActionForm {
public String getString(String name) {
return (String) this.get(name);
}
public String getString(String name, int ind) {
return (String) this.get(name, ind);
}
}
In addition, now that the class no longer derives from
BaseForm
, you need to call out explicitly to
BaseForm
to get helper functions such as
nullOrVoid
. Because they were originally defined nonstatic, you must go into
BaseForm
and declare them static. I suppose that we could have
The <form-property> TagSo, if all the getters and setters have been removed from the class, how does the class know what its legal properties are? The answer lies in the form-bean tag in struts-config.xml .
Until now, the forms you've defined in the file
<form-bean name="newUserAddressForm"
type="stocktrack.struts.form.NewUserAddressForm" />
But now, you're going to add some new tags, form-property tags, to the form-bean (see Listing 17.4). Listing 17.4 The Rewritten newUserAddress Form<form-bean name="newUserAddressForm" type="stocktrack.struts.form.NewUserAddressForm"> <form-property name="streetAddress1" type="java.lang.String"/> <form-property name="streetAddress2" type="java.lang.String"/> <form-property name="city" type="java.lang.String"/> <form-property name="state" type="java.lang.String"/> <form-property name="postalCode" type="java.lang.String"/> <form-property name="homePhone" type="java.lang.String"/> <form-property name="workPhone" type="java.lang.String"/> <form-property name="workExt" type="java.lang.String"/> </form-bean> Inside the form-bean , all the properties have been listed along with their type. This allows the DynaForm to populate the fields. You can use pretty much any class you want in the type value, as well as primitives such as int , Boolean , and so on. You can also specify initializations for properties via the initial keyword. For example, the following form-property sets up a property called opt-in , which defaults to true: <form-property name="homePhone" type="boolean" initial="true"/>
The
initial
keyword is also the only way to create an array or list of a given size. Imagine that you have a form with 10 lines to fill in the
<form-property name="depName" type="java.lang.String[]"
initial="{'','','','','','','','','',''}"/>
<form-property name="depAge" type="java.lang.String[]"
initial="{'','','','','','','','','',''}"/>
<form-property name="depGender" type="java.lang.String[]"
initial="{'','','','','','','','','',''}"/>
Whatever the type
String (and other) arrays are accessed using get() and set() , the same as any other DynaBean . However, you use a second argument, which specifies the index into the array. |