Section 19.6. Views in Struts


19.6. Views in Struts

Views present information from the model. The typical view architecture used with Struts is JSP, but in fact, any servletthat is, any path mapped by the container to which Struts can forward controlcan serve as a view. We will stick to JSP because it is so common, but increasingly developers are becoming interested in view frameworks with simpler syntax or other niceties, such as Velocity. Thankfully, Struts can still be used if you decide to pursue these alternatives.

The interesting question regarding views is how they communicate with Actions and the request processor. Views need to transfer data in two ways. First, they must be able to pick up data that is provided to them by Actions. This part is easy: an Action puts objects into request or session scope, just as any servlet would; references to these objects may then be retrieved by the JSP from request or session scope directly through Java scriplets referencing the HttpServletRequest or HttpSession objects or through tags, such as those provided by JSTL (JSP Standard Tag Library) or the Struts Tag Libraries (more on this topic later in this chapter).

The second requirement for data transfer in the view is to obtain values that should be displayed in a form. Enter the ActionForm.

19.6.1. The ActionForm Class

As we discuss the ActionForm class, please refer to Figure 19-2. The key to understanding ActionForms is to notice that they are situated at a number of places in the request/response lifecycle. In particular, form data gets to an Action via an ActionForm parameter on the execute method, and an ActionForm bean can be tapped by Struts tags to ensure that form data is populated correctly in a JSP.

A class extending ActionForm represents the parameters submitted by an HTTP POST request. Your ActionForm must have JavaBeans-style getters and setters. In addition, you may override utility methods from the base ActionForm. The reset method is called by the framework to return the instance to its original state, and the validate method is called after population to give your ActionForm a chance to verify that its values are acceptable. Example 19-2 shows an ActionForm that represents the username and password fields for a login form.

Example 19-2. Login ActionForm
 import javax.servlet.http.HttpServletRequest;   import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages;   public class LoginActionForm extends ActionForm {     private String username;   private String password;     public String getUsername(  ) {     return username;   }     public void setUsername(String username) {     this.username = username;   }     public String getPassword(  ) {     return password;   }     public void setPassword(String password) {     this.password = password;   }     public ActionErrors validate(ActionMapping mapping,       HttpServletRequest request) {     ActionErrors actionErrors = new ActionErrors(  );     if (password == null || password.trim(  ).length(  ) == 0) {       actionErrors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(           "errors.required", "Password"));     }     if (username == null || username.trim(  ).length(  ) == 0) {       actionErrors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(           "errors.required", "Username"));     }     return actionErrors;   } } 

19.6.1.1. Validation

After the Struts controller populates the bean properties, it calls the validate method to give your ActionForm an opportunity to return to Struts any errors that are encountered. In LoginActionForm (see Example 19-2), we require a value for the username and password properties, so when those properties are null or contain only whitespace, we generate an ActionMessage object representing the error message that should be conveyed back to the view. We return an instance of ActionErrors that includes a list of all ActionMessages encountered. (Before Struts 1.2, ActionErrors was used to represent an error; that class is deprecated in favor of ActionMessage.)

The first parameter on the ActionErrors.add( ) method is ActionMessages.GLOBAL_MESSAGE. This key is used elsewhere in Struts to look up the list of ActionErrors from the scope (request or session) where the ActionForm is stored. ActionMessage has a number of constructors. Here we use the constructor that accepts two String parameters. The first is the name of a property (errors.required) defined in application.properties. This property gives the error message template that should be used to display the error to the user. The second parameter is the name of the field for which we are noting an error; this field name will be plugged into the template. Here are the error formatting properties and values from application.properties:

 errors.required={0} is required. errors.minlength={0} must be at least {1} characters. errors.credentials.login=Login Error Unable to log you in - username and password  combination not found. errors.isbn.length=ISBN must be 10 characters. 

Thus in our example, if the username is missing, the error message template {0} is required. will be obtained via the key errors.required, and the {0} will be filled by the word Username.

19.6.1.2. Scope

Remember that a specific ActionForm class is bound to a name in the struts-config.xml file. That name is referenced in an <action> element:

 <action path="/bookDisplay" name="BookDisplayForm" scope="session"     type="com.oreilly.jent.struts.library.ui.BookDisplayAction"/> 

This definition can also include an attribute defining the scope in which the ActionForm instance will live. If the ActionForm instance is needed for only one request/response sequence, specify request scope. You may also specify session scope if the ActionForm should be preserved from Action to Action. For instance, if you have actions that must "look back" to forms that have been submitted in earlier request/response interactions (say, for a wizard), session scope provides you with a means to keep that data available. In the example application, there is little need to use session scope because each Struts Action puts its response into request scope. There is one exception, however. In our BookDisplayForm, we want the "safe mode" (which suppresses the Check In/Check Out buttons) to be "sticky," so that it stays on no matter where the user navigates in the application. By putting the form into session scope, the very same form bean is available (and reutilized) every time the page is displayed. Thus once we turn on safe mode, the mode is persistent, even if you move off of the page.[5] Indeed, it is sticky even if we log outthis is just one reason why logout operations typically invalidate their sessions.

