Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Apache Struts - From Novice To Professional (2006)

.pdf
Скачиваний:
56
Добавлен:
17.08.2013
Размер:
11.68 Mб
Скачать

218

C H A P T E R 1 5 T H E V A L I D A T O R F R A M E W O R K

The big disadvantage of using <formset> for localizing validations is that you can’t “override” the validations of just a few fields. So, if you really, really need to localize your validations, I’d recommend either one of the two hacks I presented in Chapter 12.

Lab 15: Using the Validator Framework in

LILLDEP

In Lab 6, you hand-coded various validations for ContactForm. In this lab session, you’ll migrate ContactForm to using the Validator framework.

1.Put in the plug-in section for the Validator in struts-config.xml. Use the name default name validation.xml for your validation set.

2.Change the base class of ContactForm to org.apache.struts.validator.ValidatorForm.

3.Comment out the existing validate() function.

4.Complete validation.xml in ./web/WEB-INF. Declare all the validations you implemented in Lab 6. If you completed Lab 10b, you would also need to amend the relevant JSPs.

5.Compile, deploy, and test your changes.

Useful Links

RFC-2822 for email: http://rfc.net/rfc2822.html

Struts’ Validator page: http://struts.apache.org/struts-doc-1.2.x/userGuide/ dev_validator.html

The Commons Validator framework project: http://jakarta.apache.org/commons/ validator/

A Regular Expressions tutorial: http://www.regular-expressions.info/ tutorial.html

Some information on unit testing (also the homepage of JUnit, the Java unit testing framework): www.junit.org

C H A P T E R 1 5 T H E V A L I D A T O R F R A M E W O R K

219

ISO-639 language codes: http://en.wikipedia.org/wiki/ISO_639

ISO 3166 country codes: http://en.wikipedia.org/wiki/ISO_3166

Summary

The Validator framework brings many benefits by simplifying the creation and maintenance of your webapp’s validations.

You may extend the Validator framework either by implementing your own validate() function or by creating a new validator.

C H A P T E R 1 6

■ ■ ■

Dynamic Forms

Every time you need a new form for user input, you usually have to write a new ActionForm subclass. This can get tedious quickly, especially if your form has many getter and setter functions. (I hope you had a taste of how bad it can get in Lab 6, where you had to implement ContactActionForm, with 14 getters and setters.)

In previous chapters, I’ve shown you how to alleviate these problems a little by reusing forms (Lab 9a) or by using nested properties (Lab 10a) to avoid writing superfluous getter and setter functions in your ActionForm subclasses.

But these get you only so far. Dynamic forms, the subject of this chapter, are a way to eliminate writing Java code entirely. Instead, you declare additional XML in struts-config.xml for each form you want to create. The XML involved is an extension to the usual <form-bean> declaration you’re familiar with: you add <form-property> tags for each property in the form.

Like everything else in life, there are advantages as well as disadvantages to using dynamic forms. The primary advantage is, of course, that you don’t have to declare getters and setters for your form. This can be quite a big plus if your application requires a lot of forms. However, as you’ll see later, dynamic forms also have a number of important shortcomings.

Toward the end of this chapter, we’ll also look at a couple of recent additions to Struts (since Struts 1.2.6) that take the dynamic forms idea to the extreme. Using LazyValidatorForm, you don’t even have to declare the properties in your forms!

We won’t be done with dynamic forms in this chapter. In Chapter 19, I’ll show you how to implement a plug-in that makes interesting extensions to dynamic forms.

Declaring Dynamic Forms

To create a dynamic form, you need only to declare it in your struts-config.xml file. Declaring a dynamic form is easy: in your <form-bean> declaration of the form, put in extra <form-property> tags for each property on the form.

You can declare simple, indexed, mapped, or nested properties this way (see Chapter 10). Listing 16-1 shows how you might declare a form bean with a simple (String), indexed

(array), and mapped property (using a HashMap).

221

222 C H A P T E R 1 6 D Y N A M I C F O R M S

Listing 16-1. Declaring a Dynamic Form

<form-bean name="MyFormBean" type="org.apache.struts.action.DynaActionForm">

<!-- declaring a simple property --> <form-property name="MySimpleProp"

type="java.lang.String" initial="Hello" />

<!-- declaring another simple property --> <form-property name="MySimpleIntProp"

type="int" />

<!-- declaring an indexed property --> <form-property name="MyIndexedProp"

type="java.lang.String[]" size="314" />

<!-- declaring a mapped property --> <form-property name="MyMappedProp"

type="java.util.HashMap" />

