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

Beginning Apache Struts - From Novice To Professional (2006)

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

48 C H A P T E R 5 T H E M V C D E S I G N P A T T E R N

You’ll learn more about ActionForm, Action, and struts-config.xml as this book progresses. At this stage, you should simply understand how MVC maps into Struts. Figure 5-6 depicts this mapping.

Figure 5-6. How MVC maps into Struts

Lifecycle of a Struts Request

The three categories of processing (simple/complex validation, business logic, and flow control) come into play when an incoming HTTP request is generated by a user submitting form data.

With servlet technology all such processing is done by servlets. As I mentioned in Chapter 2, Servlets are just Java objects, usually your subclasses of HttpServlet.

Even JSP pages are converted at runtime into servlet classes—actual .java source files. These autogenerated Java source files are later compiled.

Since Struts is built on servlet technology, it is no exception to this rule. All submissions of form data to a Struts application are intercepted by a single “master” servlet, an instance of ActionServlet, which is a part of Struts.

This master servlet is responsible for delegating the actual work of your application to your subclasses of ActionForm and Action (see Figure 5-6).

C H A P T E R 5 T H E M V C D E S I G N P A T T E R N

49

USUALLY?

Servlet technology is quite generic, meaning it is not at all tied to processing HTTP requests, although this is by far the most common use.

This is because servlets solve a number of generic problems that stem from client-server communication, such as object pooling and session management. Your servlet classes could implement the basic Servlet interface from scratch, or subclass the GenericServlet base class. You’d do this if you wanted to take advantage of servlet technology’s solutions to these generic problems.

If you’re primarily concerned with processing and displaying web pages, HttpServlet is the base class you’d subclass.

Note In addition to this, ActionServlet and its helper classed are responsible for many other behind- the-scenes things like pooling Action subclasses for efficiency, reading configuration data, or initializing Struts extensions called plug-ins. These activities are invisible to Struts developers, and are one good reason why Struts is so easy to use. You don’t have to know how the clock works to tell the time!

The incoming form data is processed in stages:

Data transfer: Form data is transferred to your ActionForm subclass.

Simple validation: The form data in this subclass is passed through simple validation. If simple validation fails, then the user is automatically presented with the form with the error messages detailing the errors. This is a typical scenario, but the details would depend on how you configured Struts. We’ll go through the mechanics of simple validation in Chapter 6 and tackle configuration in Chapter 9.

Further processing: Form data is next sent to your Action subclass for complex validation and the processing of business logic. We’ll discuss this in Chapter 7.

Your Action subclass also specifies the “next” page to be displayed, and this ends the processing of a single request.

Frameworks for the Model

In a previous section, I sang the joys of designing the Model portions of your webapp first, so Struts’ lack in this area might come as a bit of a letdown.

50

C H A P T E R 5 T H E M V C D E S I G N P A T T E R N

One reason for this glaring deficiency in Struts is because there are already several frameworks to help you with data access and persistence. These go under the monikers “persistence framework/layer” or “object/relational (O/R) persistence service.” The reason these frameworks exist is to persist data, and do it well. You may use any one of these in conjunction with Struts. So you see, it isn’t a deficiency at all but just good design.

There are two different approaches to data persistence:

Persist the classes: This approach lets you store and retrieve any Java object to/from a relational database. You are provided with a set of classes, which you must use to persist and retrieve your own Java classes. An example of this approach is Hibernate, from Apache.

Classes that persist: In this approach, you specify a description for your database tables and their interrelationships. The persistence framework autogenerates Java classes (source code), which have mappings to your database tables. These autogenerated classes have save(), select(), and delete() functions to allow persistence and retrieval of data objects. A prime example of this approach is Torque, also from Apache.

The “persist the classes” approach is by far the more flexible of the two, since you could potentially persist any Java class, including legacy ones, or third-party classes. The “classes that persist” approach, on the other hand, lacks this capability but produces a cleaner separation between Model and Controller.

I should warn you that while this comparison between approaches is valid, you can’t use it to choose between products like Hibernate and Torque. Hibernate, for example, provides many features beyond what I’ve described here. These extra features are what you’d want to consider when choosing a persistence framework.

In Appendix A, I give you simple examples of how to use Hibernate and Torque, and also (shameless plug) a simple, open source, “classes that persist” framework called Lisptorq, developed by the company I work for.

Useful Links

Hibernate: www.hibernate.org/

Torque: http://db.apache.org/torque/

Lisptorq: www.thinksquared.net/dev/lisptorq/

C H A P T E R 5 T H E M V C D E S I G N P A T T E R N

51

Summary

The Model-View-Controller (MVC) design pattern brings many benefits to the webapp that implements it.

Implementing MVC is difficult, which is why you need a web application framework like Struts.

Struts only places restrictions on the View and Controller. You are free to implement the Model portion in any way you wish.

Two very popular persistence frameworks are Hibernate and Torque.

C H A P T E R 6

■ ■ ■

Simple Validation

One common element of web applications is form validation. This involves running checks to ensure that user input conforms to the system’s expectations. In the previous chapter we saw how these checks naturally fall into two categories:

Simple validation: Validations that do not require complex processing or business logic. This includes, for example, checks that mandatory fields are filled, that user input conforms to the right format (email addresses, postal codes, or credit card numbers), and a variety of other checks.

Complex validation: Validations that are dependent on processing of business logic. For example, a check for a duplicate user ID would qualify as a complex validation because it involves reading data from a Model component.

Also in the previous chapter, I briefly described how Struts fits into MVC. You also saw how for the Controller, Struts makes a distinction between “simple” and “complex” validations of user input by using different classes to perform them.