[5] Also notice that, since the same form is reused, the same book will be selected if we click Return Home and then go back to View Books. To experiment with this, change the scope to request scope, redeploy, and observe how safe mode is forgotten after clicking the Return Home link. This is because a form bean in request scope is created afresh every time it is needed.

19.6.1.3. The ActionForm and the JSP

Let's look at how this ActionForm is utilized in the JSP page that makes up a login page.

To create a page with a form and its associated ActionForm and Action, the Struts developer must typically manipulate five or six files:

  • The Struts configuration file (struts-config.xml).

  • The JSP page that includes the HTML form. This JSP page will include Struts tags to reference an Action and tags from the JSTL.

  • The Java code for the ActionForm that defines the parameters for the HTML form's POST action.

  • The Java code for the Action that defines what should be done with the parameters.

  • A Struts property file (typically application.properties).

Additionally, if you are using the Struts validator, validation definitions will be in a file such as validation.xml.

Here's a JSP that uses our LoginActionForm:

 <%@ include file="/includes/setup.inc" %> <html>   <head>     <title>       Struts Library Login     </title>     <link rel="stylesheet" type="text/css"         href='<c:url value="/css/app.css"/>'>   </head>   <body>     <h1>       Struts Library Login     </h1>     <hr>     In order to borrow or add books you must log into the Library     application.     <html:errors/>     <html:form action="/login" focus="username">       <table>         <tr>           <td>             Username:           </td>           <td>             <html:text property="username"/>           </td>         </tr>         <tr>           <td>             Password:           </td>           <td>             <html:password property="password" redisplay="false"/>           </td>         </tr>       </table>       <p>         <html:submit/>       </p>     </html:form>   </body> </html> 

The first line of the JSP is a static include of setup.inc that we use in all of the JSPs in the sample application. This include file defines the prefixes used in the application for the Struts tags and JSTL tags. Additionally, a reference to the current list of books is copied into request scope from a member of an application-scoped reference to the singleton instance of com.oreilly.jent.struts.library.model.Library (see LibraryPlugIn.java). To learn about the JSTL <c:set> and <c:url> tags, see Chapter 4.

Moving down the page, the first Struts-specific item is the <html:errors/> tag. Any errors found during the form validation phase will be displayed here. Notice that this tag is XML, not HTML. Therefore, you must always provide a close tag. We might have written <html:errors></html:errors>, but we are using the XML shorthand syntax for an "empty" tag <html:errors/>.

The form is defined with the <html:form>, <html:text>, and <html:password> tags. Struts tags are discussed in more detail later in this chapter; for now, note that these tags are rendered to HTML tags such as <form>, <input type="text">, and <input type="password">. In the <html:form> tag, the value (/login) of the required action attribute defines the action mapping in struts-config.xml to which the Struts ActionServlet will delegate the POST request.

If you are running the application, a useful exercise at this point is to look at the HTML that is produced by this assortment of Struts and JSTL tags:

     <form name="LoginForm" method="post"         action="/jent-struts/login.do">       <table>         <tr>           <td>               Username:           </td>           <td>             <input type="text" name="username" value="">           </td>         </tr>         <tr>           <td>             Password:           </td>             <td>             <input type="password" name="password" value="">           </td>         </tr>       </table>       <p>         <input type="submit" value="Submit">       </p>     </form> 

As you can see from the generated HTML, Struts has made a number of substitutions. The most important one is that the action attribute on the form has the value /jent-struts/login.do Struts has included the context path (/jent-struts) as well as appended the .do extension (specified in the servlet mapping in the web.xml file), marking this as a Struts target that will be intercepted by the Struts servlet.[6]

[6] Perhaps you are wondering, as I did, how Struts figures out that you have mapped the ActionServlet to *.do since the developer is not required to define this mapping in any Struts configuration file. This information is not available through any of the standard classes such as ServletConfig. If you study the Struts source, you will discover that the Struts ActionServlet itself parses your web.xml file to dig up the mapping.

Now let's look briefly at the login action mapping in struts-config.xml:

     <action path="/login" name="LoginForm" scope="request"             validate="true" input="/login/index.jsp"             type="com.oreilly.jent.struts.library.ui.LoginAction">         <forward name="success" path="/login/success.jsp"                  redirect="false"/>         <forward name="failure" path="/login/index.jsp"                  redirect="false"/>     </action> 

Notice that the name of the form (LoginForm) is defined in struts-config.xml; the form name is not referenced directly in the JSP. Because LoginForm is mapped to the LoginActionForm class in the struts-config.xml file, you can control which specific class defines the form properties without changing your JSP.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net