<!-- declaring a nested property --> <form-property name="MyNestedProp"

type="com.myco.myapp.MyBean" />

</form-bean>

The first thing to note is that the type attribute must be either org.apache.struts. action.DynaActionForm (itself a subclass of ActionForm) or your subclass of DynaActionForm.

To put it another way, Struts disregards a <form-bean>’s <form-property> tags unless the ActionForm subclass indicated by the type attribute is also a subclass of DynaActionForm. For Struts versions later than 1.2.6, this is not strictly true. See the note for details.

Note In versions of Struts later than 1.2.6, if the class indicated by the type attribute is of type

BeanValidatorForm or a subclass of it (like LazyValidatorForm), then the <form-property> tags are read as well. I’ll describe both of these interesting new additions toward the end of this chapter.

C H A P T E R 1 6 D Y N A M I C F O R M S

223

Declaring Simple Properties

From Listing 16-1, you can easily see that simple properties can either be supported Java classes or primitive types. Java classes and primitive types supported by DynaActionForm are

java.lang.BigDecimal

java.lang.BigInteger

boolean and java.lang.Boolean

byte and java.lang.Byte

char and java.lang.Character

java.lang.Class

double and java.lang.Double

float and java.lang.Float

int and java.lang.Integer

long and java.lang.Long

short and java.lang.Short

java.lang.String

java.sql.Date

java.sql.Time

java.sql.Timestamp

(This list is available on the Struts documentation site. Refer to “Useful Links” at the end of this chapter.) You may optionally specify a default value for the simple property using the initial attribute. For example, in Listing 16-1, the default value for the simple property

MySimpleProp is Hello.

Declaring Indexed Properties

To create an indexed property, simply put [] after the classname (or primitive type). You also need to specify a size for the array. If you need flexible length arrays, there are two ways to do it.

224 C H A P T E R 1 6 D Y N A M I C F O R M S

In the first method, you omit the size attribute. Since the size isn’t specified, a newly created form bean will have a null value for this indexed property. So, you have to “prime” the form bean (a DynaActionForm instance) in an Action subclass before it can be used to store data. You’ve seen a variation of this technique in Chapter 10.

When a page is requested, it’s really a request to a form handler. This form handler (an Action subclass) creates an array of the right type and length and inserts it into the form bean. Only then is control passed to a JSP page, which populates the form. DynaActionForm has a function called set(String propertyName,Object value), which you can use to prime the form bean. Listing 16-2 illustrates this technique in action.

Listing 16-2. Setting Array Lengths at Runtime

public ActionForward execute(...){

DynaActionForm dForm = (DynaActionForm) form;

/* somehow get the needed array length */ int length = ...;

/* create the new array */

String[] myProp = new String[length];

/* insert array into form */ dForm.set("MyIndexedProp", myProp);

/* forward to JSP */

return mapping.findForward("success");

}

The JSP page pointed to by success will be able to use the form’s MyIndexedProp property. Needless to say, you need to use session scope for this to work.

The second method for flexible arrays is to use a class that implements the java.util.List interface (e.g., ArrayList) as the <form-property>’s type:

<form-property name="MyIndexedProperty" type="java.util.ArrayList" />

Unfortunately, with this approach you lose Struts’s automatic type conversion, since all data stored in the List are stored as String instances.

Declaring Mapped Properties

Mapped properties are defined by using a type that implements the java.util.Map interface (e.g., HashMap):

<form-property name="MyMappedProperty" type="java.util.HashMap" />

C H A P T E R 1 6 D Y N A M I C F O R M S

225

Declaring Nested Properties

Lastly, nested properties are defined using a type attribute, which is a JavaBean, assumed to contain nested properties used in your JSPs:

<form-property name="MyNestedProperty" type="com.myco.myapp.MyBean" />

The only thing to note about nested properties is that you must ensure that the nested property finally resolves to one of the supported primitive types or Java classes listed earlier for simple properties. This should hardly come as a surprise, since this is what you’d expect even with ordinary ActionForms.

Accessing Dynamic Properties

At some point, you will need to read (or write) data from your dynamic form. The DynaActionForm base class exposes three getters corresponding to simple, indexed, and mapped properties:

get(String property) returns an Object value of the given property. This accessor is for simple properties.

get(String property,int index) returns an Object value of the given property with the given index. This accessor is for indexed properties.

get(String property,String key) returns an Object value for the given key on the given property. This accessor is for mapped properties.

Similarly, there are set(String property, Object value), set(String property, int index, Object value), and set(String property, String key, Object value).

If you’ve used a primitive type for a property, then the Object type in the return of get() and the argument of set() would be the corresponding Java class for that primitive type. For example, if you’ve declared a property of type int, then you should expect an Integer to be returned from get() and you should pass an instance of Integer in set().

Transferring Properties to a JavaBean

In many instances, all you want to do with a dynamic form bean is to transfer the property values into a JavaBean instance. This is often the case if:

You’re using Struts as a web-based front-end to an existing application. The existing application usually would already have JavaBean classes to hold data. You want to transfer properties from your dynamic form to this JavaBean.

226 C H A P T E R 1 6 D Y N A M I C F O R M S

You want to transfer data into a data object. This is the case if you’re using a Model framework like Torque or Lisptorq, which exposes data through data objects. See Chapter 5 and Appendix A for details.

The Apache Commons’ Bean project (see “Useful Links”) provides a utility class to help you to do this transfer easily. The following

org.apache.commons.bean.BeanUtils.copyProperties(myJavaBean , form)

will copy the properties of the DynaActionForm instance (form) into your JavaBean class (myJavaBean).

Of course, only properties existing on myJavaBean will be copied over. The JAR file for Apache Bean classes are bundled with Struts in commons-beanutils.jar.

Dynamic Form Disadvantages

Dynamic forms have a few significant limitations compared to ordinary ActionForms:

You must use different syntax properties when using EL: If you’re using EL, you have to prefix map to a form’s property. For example, instead of using ${myform.

myproperty}, you’d have to use ${myform.map.myproperty}. This applies to simple, indexed, mapped, and nested properties. This is only necessary when referring to a property using EL. No change would be necessary when referring to a property within either the original or EL-enabled Struts tags. For example, <bean:write name="myform" property="myproperty"/> would work fine.

You can’t use conventional getters and setters: If you subclass DynaActionForm (in order to run custom validations, for example), you can’t use conventional getters and setters on your subclass and expect them to work with Struts tags. Struts handles DynaActionForm and its subclass differently from normal ActionForms. This can be a serious problem if you need to work directly with form data. The design philosophy of dynamic forms assumes that you won’t have to work with their data directly. Rather, you are expected to transfer the form’s properties to a suitable JavaBean, using the BeanUtils technique described in the previous subsection.

Compile-time checking is not possible: By using dynamic forms, you lose compiletime checking. For example, because the return value of DynaActionForm’s get() are all Objects, there’s no way to tell at compile time if you’ve assigned a return value to the wrong type. This would not be a problem with conventional getters because their return types are usually quite narrow (String or Double, etc.). A mismatch in assignment would be caught by the Java compiler. A similar situation occurs for setters as well.

C H A P T E R 1 6 D Y N A M I C F O R M S

227

When to Use Dynamic Forms

So, when do you use dynamic forms? The Apache Struts User’s Guide (see “Useful Links”) spells this out in detail:

DynaActionForms are meant as an easy solution to a common problem: Your ActionForms use simple properties and standard validations, and you just pass these properties over to another JavaBean...DynaActionForms are not a drop-in replacement for ActionForms...

The first sentence refers to the use of BeanUtils to transfer properties over to a JavaBean. You’ve seen this in action in the previous subsection.

Using “standard validations” means using the Validator framework. This is recommended because frequent accessing of form properties requires you to use DynaActionForm’s get() functions, which is an error prone approach since you’ve lost compile-time checking of your code. The get() function isn’t error prone, but your code might have bugs and there’s no way to find this out at compile time. This problem just doesn’t exist when you use conventional ActionForms, since the compiler does the check for you.

The second sentence refers to the fact that a dynamic form’s properties have a different EL reference than the conventional ActionForm’s properties. This can be a brick wall if you’re trying to replace regular ActionForms in an application that makes heavy use of EL.

Validating Dynamic Forms

Like ordinary ActionForms, there are two ways to validate data in a dynamic form:

Implement a subclass of DynaActionForm with a validate() function.

Use a subclass of DynaActionForm called org.apache.struts.validator. DynaValidatorForm, which uses the Validator framework. (See Chapter 15 for details.)

As with ActionForms using the Validator framework, you can run your own custom validations on top of the DynaValidatorForm by simply subclassing it and calling super.validate() in your subclass’s validate() function. I’ve described this technique in Chapter 15.

Used together with the Validator framework and BeanUtils, dynamic forms can completely eliminate the need to write any Java code for a form bean. But before we go into this, I’ll show you how the Registration webapp (see Chapter 5) might be implemented using dynamic forms.