Using Struts--A Detailed Example

This document shows how Struts is used to produce the example Basketball Database Application discussed in class.

Forwarding the URL to a Login Page

When a browser requests the URL http://redrock.d.umn.edu:8082/bball/, Tomcat consults the web.xml file for the bball web application (or, more accurately, checks the object created when web.xml is parsed at startup) and finds that the welcome-file for the webapp is index.jsp. This snippet shows the welcome-file element from web.xml.

Now look at the contents of index.jsp. This page uses the Struts tag logic:redirect and its forward attribute to look up the page to which visitors to this URL are to be redirected. Struts gets this information from the struts-config.xml file. Here is the global-forwards element from that file.

For the forward named welcome, the browser is to be redirected to the page at login.jsp. (Unless otherwise stated, all JSPs are stored in bball/pages.) Note that despite the use of the attribute name forward, the logic:redirect tag does a true redirect, which responds to the URL request with a message to the browser to make a new request to the redirected page. If a true forward had occurred, the browser would not have been involved, and the original request would have been directly sent to another resource on the server.

Web Forms and Form Beans

The login page login.jsp puts up a form for the user to fill out and submit. Here is a skeletal look at the login form in login.jsp

Note the Struts html:form tag and the html:text tags within its body. These render the part of the page that puts up the input fields for the user to enter. Note the property attribute of the html:text tag. These name properties of a Javabean that will hold the user name and password strings entered by the user. Recall that a Javabean is just a Java object that follows certain conventions for naming the accessor and mutator methods of its instance fields. In this case there are two fields called userName and password, and their access methods are called getUserName(), setUserName(), getPassword(), and setPassword(). Struts calls these beans form beans.

Struts will set the properties of the form bean in this example when the user submits the form. How does Struts know which bean the property attribute of the html:text tag is accessing?

Action Mappings

Form beans, JSP pages that access them, and what to do with form beans when a browser makes a request are specified in a Struts action mapping. Action mappings are made through the action subelement of the action-mappings element in the struts-config.xml file. Here is the login action element that links the basketball login page with a form bean, among other things.

Note first the input attribute of the action subelement, which specifies the page containing the input form. Note also the name attribute, which names the form bean whose properties the html:text tags access. Now Struts knows the name of the form bean - "loginForm" - to associate with the login page, but how is the class that actually implements the bean defined?

ActionForm Classes

Form beans and their classes are specified in the form-bean subelement of the form-beans element in the struts-config.xml file. Here is the loginForm form-bean element.

Here, the type attribute gives the Java class of the loginForm bean. Struts will find the class LoginForm in the form package located under WEB-INF/classes. This class must subclass the Struts framework's ActionForm class and may override a number of methods. Here is the definition of the LoginForm class.

Note that this class provides the required accessor and mutator methods that make it a Javabean. It also overrides the reset() and validate() methods. reset() is called by Struts whenever the login page is requested; it simply blanks out the input text fields. The validate() method is called when a user submits the page. It is intended to check that the form has simply been filled out properly; it does not do any user authentication. Here, the method merely checks that neither field is blank. Note that this check makes use of an ActionErrors object and a message resource bundle.

Message Resource Bundles

A resource bundle file allows configurable elements of a web application to be specified in a file, so that when changes to these elements are made, recompiling of Java classes can be avoided. Resource bundle files also make it easy to switch among locales for internationalization. Configurable elements are things like: Here is the resource bundle file BballMessageResources.properties for the basketball database web app.

Note that these properties are accessible from JSP pages. Here is the full basketball login page login.jsp.

Several Struts tags in this page (bean:message and html:img) refer to properties in the resource bundle file, for example:

Struts knows where to find these properties through the message-resources element in struts-config.xml. Here is the message-resources element from that page. Struts finds the properties in the file BballMessageResources.properties located at WEB-INF/classes.

The ActionErrors Object

When a user submits a login page with either the user name or password missing, the page is redisplayed with error messages included at the top of the form. This happens because the validate() method in the loginForm bean returns an ActionErrors object, which is a collection of ActionError objects. When validate() is entered, an empty collection is created. Then, if a login field is missing, an ActionError object is created and added to the collection. Note that the ActionError constructor in this case takes the property name global.error.login.requiredfield as its first parameter. The value of this property, the string
    <li>The {0} is required for login</li>
is an HTML list item that will be returned as part of the ActionError object. It takes an argument, specified by "{0}", whose value is found in the second parameter to the ActionError constructor. In this case it is the label of the field that the user failed to fill in. So, if the user did not provide the user name, the string returned in the ActionError will be:
    <li>The User Name is required for login</li>
How does Struts know that when a validation error occurs the login page should be redisplayed? Recall the login action element that links the basketball login page with a form bean.

Since the validate attribute is set to true, if the form bean's validate() method returns any errors, Struts will forward control back to the resource indicated by the input attribute, in this case the login page.

And how does the login page access the error messages? Recall that the page login.jsp is associated with the loginForm bean. The Struts tag html:errors accesses the bean and any ActionErrors that have been set for it. Here again is the login form, this time emphasizing the beginning of the form where any errors appear.

The html:errors tag renders a list in the form containing the text of any errors.

So, what happens if there are no validation errors? Then instead of forwarding to the login page, Struts passes control to an Action class object.

Action Classes

Recall again the login action element. This mapping, through the type attribute, specifies the class of an Action object that handles the processing of the login request if there are no validation errors. In this case, the object is of the class LoginAction in the action package located under WEB-INF/classes. This class must subclass the Struts framework's Action class and override the execute() method, which is invoked by Struts when the login page is submitted without validation errors. One of the parameters passed to execute() is the form bean associated with it.

Here is a diagram showing the relationships of the login page, the form bean, and the login action:

Here is the definition of the LoginAction class. Note that the execute() method uses the form bean passed to it and attempts to authenticate the user using classes and interfaces written for the application in the service and view packages included under WEB-INF/classes (see the SecurityService and UserView classes). In this case authentication is trivial, looking for user name "123" and password "456", but a real application would look up user permissions in a database.

Putting Objects into Session Scope

Once a user is properly logged in, his/her information needs to be available across requests for the duration of their session. LoginAction's execute() method shows how user information is encapsulated in a UserView object (essentially another Javabean) and stored as an attribute in session scope. Actions associated with other requests can then retrieve user information as long as the session is alive. Struts sessions by default live for 30 minutes when idle.

Declarative Exception Handling

Note that the login() method in the SecurityService class may throw an InvalidLoginException. However, this method is not protected by an enclosing try-catch. This is permissible since the enclosing execute() method is declared to throw a general Exception.

Struts knows what to do with an InvalidLoginException through the global-exceptions element in struts-config.xml. This element specifies the property name (attribute key) under which to find an error message in the resource bundle.

Action Forwarding

The execute() method must return an ActionForward object. In our example, in the case of login success an ActionForward object is retrieved from the ActionMapping object passed as the first parameter to execute(). How does an appropriate forward object get put on the mapping object?

Recall yet again the login action element. It contains named forward subelements. Struts builds ActionForward objects associated with each and stores them in the ActionMapping object passed to execute(), which can access them by name using the findForward() method. In our example, execute() always returns a forward associated with success. That forward passes control to a showoptions action. Here is a diagram showing that passing of control, among other things:

Here is the showoptions action element from struts-config.xml. Ultimately, this action will result in a page (options.jsp) allowing the user to choose a basketball database option, but first those options must be created. This is done by the execute() method in the ShowOptionsAction class.

This action differs from the login action in that the form bean passed to it (optionsForm) is used to set rather than get bean properties. Specifically, the options "Add Team" and "Add Player" are placed on a list, which is made the value of the options property on the form bean. However, in this case the class of the form bean is not provided by the webapp developer. Instead, it is dynamically created by Struts.

DynaActionForm Objects

Here is the optionsForm form-bean element. It specifies that the form bean will be of Struts type DynaActionForm and that it will have a property called options of type ArrayList. You should use this method of specifying form beans when input form validation is not required.

Note that the DynaActionForm class has a set() method that takes a property name, which should be one specified in the form-bean definition, and a value to set that property to.

It is important to recognize that the loginForm and optionsForm beans are used in contrasting ways:

options.jsp accesses the optionsForm bean so that it can iteratively display basketball database options in a selection box.

Iterating in JSPs

Here is the options.jsp page. Its central element is an ordinary HTML select tag whose body makes use of a Struts logic:iterate tag. This tag names a bean and the property over which it wants to iterate. In this example, the bean is optionsForm and the property is options.

logic:iterate also specifies an id attribute, whose value is used to refer to the individual objects in the collection being iterated over. The body of logic:iterate can then refer to the individual objects using this attribute value, which in this case is option. When this JSP is rendered, the values of the two string objects, "Add Team" and "Add Player", are written to the selection box using the Struts bean:write tag. Note that validation of the form does not occur, since the user does not fill in any input fields. This is assured by setting the validate attribute to false in the showoptions action element.

When the user makes a selection and submits the page (clicking the Do It button), the selected string is sent as the value of the options parameter in the HTTP request (don't confuse the HTML select tag's use of the name "options" with the similar use here by the Struts logic:iterate tag.) Struts then translates the request into the dispatchOptionForm bean and passes control to the dispatchOption action, as specified in the html:form tag's action attribute. The DispatchOptionAction class then forwards to either AddTeam.jsp or AddPlayer.jsp, depending on the option specified in the form bean.

Here is a diagram showing this flow of control:

Accessing a Database

Selecting the "Add Team" option in this example does nothing interesting, but selecting "Add Player" displays a page that allows the user to fill in player information and select a team and jersey number. The options for the team selection box are retrieved from a database table using custom tags in a way similar to how it was done in the custom tag lab exercise. Here is the AddPlayer.jsp page.

Pooled Database access is handled by Tomcat as described in the JSP lab exercise.

Includes and Links

The non-login JSPs in the bball webapp contain Logout links by including this header.jsp page. (This page and a corresponding footer page are stored in bball/include.) The link is rendered as an ordinary HTML anchor tag a whose body is a Struts html:img tag. Clicking on the image causes Struts to pass control to the logout action, as specified in the a tag's href attribute. The action mapping does not specify a form bean, since none is used by the LogoutAction class, which simply invalidates the user's session, if one exists, and forwards directly to the login page.

Here is a diagram showing this flow of control:

Finally

Here is the complete struts-config.xml file.

And, to put it all together, here is a diagram showing the interaction of all JSPs, form beans, and action objects: