Struts User's Guide
Quick Links
Home
Table of Contents
Introduction
Model Components
View Components
Controller Components
Resources
Who We Are
3. Building View Components
3.1 Overview

This chapter focuses on the task of building the View components of the application, which will primarily be created using JavaServer Pages (JSP) technology. In particular, Struts provides support for building internationalized applications, as well as for interacting with input forms. Several other topics related to the View components are briefly discussed.

3.2 Internationalized Messages

A few years ago, application developers could count on having to support only residents of their own country, who are used to only one (or sometimes two) languages, and one way to represent numeric quantities like dates, numbers, and monetary values. However, the explosion of application development based on web technologies, as well as the deployment of such applications on the Internet and other broadly accessible networks, have rendered national boundaries invisible in many cases. This has translated (if you will pardon the pun) into a need for applications to support internationalization (often called "i18n" because 18 is the number of letters in between the "i" and the "n") and localization.

Struts builds upon the Java platform to provide assistance for building internationalized and localized applications. The key concepts to become familiar with are:

  • Locale - The fundamental Java class that supports internationalization is java.util.Locale. Each Locale represents a particular choice of country and language (plus an optional language variant), and also a set of formatting assumptions for things like numbers and dates.
  • ResourceBundle - The java.util.ResourceBundle class provides the fundmental tools for supporting messages in multiple languages. See the Javadocs for the ResourceBundle class, and the information on Internationalization in the documentation bundle for your JDK release, for more information.
  • PropertyResourceBundle - One of the standard implementations of ResourceBundle allows you to define resources using the same "name=value" syntax used to initialize properties files. This is very convenient for preparing resource bundles with messages that are used in a web application, because these messages are generally text oriented.
  • MessageFormat - The java.text.MessageFormat class allows you to replace portions of a message string (in this case, one retrieved from a resource bundle) with arguments specified at run time. This is useful in cases where you are creating a sentence, but the words would appear in a different order in different languages. The placeholder string {0} in the message is replaced by the first runtime argument, {1} is replaced by the second argument, and so on.
  • MessageResources - The Struts class org.apache.struts.util.MessageResources lets you treat a set of resource bundles like a database, and allows you to request a particular message string for a particular Locale (normally one associated with the current user) instead of for the default Locale the server itself is running in.

Please note that the i18n support in a framework like Struts is limited to the presentation of internationalized text and images to the user. Support for Locale specific input methods (used with languages such as Japanese, Chinese, and Korean) is left up to the client device, which is usually a web browser.

For an internationalized application, follow the steps described in the Internationalization document in the JDK documentation bundle for your platform to create a properties file containing the messages for each language. An example will illustrate this further:

Assume that your source code is created in package com.mycompany.mypackage, so it is stored in a directory (relative to your source directory) named com/mycompany/mypackage. To create a resource bundle called com.mycompany.mypackage.MyResources, you would create the following files in the com/mycompany/mypackage directory:

  • MyResources.properties - Contains the messages in the default language for your server. If your default language is English, you might have an entry like this: prompt.hello=Hello
  • MyResources_xx.properties - Contains the same messages in the language whose ISO language code is "xx" (See the ResourceBundle Javadoc page for a link to the current list). For a French version of the message shown above, you would have this entry: prompt.hello=Bonjour You can have resource bundle files for as many languages as you need.

When you configure the controller servlet in the web application deployment descriptor, one of the things you will need to define in an initialization parameter is the base name of the resource bundle for the application. In the case described above, it would be com.mycompany.mypackage.MyResources.

<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  <init-param>
    <param-name>application</param-name>
    <param-value>com.mycompany.mypackage.MyResources</param-value>
  </init-param>
  <.../>
</servlet>

The important thing is for the resource bundle to be found on the class path for your application. Another approach is to store the MyResources.properties file in your application's classes folder. You can then simply specify "myResources" as the application value. Just be careful it is not deleted if your build script deletes classes as part of a "clean" target.

If it does, here is an Ant task to run when compiling your application that copies the contents of a src/conf directory to the classes directory:

        <!-- Copy any configuration files -->
        <copy todir="classes">
            <fileset dir="src/conf"/>
        </copy>
       