Struts makes this distinction for two reasons. First, they involve significantly different operations and are usually maintained separately. The second reason is to give you a powerful tool (the Validator framework), which greatly relieves the tedium of doing simple validation.

In this chapter, we’ll focus on the basics of simple validation.

Processing Simple Validations

In addition to using different classes for simple and complex validations, Struts processes these validations differently too.

When a user submits a form, Struts first runs simple validations on it. If the validations fail, Struts sends the user the same form, with the same data the user keyed in but with error messages corresponding to each field with an error.

53

54

C H A P T E R 6 S I M P L E V A L I D A T I O N

Only after the simple validations pass will Struts send the form data for further processing. This might include performing complex validations or data transformations or storing data into the database.

Anatomy of ActionForm

The central class for performing simple validations is

org.apache.struts.action.ActionForm

Struts requires you to associate each form displayed to the user with your subclass of ActionForm.

Your subclass of ActionForm does two things:

It holds all form data: Your subclass of ActionForm must have getters and setters corresponding to each property of the form.

It performs simple validation: Your subclass must override the validate() function, if you want to perform any simple checks.

Figure 6-1 illustrates this relationship between ActionForm, your ActionForm subclass, and the input HTML form for which your subclass stores data.

Figure 6-1. Subclassing ActionForm

When a user submits a form, Struts populates the associated ActionForm subclass with the data, then calls validate() to perform any simple validations required. This function looks like this:

C H A P T E R 6 S I M P L E V A L I D A T I O N

55

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request)

Note For the time being, (and for your sanity!) you may safely ignore the ActionMapping and HttpServletRequest classes, which are validate()’s arguments. At this stage, the only important class apart from ActionForm is ActionErrors. We’ll come back to the other two classes in future chapters.

ActionErrors, which is validate()’s return value, is essentially like a HashMap to store error messages by key. If validate() returns a null value (or an empty ActionErrors instance), the validation passes.

If validate() returns a non-empty ActionErrors instance, the validation fails, and Struts redisplays the form to the user along with any error messages.

Listing 6-1 illustrates how you might implement the ActionForm subclass corresponding to the Registration webapp form of Chapter 5.

Listing 6-1. RegistrationForm.java

package net.thinksquared.registration.struts;

import javax.servlet.http.*; import org.apache.struts.action.*;

public final class RegistrationForm extends ActionForm{

private String _userid = null; private String _pwd = null; private String _pwd2 = null;

/**

*getXXX and setXXX functions

*corresponding to form properties

*/

public String getUserid(){ return _userid; }

public void setUserid(String userid){ _userid = userid; }

public String getPassword(){ return _pwd; }

public void setPassword(String pwd){ _pwd = pwd; }

public String getPassword2(){ return _pwd2; }

public void setPassword2(String pwd2){ _pwd2 = pwd2; }

56

C H A P T E R 6 S I M P L E V A L I D A T I O N

/**

*Validate the user input. Called automatically

*by Struts framework.

*/

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request){

//create a blank ActionErrors ActionErrors errors = new ActionErrors();

//check for a blank user id if( null == _userid ){

errors.add("userid",

new ActionMessage("reg.error.userid.missing"));

}

//check password 1 == password 2 if( !_pwd.equals(_pwd2)){

errors.add("password",

new ActionMessage("reg.error.password.mismatch"));

}

return errors;

}

}

Recall that the Registration webapp’s user input consisted of three fields: a user ID, a password, and the password confirmation. The first thing to note from Listing 6-1 is that this ActionForm subclass contains getters and setters for each of these three fields.

The body of validate() follows a simple pattern:

1.Create a blank ActionErrors instance.

2.Run a check for each field, and populate the ActionErrors instance with error messages.

3.Return the ActionErrors instance.

In Listing 6-1, I’ve only put in a check for a blank user ID and a test to see if the password and its confirmation were the same. In a real application, you might want to incorporate further checks to ensure that the password isn’t too short or too long and that the user ID doesn’t contain nonalphanumeric characters. As you can see, even for a trivial application like this, you’d need quite a few checks!

Note

C H A P T E R 6 S I M P L E V A L I D A T I O N

57

One thing you should not put in here is the check to see if the user ID is taken. You could actually do this, for example, by adding the code shown in Listing 6-2.

Listing 6-2. Code to Check for a Duplicate User ID

//test for duplicate userid if(User.exists(_userid)){

errors.add("userid",

new ActionMessage("reg.error.userid.exists"));

}

But you should not do this because it isn’t a simple validation, since it involves calling Model code (the User class from Listing 5-1):

if(User.exists(_userid)){...

and therefore, should be excluded from your subclass of ActionForm. This rule (you could call it a “best practice”) has practical benefits. You’ll see this later when I describe the Validator framework in Part 2 of this book.

To summarize, each form displayed to the user is associated with an ActionForm subclass. This subclass stores the form data, and exposes a validate() function that Struts calls automatically in order to run validations.

There’s still one loose string, which is ActionErrors, and I’ll describe this next.

Using ActionErrors

Struts uses the ActionErrors instance returned by validate() to display the error messages on the redisplayed form.

ActionErrors (note the plural) is very much like a HashMap, and it is used to store error messages, indexed by error keys. The error message is not a String, but an instance of

ActionMessage.

In earlier versions of Struts, the error message was an instance of ActionError (note the singular), but that’s been deprecated in favor of ActionMessage.

ActionMessages are added to an ActionErrors instance using the add() function:

public void add(String key, ActionMessage message)

You can think of this as analogous to the put() function on a HashMap.

The constructor for ActionMessage (see Listing 6-1) might seem a little cryptic: