blob: bc09da8717a736a7984671e96d1dc417251c56fc [file] [log] [blame]
<?xml version="1.0"?>
<document url="./building_view.xml">
<properties>
<author>Craig R. McClanahan</author>
<author>Mike Schachter</author>
<author>Ted Husted</author>
<author>Martin Cooper</author>
<author>Ed Burns</author>
<title>The Struts User's Guide - Building View Components</title>
</properties>
<body>
<chapter name="3. Building View Components" href="building_view">
<section name="3.1 Overview" href="overview">
<p>
This chapter focuses on the task of building the <i>View</i> 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.
</p>
</section>
<section name="3.2 Internationalized Messages" href="i18n">
<p>
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
<i>internationalization</i> (often called "i18n" because 18 is the number of
letters in between the "i" and the "n") and <i>localization</i>.
</p>
<p>
Struts builds upon the Java platform to provide assistance
for building internationalized and localized applications. The key concepts
to become familiar with are:
</p>
<ul>
<li><b>Locale</b> - The fundamental Java class that supports
internationalization is <code>java.util.Locale</code>. Each
<code>Locale</code> 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.</li>
<li><b>ResourceBundle</b> - The <code>java.util.ResourceBundle</code> class
provides the fundmental tools for supporting messages in multiple
languages. See the Javadocs for the <code>ResourceBundle</code> class,
and the information on Internationalization in the documentation bundle
for your JDK release, for more information.</li>
<li><b>PropertyResourceBundle</b> - One of the standard implementations of
<code>ResourceBundle</code> 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.</li>
<li><b>MessageFormat</b> - The <code>java.text.MessageFormat</code> 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 <code>{0}</code> in the message is replaced by
the first runtime argument, <code>{1}</code> is replaced by the second
argument, and so on.</li>
<li><b>MessageResources</b> - The Struts class
<code><a href="api/org/apache/struts/util/MessageResources.html">org.apache.struts.util.MessageResources</a></code> 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.</li>
</ul>
<p>
Please note that the i18n support in a framework like Struts is limited to the
<b>presentation</b> of internationalized text and images to the user.
Support for Locale specific <b>input methods</b> (used with languages
such as Japanese, Chinese, and Korean) is left up to the client device, which
is usually a web browser.
</p>
<p>
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:
</p>
<p>
Assume that your source code is created in package
<code>com.mycompany.mypackage</code>, so it is stored in a directory
(relative to your source directory) named
<code>com/mycompany/mypackage</code>. To create a resource bundle called
<code>com.mycompany.mypackage.MyResources</code>, you would create the
following files in the <code>com/mycompany/mypackage</code> directory:
</p>
<ul>
<li><b>MyResources.properties</b> - Contains the messages in the default
language for your server. If your default language is English, you
might have an entry like this:
<code>prompt.hello=Hello</code></li>
<li><b>MyResources_xx.properties</b> - 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:
<code>prompt.hello=Bonjour</code>
You can have resource bundle files for as many languages as you need.</li>
</ul>
<p>
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
<code>com.mycompany.mypackage.MyResources</code>.
</p>
<pre>
&lt;servlet>
&lt;servlet-name>action&lt;/servlet-name>
&lt;servlet-class>org.apache.struts.action.ActionServlet&lt;/servlet-class>
&lt;init-param>
&lt;param-name>application&lt;/param-name>
&lt;param-value>com.mycompany.mypackage.MyResources&lt;/param-value>
&lt;/init-param>
&lt;.../>
&lt;/servlet>
</pre>
<p>
The important thing is for the resource bundle to be found on the
class path for your application. Another approach is to store
the <code>MyResources.properties</code> file in your application's
<code>classes</code> 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.
</p>
<p>
If it does, here is an Ant task to run when compiling your application
that copies the contents of a <code>src/conf</code>
directory to the <code>classes</code> directory:
</p>
<pre>
&lt;!-- Copy any configuration files -->
&lt;copy todir="classes">
&lt;fileset dir="src/conf"/>
&lt;/copy>
</pre>
</section>
<section name="3.3 Forms and FormBean Interactions" href="form_beans">
<p>
At one time or another, most web developers have built forms using
the standard capabilities of HTML, such as the <code>&lt;input&gt;</code>
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.
</p>
<p>
Fulfilling this expectation is tedious and cumbersome when coding with
standard HTML and JSP pages. For example, an input element for a
<code>username</code> field might look like this (in JSP):
</p>
<pre>
&lt;input type="text" name="username"
value="&lt;%= loginBean.getUsername() %&gt;"/&gt;
</pre>
<p>
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:
</p>
<pre>
&lt;html:text property="username"/&gt;
</pre>
<p>
with no need to explicitly refer to the JavaBean from which the initial
value is retrieved. That is handled automatically by the framework.
</p>
<p>
HTML forms are sometimes used to upload other files. Most browsers
support this through a &lt;input type="file"&gt; 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.
</p>
</section>
<section name="3.3.1 Building Forms With Struts" href="forms">
<p>
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 <code>logon.jsp</code>:
</p>
<hr/>
<pre>
&lt;%@ page language="java" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld"
prefix="html" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld"
prefix="bean" %&gt;
&lt;html:html&gt;
&lt;head&gt;
&lt;title&gt;
&lt;bean:message key="logon.title"/&gt;
&lt;/title&gt;
&lt;body bgcolor="white"&gt;
&lt;html:errors/&gt;
&lt;html:form action="/logon" focus="username"&gt;
&lt;table border="0" width="100%"&gt;
&lt;tr&gt;
&lt;th align="right"&gt;
&lt;html:message key="prompt.username"/&gt;
&lt;/th&gt;
&lt;td align="left"&gt;
&lt;html:text property="username"
size="16"/&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th align="right"&gt;
&lt;html:message key="prompt.password"/&gt;
&lt;/th&gt;
&lt;td align="left"&gt;
&lt;html:password property="password"
size="16"/&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;html:submit&gt;
&lt;bean:message key="button.submit"/&gt;
&lt;/html:submit&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;html:reset&gt;
&lt;bean:message key="button.reset"/&gt;
&lt;/html:reset&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/html:form&gt;
&lt;/body&gt;
&lt;/html:html&gt;
</pre>
<hr/>
<p>
The following items illustrate the key features of form handling in Struts,
based on this example:
</p>
<ul>
<li>The <code>taglib</code> directive tells the JSP page compiler where to
find the <i>tag library descriptor</i> for the Struts tag library. In
this case, we are using <code>bean</code> 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.</li>
<li>This page uses several occurrences of the
message tag to look up internationalized
message strings from a <code>MessageResources</code> object containing
all the resources for this application. For this page to work, the
following message keys must be defined in these resources:
<ul>
<li><b>logon.title</b> - Title of the logon page</li>
<li><b>prompt.username</b> - A "Username:" prompt string</li>
<li><b>prompt.password</b> - A "Password:" prompt string</li>
<li><b>button.submit</b> - "Submit" for the button label</li>
<li><b>button.reset</b> - "Reset" for the button label</li>
</ul>
When the user logs on, the application can store a <code>Locale</code>
object in the user's session. This <code>Locale</code> 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 <code>Locale</code> object, and all messages are switched
automatically.</li>
<li>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.</li>
<li>The form tag renders an HTML
<code>&lt;form&gt;</code> 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 <code>logonForm</code>.
The Struts developer provides the Java implementation of this form
bean, subclassing the Struts class <code>ActionForm</code>.
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.
<li>The form bean can also be specified in the Struts configuration file,
in which case the Name and Type can be omitted
here. See "<a href="building_controller.html#config">The Action Mappings
Configuration File</a>" for details.)</li>
</li>
<li>The text tag renders an HTML
<code>&lt;input&gt;</code> 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 <code>username</code> property of the corresponding bean
(that is, the value returned by <code>getUsername()</code>).</li>
<li>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.</li>.
<li>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.</li>
</ul>
<p>
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:
</p>
<hr/>
<pre>
&lt;%@page language="java">
&lt;%@taglib uri="/WEB-INF/struts-html.tld"
prefix="html">
&lt;html:form action="uploadAction.do">
Please Input Text:
&lt;html:text property="myText">&lt;br/>
Please Input The File You Wish to Upload:&lt;br/>
&lt;html:file property="myFile">&lt;br />
&lt;html:submit />
&lt;/html:form>
</pre>
<hr/>
<p>
The next step is to create your ActionForm bean:
</p>
<hr/>
<pre>
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;
}
}
</pre>
<hr/>
<p>
Look at the <a href="../api/index.html">Javadocs</a> 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 <code>((UploadForm) form).getMyFile()</code>
to retrieve the FormFile and do what you want with it.
</p>
</section>
<section name="3.3.2 Input Field Types Supported" href="form_input">
<p>
Struts defines HTML tags for all of the following types of input fields,
with hyperlinks to the corresponding reference information.
</p>
<ul>
<li><a href="../struts-html.html#checkbox">checkboxes</a></li>
<li><a href="../struts-html.html#hidden">hidden</a> fields</li>
<li><a href="../struts-html.html#password">password</a> input fields</li>
<li><a href="../struts-html.html#radio">radio</a> buttons</li>
<li><a href="../struts-html.html#reset">reset</a> buttons</li>
<li><a href="../struts-html.html#select">select</a> lists with embedded option or options items</li>
<li><a href="../struts-html.html#option">option</a></li>
<li><a href="../struts-html.html#options">options</a></li>
<li><a href="../struts-html.html#submit">submit</a> buttons</li>
<li><a href="../struts-html.html#text">text</a> input fields</li>
<li><a href="../struts-html.html#textarea">textareas</a></li>
</ul>
<p>
In every
case, a field tag must be nested within a <code>form</code> tag, so that
the field knows what bean to use for initializing displayed values.
</p>
</section>
<section name="3.3.3 Other Useful Presentation Tags" href="presentation_tags">
<p>
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:
</p>
<ul>
<li>[logic] <a href="../struts-logic.html#iterate">iterate</a> 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).</li>
<li>[logic] <a href="../struts-logic.html#present">present</a> 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.
</li>
<li>[logic] <a href="../struts-logic.html#notPresent">notPresent</a> the companion tag to
present, notPresent provides the same functionality when the specified attribute
is not present.</li>
<li>[html] <a href="../struts-html.html#link">link</a> generates a HTML &lt;a&gt; 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.</li>
<li>[html] <a href="../struts-html.html#img">img</a> generates a HTML &lt;img&gt; element
with the ability to dynamically modify the URLs specified by the "src" and
"lowsrc" attributes in the same manner that &lt;html:link> can.
</li>
<li>[bean] <a href="../struts-bean.html#parameter">parameter</a> retrieves the value of the
specified request parameter, and defines the result as a page scope attribute of
type String or String[].</li>
</ul>
</section>
<section name="3.3.4 Automatic Form Validation" href="form_validation">
<p>
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:
</p>
<pre>public ActionErrors
validate(ActionMapping mapping,
HttpServletRequest request);
</pre>
<p>
The validate() method is called by the controller servlet after the bean
properties have been populated, but before the corresponding action class's
<code>perform()</code> method is invoked. The <code>validate()</code> method
has the following options:
</p>
<ul>
<li>Perform the appropriate validations and find no problems -- Return either
<code>null</code> or a zero-length ActionErrors instance, and the controller
servlet will proceed to call the <code>perform()</code> method of the
appropriate <code>Action</code> class.</li>
<li>Perform the appropriate validations and find problems -- Return an ActionErrors
instance containing <code>ActionError</code>'s, which are classes that contain
the error message keys (into the application's
<code>MessageResources</code> bundle) that should be displayed. The
controller servlet will store this array as a request attribute suitable
for use by the <code>&lt;html:errors&gt;</code> tag, and will forward
control back to the input form (identified by the <code>input</code>
property for this <code>ActionMapping</code>).</li>
</ul>
<p>
As mentioned earlier, this feature is entirely optional. The default implementation
of the validate() method returns <code>null</code>, and the controller servlet
will assume that any required validation is done by the action class.
</p>
<p>
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.
</p>
<p>
An optional package for performing ActionForm validations is available in the
Nightly Build and from <a href="http://home.earthlink.net/~dwinterfeldt/">
David Winterfeldt's Web site</a>.
</p>
</section>
<section name="3.4 Other Presentation Techniques" href="other_presentations">
<p>
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.
</p>
</section>
<section name="3.4.1 Application-Specific Custom Tags" href="custom_tags">
<p>
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:
</p>
<ul>
<li><b>checkLogon</b> - 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.</li>
<li><b>linkSubscription</b> - 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.</li>
<li><b>linkUser</b> - Generates a hyperlink to a details page
for a User, which passes the required primary key values as
request attributes.</li>
</ul>
<p>
The source code for these tags is in the <code>src/example</code> directory,
in package <code>org.apache.struts.example</code>, along with the other Java
classes that are used in this application.
</p>
</section>
<section name="3.4.2 Page Composition With Includes" href="includes">
<p>
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.</p>
<p>
For example, a portal application might have some or all of the following
functional capabilities available on the portal's "home" page:
</p>
<ul>
<li>Access to a search engine for this portal.</li>
<li>One or more "news feed" displays, with the topics of interest customized
from the user's registration profile.</li>
<li>Access to discussion topics related to this portal.</li>
<li>A "mail waiting" indicator if your portal provides free email
accounts.</li>
</ul>
<p>
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 <i>include</i> 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 <i>include</i> available, depending on when you want the combination
of output to occur:
</p>
<ul>
<li>An <code>&lt;%@ include file="xxxxx" %&gt;</code> 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.</li>
<li>The include <i>action</i> (<code>&lt;jsp:include page="xxxxx"
flush="true" /&gt;</code>) 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 <a href="../struts-logic.html#equals">equals</a> by using it's
parameter attribute.</li>
<li>The <a href="../struts-bean.html#include">bean:include</a> 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.</li>
</ul>
<p>
Another approach to this would be to use the Struts Template Tag library. See the
Developer's Guide for details.
</p>
<p>
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 <a href="http://www.lifl.fr/~dumoulin/tiles/">
Cedric Dumoulin's Web site</a>.
</p>
</section>
<section name="3.4.3 Image Rendering Components" href="image_rendering">
<p>
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:
</p>
<ul>
<li>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 <code>image/gif</code>),
and send back the bytes of that image to the browser, which will display
them just as if it had received a static file.</li>
<li>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.</li>
</ul>
</section>
<section name="3.4.4 Rendering Text" href="text_rendering">
<p>
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:
</p>
<pre>
response.setContentType("text/plain"); // or text/xml
PrintWriter writer = response.getWriter();
// use writer to render text
return(null);
</pre>
<p align="center">
Next: <a href="building_controller.html">Building Controller Components</a>
</p>
</section>
</chapter>
</body>
</document>