3.3 Forms and FormBean Interactions

At one time or another, most web developers have built forms using the standard capabilities of HTML, such as the <input> tag. Users have come to expect interactive applications to have certain behaviors, and one of these expectations relates to error handling -- if the user makes an error, the application should allow them to fix just what needs to be changed -- without having to re-enter any of the rest of the information on the current page or form.

Fulfilling this expectation is tedious and cumbersome when coding with standard HTML and JSP pages. For example, an input element for a username field might look like this (in JSP):

<input type="text" name="username"
      value="<%= loginBean.getUsername() %>"/>

which is difficult to type correctly, confuses HTML developers who are not knowledgeable about programming concepts, and can cause problems with HTML editors. Instead, Struts provides a comprehensive facility for building forms, based on the Custom Tag Library facility of JSP 1.1. The case above would be rendered like this using Struts:

<html:text property="username"/>

with no need to explicitly refer to the JavaBean from which the initial value is retrieved. That is handled automatically by the framework.

HTML forms are sometimes used to upload other files. Most browsers support this through a <input type="file"> element, that generates a file browse button, but it's up to the developer to handle the incoming files. Struts handles these "multipart" forms in a way identical to building normal forms. In the next section, we will cover using Struts to create a simple login form, and also a simple mulitpart form.

3.3.1 Building Forms With Struts

A complete example of a login form will illustrate how Struts makes dealing with forms much less painful than using straight HTML and standard JSP facilities. Consider the following page (based on the example application included with Struts) named logon.jsp:


<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld"
        prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld"
        prefix="bean" %>
<html:html>
<head>
<title>
  <bean:message key="logon.title"/>
</title>
<body bgcolor="white">
<html:errors/>
<html:form action="/logon" focus="username">
<table border="0" width="100%">
  <tr>
    <th align="right">
      <html:message key="prompt.username"/>
    </th>
    <td align="left">
      <html:text property="username"
                     size="16"/>
    </td>
  </tr>
  <tr>
    <th align="right">
      <html:message key="prompt.password"/>
    </th>
    <td align="left">
      <html:password property="password"
                         size="16"/>
    </td>
  </tr>
  <tr>
    <td align="right">
      <html:submit>
        <bean:message key="button.submit"/>
      </html:submit>
    </td>
    <td align="right">
      <html:reset>
        <bean:message key="button.reset"/>
      </html:reset>
    </td>
  </tr>
</table>
</html:form>
</body>
</html:html>

The following items illustrate the key features of form handling in Struts, based on this example:

  • The taglib directive tells the JSP page compiler where to find the tag library descriptor for the Struts tag library. In this case, we are using bean as the prefix that identifies tags from the struts-bean library, and "html" as the prefix that identifies tags from the struts-html library. Any desired prefix can be used.
  • This page uses several occurrences of the message tag to look up internationalized message strings from a MessageResources object containing all the resources for this application. For this page to work, the following message keys must be defined in these resources:
    • logon.title - Title of the logon page
    • prompt.username - A "Username:" prompt string
    • prompt.password - A "Password:" prompt string
    • button.submit - "Submit" for the button label
    • button.reset - "Reset" for the button label
    When the user logs on, the application can store a Locale object in the user's session. This Locale will be used to select messages in the appropriate language. This makes it easy to implement giving the user an option to switch languages -- simply change the stored Locale object, and all messages are switched automatically.
  • The errors tag displays any error messages that have been stored by a business logic component, or nothing if no errors have been stored. This tag will be described further below.
  • The form tag renders an HTML <form> element, based on the specified attributes. It also associates all of the fields within this form with a session scoped FormBean that is stored under the key logonForm. The Struts developer provides the Java implementation of this form bean, subclassing the Struts class ActionForm. This bean is used to provide initial values for all of the input fields that have names matching the property names of the bean. If an appropriate bean is not found, a new one will be created automatically, using the specified Java class name.
  • The form bean can also be specified in the Struts configuration file, in which case the Name and Type can be omitted here. See "The Action Mappings Configuration File" for details.)
  • The text tag renders an HTML <input> element of type "text". In this case, the number of character positions to occupy on the browser's screen has been specified as well. When this page is executed, the current value of the username property of the corresponding bean (that is, the value returned by getUsername()).
  • The password tag is used similarly. The difference is that the browser will echo asterisk characters, instead of the input value, as the user types their password.
  • .
  • The submit and reset tags generate the corresponding buttons at the bottom of the form. The text labels for each button are created using message tags, as with the prompts, so that these values are internationalized.

Handling multipart forms is also easy. Obviously when you create a multipart form you're creating a form that has at least one input of type "file". The first step to creating a multipart form is to utilize the struts-html taglib to create the presentation page:


<%@page language="java">
<%@taglib uri="/WEB-INF/struts-html.tld"
       prefix="html">
<html:form action="uploadAction.do">
  Please Input Text:
  <html:text property="myText"><br/>
  Please Input The File You Wish to Upload:<br/>
  <html:file property="myFile"><br />
  <html:submit />
</html:form>

The next step is to create your ActionForm bean:


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
public class UploadForm extends ActionForm {
  protected String myText;
  protected FormFile myFile;
  public void setMyText(String text) {
    myText = text;
  }
  public String getMyText() {
    return myText;
  }
  public void setMyFile(FormFile file) {
    myFile = file;
  }
  public FormFile getMyFile() {
    return myFile;
  }
}

Look at the Javadocs for FormFile to see the methods it exposes to manipulate files in file uploading. Also look at the Javadocs for ActionServlet and ActionMapping for the various parameters you can specify to change how files are uploaded. Basically in your peform() method in your action class you would call ((UploadForm) form).getMyFile() to retrieve the FormFile and do what you want with it.

3.3.2 Input Field Types Supported

Struts defines HTML tags for all of the following types of input fields, with hyperlinks to the corresponding reference information.

In every case, a field tag must be nested within a form tag, so that the field knows what bean to use for initializing displayed values.

3.3.3 Other Useful Presentation Tags

There are several tags useful for creating presentations, consult the documentation on each specific tag library, along with the Tag Developers Guides, for more information:

  • [logic] iterate repeats its tag body once for each element of a specified collection (which can be an Enumeration, a Hashtable, a Vector, or an array of objects).
  • [logic] present depending on which attribute is specified, this tag checks the current request, and evaluates the nested body content of this tag only if the specified value is present. Only one of the attributes may be used in one occurrence of this tag, unless you use the property attribute, in which case the name attribute is also required. The attributes include cookie, header, name, parameter, property, role, scope, and user.
  • [logic] notPresent the companion tag to present, notPresent provides the same functionality when the specified attribute is not present.
  • [html] link generates a HTML <a> element as an anchor definition or a hyperlink to the specified URL, and automatically applies URL encoding to maintain session state in the absence of cookie support.
  • [html] img generates a HTML <img> element with the ability to dynamically modify the URLs specified by the "src" and "lowsrc" attributes in the same manner that <html:link> can.
  • [bean] parameter retrieves the value of the specified request parameter, and defines the result as a page scope attribute of type String or String[].
3.3.4 Automatic Form Validation

In addition to the form and bean interactions described above, Struts offers an additional facility to validate the input fields it has received. To utilize this feature, override the following method in your ActionForm class:

public ActionErrors
  validate(ActionMapping mapping,
    HttpServletRequest request);

The validate() method is called by the controller servlet after the bean properties have been populated, but before the corresponding action class's perform() method is invoked. The validate() method has the following options:

  • Perform the appropriate validations and find no problems -- Return either null or a zero-length ActionErrors instance, and the controller servlet will proceed to call the perform() method of the appropriate Action class.
  • Perform the appropriate validations and find problems -- Return an ActionErrors instance containing ActionError's, which are classes that contain the error message keys (into the application's MessageResources bundle) that should be displayed. The controller servlet will store this array as a request attribute suitable for use by the <html:errors> tag, and will forward control back to the input form (identified by the input property for this ActionMapping).

As mentioned earlier, this feature is entirely optional. The default implementation of the validate() method returns null, and the controller servlet will assume that any required validation is done by the action class.

One common approach is to perform simple, prima facia validations using the ActionForm validate() method, and then handle the "business logic" validation from the Action.

An optional package for performing ActionForm validations is available in the Nightly Build and from David Winterfeldt's Web site.

3.4 Other Presentation Techniques

Although the look and feel of your application can be completely constructed based on the standard capabilities of JSP and the Struts custom tag library, you should consider employing other techniques that will improve component reuse, reduce maintenance efforts, and/or reduce errors. Several options are discussed in the following sections.

3.4.1 Application-Specific Custom Tags

Beyond using the custom tags provided by the Struts library, it is easy to create tags that are specific to the application you are building, to assist in creating the user interface. The example application included with Struts illustrates this principle by creating the following tags unique to the implementation of this application:

  • checkLogon - Checks for the existence of a particular session object, and forwards control to the logon page if it is missing. This is used to catch cases where a user has bookmarked a page in the middle of your application and tries to bypass logging on, or if the user's session has been timed out.
  • linkSubscription - Generates a hyperlink to a details page for a Subscription, which passes the required primary key values as request attributes. This is used when listing the subscriptions associated with a user, and providing links to edit or delete them.
  • linkUser - Generates a hyperlink to a details page for a User, which passes the required primary key values as request attributes.

The source code for these tags is in the src/example directory, in package org.apache.struts.example, along with the other Java classes that are used in this application.

3.4.2 Page Composition With Includes

Creating the entire presentation of a page in one JSP file (with custom tags and beans to access the required dynamic data) is a very common design approach, and was employed in the example application included with Struts. However, many applications require the display of multiple logically distinct portions of your application together on a single page.

For example, a portal application might have some or all of the following functional capabilities available on the portal's "home" page:

  • Access to a search engine for this portal.
  • One or more "news feed" displays, with the topics of interest customized from the user's registration profile.
  • Access to discussion topics related to this portal.
  • A "mail waiting" indicator if your portal provides free email accounts.

The development of the various segments of this site is easier if you can divide up the work, and assign different developers to the different segments. Then, you can use the include capability of JavaServer Pages technology to combine the results into a single result page, or use the include tag provided with Struts. There are three types of include available, depending on when you want the combination of output to occur:

  • An <%@ include file="xxxxx" %> directive can include a file that contains Java code or JSP tags. The code in the included file can even reference variables declared earlier in the outer jsp page. The code is inlined into the other JavaServer Page before it is compiled so it can definately contain more than just HTML.
  • The include action (<jsp:include page="xxxxx" flush="true" />) is processed at request time, and is handled transparently by the server. Among other things, that means you can conditionally perform the include by nesting it within a tag like equals by using it's parameter attribute.
  • The bean:include tag takes either a an argument "forward" representing a logical name mapped to the jsp to include, or the "id" argument, which represents a page context String variable to print out to the jsp page.

Another approach to this would be to use the Struts Template Tag library. See the Developer's Guide for details.

Tiles is an alternative to the original Template Tag library, offering several enhancements and new capabilities. Tiles is available in the Nightly Build, and from Cedric Dumoulin's Web site.

3.4.3 Image Rendering Components

Some applications require dynamically generated images, like the price charts on a stock reporting site. Two different approaches are commonly used to meet these requirements:

  • Render a hyperlink with a URL that executes a servlet request. The servlet will use a graphics library to render the graphical image, set the content type appropriately (such as to image/gif), and send back the bytes of that image to the browser, which will display them just as if it had received a static file.
  • Render the HTML code necessary to download a Java applet that creates the required graph. You can configure the graph by setting appropriate initialization parameters for the applet in the rendered code, or you can have the applet make its own connection to the server to receive these parameters.
3.4.4 Rendering Text

Some applications require dynamically generated text or markup, such as XML. If a complete page is being rendered, and can be output using a PrintWriter, this is very easy to do from an Action:

           response.setContentType("text/plain"); // or text/xml
           PrintWriter writer = response.getWriter();
           // use writer to render text
           return(null);
         

Next: Building Controller Components


Copyright (c) 2000-2002, Apache Software Foundation