Programming Jakarta Struts, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Jakarta Struts, 2nd Edition [Electronic resources] - نسخه متنی

Chuck Cavaness

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








3.5 Struts View Components


The last of the MVC components to
discuss are the view components. Arguably, they are the easiest to
understand. The view components typically employed in a Struts
application are:

  • HTML

  • Data transfer objects

  • Struts ActionForms

  • JavaServer Pages

  • Custom tags

  • Java resource bundles



3.5.1 Using the Struts ActionForm


Struts ActionForm objects are used in the
framework to pass client input data back and forth between the user
and the business layer. The framework automatically collects the
input from the request and passes this data to an
Action using a form bean, which then can be passed
along to the business layer. To keep the presentation layer decoupled
from the business layer, you should not pass the
ActionForm itself to the business layer; rather,
create the appropriate DTO using the data from the
ActionForm. The following steps illustrate how the
framework processes an
ActionForm for every request:

  1. Check the mapping for the action and see if an
    ActionForm has been configured.

  2. If an ActionForm is configured for the action, use
    the name attribute from the
    action element to look up the form bean
    configuration information.

  3. Check to see if an instance of the ActionForm
    already has been created.

  4. If an ActionForm instance is present in the
    appropriate scope and it's the same type as needed
    for the new request, reuse it.

  5. Otherwise, create a new instance of the required
    ActionForm and store it in the appropriate scope
    (set by the scope attribute for the
    action element).

  6. Call the reset() method on the
    ActionForm instance.

  7. Iterate through the request parameters, and populate every parameter
    name that has a corresponding setter method in the
    ActionForm with the value for that request
    parameter.

  8. Finally, if the validate attribute is set to
    "true", invoke the
    validate() method on the
    ActionForm instance and return any errors.


For every HTML page where form data is posted, you should use an
ActionForm. The same ActionForm
can be used for multiple pages if necessary, as long as the HTML
fields and ActionForm properties match up.

Example 3-5 shows the
com.oreilly.struts.banking.form.LoginForm that is
used with the banking application.


Example 3-5. The LoginForm used by the banking application

package com.oreilly.struts.banking.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;
/**
* This ActionForm is used by the online banking appliation to validate
* that the user has entered an accessNumber and a pinNumber. If one or
* both of the fields are empty when validate( ) is called by the
* ActionServlet, error messages are created.
*/
public class LoginForm extends ActionForm {
// The user's private ID number
private String pinNumber;
// The user's access number
private String accessNumber;
public LoginForm( ) {
super( );
resetFields( );
}
/**
* Called by the framework to validate the user has entered values in the
* accessNumber and pinNumber fields.
*/
public ActionErrors validate(ActionMapping mapping, HttpServletRequest req ){
ActionErrors errors = new ActionErrors( );
// Get access to the message resources for this application.
// There's no easy way to access the resources from an ActionForm.
MessageResources resources =
(MessageResources)req.getAttribute( Action.MESSAGES_KEY );
// Check and see if the access number is missing.
if(accessNumber == null || accessNumber.length( ) == 0) {
String accessNumberLabel = resources.getMessage( "label.accessnumber" );
ActionError newError =
new ActionError("global.error.login.requiredfield", accessNumberLabel );
errors.add(ActionErrors.GLOBAL_ERROR, newError);
}
// Check and see if the pin number is missing.
if(pinNumber == null || pinNumber.length( ) == 0) {
String pinNumberLabel = resources.getMessage( "label.pinnumber" );
ActionError newError =
new ActionError("global.error.login.requiredfield", pinNumberLabel );
errors.add(ActionErrors.GLOBAL_ERROR, newError);
}
// Return the ActionErrors, if any.
return errors;
}
/**
* Called by the framework to reset the fields back to their default values.
*/
public void reset(ActionMapping mapping, HttpServletRequest request) {
// Clear out the accessNumber and pinNumber fields.
resetFields( );
}
/**
* Reset the fields back to their defaults.
*/
protected void resetFields( ) {
this.accessNumber = ";
this.pinNumber = ";
}
public void setAccessNumber(String nbr) {
this.accessNumber = nbr;
}
public String getAccessNumber( ) {
return this.accessNumber;
}
public String getPinNumber( ) {
return this.pinNumber;
}
public void setPinNumber(String nbr) {
this.pinNumber = nbr;
}
}

The
ActionForm class
provided by the Struts framework implements several methods, but by
far the two most important are the reset() and
validate() methods:

public void reset( ActionMapping mapping, HttpServletRequest request );
public ActionErrors validate( ActionMapping mapping, HttpServletRequest request );

The default implementation for both methods in the Struts
ActionForm class doesn't perform
any default logic. You'll need to override these two
methods in your ActionForm classes, as was done in
the LoginForm class shown in Example 3-5.

The controller calls the reset() method
right before it populates the
ActionForm instance with values from the request.
It gives the ActionForm a chance to reset its
properties back to the default state. This is very important, as the
form bean
instance may be shared across different requests or accessed by
different threads. However, if you are using an
ActionForm instance across multiple pages, you
might not want to implement the reset() method so
that the values don't get reset until
you're completely done with the instance. Another
approach is to implement your own resetFields()
method and call this method from the Action class
after a successful update to the business tier.

The validate() method is called by the controller
after the values from the request have been inserted into the
ActionForm. The ActionForm
should perform any input validation that can be done and return any
detected errors to the controller.
Business
logic validation should be performed in the business objects, not in
the ActionForm. The validation that occurs in the
ActionForm is presentation validation only. Where
to perform certain types of validation logic will be covered in
detail in Chapter 6 and Chapter 7.

The validate() method in the
LoginForm in Example 3-5 checks
to see if the access number and/or pin number is missing and creates
error messages if they are. If no errors are generated, the
controller passes the ActionForm and several other
objects to the execute() method. The
Action instance can then pull the information out
of the ActionForm.


You might have noticed that the execute() method
in the Action class contains an argument that is
always of the type ActionForm. You will need to
cast this argument to the appropriate subclass to retrieve the needed
properties. To see an example of this, look back at Example 3-3.

Once you have coded your ActionForm classes, you
need to inform your Struts application that they exist and tell it
which action mappings should use which
ActionForms. This is done in the configuration
file. The first step is to configure all of the
ActionForms for your application in the
form-beans section of
the configuration file. The following fragment from the banking
configuration file informs Struts of the three
ActionForm beans used by the banking application:

<form-beans>
<form-bean
name="loginForm"
type="com.oreilly.struts.banking.form.LoginForm"/>
<form-bean
name="accountInformationForm"
type="org.apache.struts.action.DynaActionForm">
<form-property name="accounts" type="java.util.ArrayList"/>
</form-bean>
<form-bean
name="accountDetailForm"
type="org.apache.struts.action.DynaActionForm">
<form-property
name="view"
type="com.oreilly.struts.banking.view.AccountDetailView"/>
</form-bean>
</form-beans>

The name attribute for each form bean must be
unique, and the type attribute must define a fully
qualified Java class that extends the Struts
ActionForm class. The next step is to use one of
the form-bean names from the
form-beans section in one or more
action elements. The following fragment shows the
mapping for the LoginAction, which
you've already seen earlier in this chapter:

<action
path="/login"
type="com.oreilly.struts.banking.action.LoginAction"
scope="request"
name="loginForm"
validate="true"
input="/login.jsp">
<forward name="Success" path="/action/getaccountinformation" redirect="true"/>
<forward name="Failure" path="/login.jsp" redirect="true"/>
</action>

Notice that the name attribute of the login
mapping matches one of the names in the form-beans
section from earlier.


Instances of the ActionForm class commonly are
referred to as "form beans." I use
this term throughout the book to refer to an object of type
ActionForm.

One of the new features in Struts 1.1 is shown in the previous
form-beans fragment. With previous versions of the
framework, you always had to extend the ActionForm
class with your own subclass, even if the
ActionForm performed very generic behavior. With
Struts 1.1, a new type of action form, called
org.
apache.struts.action.DynaActionForm
, has been added. This
class can be configured for an action mapping and will automatically
handle the data passed from the HTML form to the
Action. The DynaActionForm is
able to deal with the data generically because it uses a
Map to store the values internally. Chapter 7 will cover the
DynaActionForm in more detail.

You may be wondering what the difference is between an
ActionForm and the DTOs mentioned earlier. This is
a good question, and one that is a little confusing for developers
new to Struts.

The view components can use both
ActionForms and
DTOs to populate dynamic content. When no
ActionForm is configured for a mapping, you can
use DTOs to build the views. And when a form bean is defined for the
mapping, there are several ways to handle extracting the data from
the bean. One approach is to always wrap a form bean around one or
more DTOs returned from the business tier and to force the Struts
view components to access the DTO data through the form bean.
Likewise, when a client submits an HTML page, Struts will invoke the
form bean setter methods, which can shove the data back into the DTO
after the validation method has completed successfully. This provides
a single, cohesive interface to which the views can retrieve and
submit the HTML data to. We'll discuss the various
pros and cons of this and other approaches in Chapter 7.


3.5.2 Using JSP for Presentation


JSP pages make up the majority of what has
to be built for the Struts view components. Combined with custom tag
libraries and HTML, JSP makes it easy to provide a set of views for
an application.

Although JSP is the technology most commonly used by organizations
and developers to display dynamic content, it's not
the only option. Many developers feel that JSP has the following
problems:

  • Developers are free to embed application logic into the JSP pages,
    which can lead to applications that are difficult to maintain (with
    JSP 2.0, you can configure JSP pages to not allow scriptlets).

  • Developers must learn the JSP syntax and how to program custom tags
    (although this will not be true when JSP 2.0 is final and the new
    expression language is available).

  • The container must recompile the JSP page when a change is made to
    the page.


Some developers do not see these issues as a major problem, and many
sites have been built using the JSP technology. However, for those
that want alternatives, there are other forms of
presentation
technologies that can be combined with the Struts framework. One
popular alternative is the XML/XSLT combination. This
model combines the controller servlet from the Struts framework with
XSLT and beans serialized from DTOs to render the views.


3.5.3 Using Custom Tag Libraries


The Struts framework provides six core
tag libraries that your applications
can use. Each one has a different purpose and can be used
individually or alongside others. You also may extend the Struts tags
or create your own custom tags if you need additional functionality.
The custom tag libraries that are included with the
framework are the HTML, Bean, Logic, Template, Nested, and Tiles tag libraries.

To use these libraries in your application, you first need to
register them with the web application in the
web.xml file. You need
to register only the tag libraries that you plan to use in your
application. For example, if you are planning on using the HTML and
Logic tag libraries, you must add the following fragment to the
deployment descriptor for the web application:

<web-app>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>

More information on installing and configuring Struts for your
application is provided in Appendix B.

The next step is to create your JSP pages and include the necessary
taglib elements, depending on which tag libraries
the page will need:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

Once this is done and the JAR files are in the web
application's CLASSPATH, you can use the custom tags
in your JSP pages. Example 3-6 illustrates the usage
of several of the Struts custom tags inside the
login.jsp page for the banking application.


Example 3-6. The login.jsp page used by the banking application

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:html>
<head>
<html:base/>
<title><bean:message key="title.login"/></title>
<link rel="stylesheet" href="/image/library/english/10003_login_style_ie.css" type="text/css">
</head>
<body topmargin="0" leftmargin="5" marginheight="0" marginwidth="0" bgcolor="#6699FF">
<html:form action="login" focus="accessNumber">
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="#6699FF">
<tr><td>
<html:img srcKey="image.logo" width="79" height="46"
altKey="image.logo.alt" border="0"/>
</td></tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr><td bgcolor="#000000">
<table border="0" cellpadding="0" cellspacing="0" width="1" height="2"></table>
</td></tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="1" height="1">
<tr><td></td></tr>
</table>
<table>
<tr><td></td></tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="590">
<tr><td width="15" height="31"></td><td width="12"></td></tr>
<tr>
<td width="15"></td>
<td width="575" bgcolor="#FFFFFF" colspan="2">
<table cellpadding="0" cellspacing="0" border="0" width="575" height="3">
<tr><td></td></tr>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="590" bgcolor="#ffffff">
<tr>
<td width="15" bgcolor="#6699FF"></td>
<td width="15"></td><td width="379"></td>
<td width="15"></td>
<td width="15"></td>
<td width="15"></td>
</tr>
<tr>
<td bgcolor="#6699FF" width="15"></td>
<td></td>
<td valign="top">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="fieldlabel">
<td><bean:message key="label.accessnumber"/></td>
</tr class="fieldlabel">
<tr>
<td><html:text property="accessNumber" size="9" maxlength="9"/></td>
<td class="error"><html:errors/></td>
</tr>
<tr class="fieldlabel"><td height="10"></td></tr>
<tr class="fieldlabel"><td><bean:message key="label.pinnumber"/></td></tr>
<tr class="fieldlabel">
<td><html:password property="pinNumber" size="4" maxlength="4"/></td>
</tr>
<tr><td height="10"></td></tr>
<tr><td><html:submit styleClass="fieldlabel" value="Login"/></td></tr>
<tr><td></td></tr>
</table>
</td>
<td width="151" valign="top">
<html:img srcKey="image.strutspower" altKey="image.strutspower.alt"/>
</td>
</tr>
</table>
<%@include file="include/footer.jsp"%>
<br>
</html:form>
</body>
</html:html>

One of the first things that should strike you about the login page
in Example 3-6 is that there's no
Java code in it. Instead, you see mostly HTML formatting tags and
several uses of Struts tag libraries. This is exactly the purpose of
using custom tag libraries. No Java programming is necessary, so HTML
designers can work freely with the page layout without being burdened
by the programming aspects of the page. The other nice feature is
that many JSP pages can use the same tags. For more information on
tag libraries, see Chapter 8.


3.5.4 Using Message Resource Bundles


One of the hardest customizations that
developers face is to quickly and effortlessly customize a web
application for multiple languages. Java has several built-in
features that help support
internationalization;
Struts builds on those features to provide even more support.

The Java library includes a set of classes to support reading
message resources from
either a Java class or a properties file. The core class in this set
is the java.util.
ResourceBundle. The Struts framework provides a
similar set of classes, based around the org.apache.struts.util.MessageResources
class, that provides similar functionality but allows for more of the
flexibility that the framework requires.

With a Struts application, you must provide a resource bundle for
each language you want to support. The name of the class or
properties file must adhere to the guidelines listed in the JavaDocs
for the
java.util.ResourceBundle
class. Example 3-7 shows the properties file used by
the example banking application.


Example 3-7. The resource bundle for the banking application

# Labels
label.accessnumber=Access Number
label.pinnumber=Pin Number
label.accounts=Accounts
label.balance=Balance
label.totalassets=Total Assets
label.account=Account
label.balance=Available Balance
label.description=Description
label.amount=Amount
label.deposits=Deposits
label.withdrawls=Withdrawls
label.openingbalance=Opening Balance
# Links
link.customeragreement=Customer Agreement
link.privacy=Privacy
link.security=Security
link.viewaccountdetail=View Account Detail
# Page Titles
title.login=Struts Online Banking - Account Login
title.accountinfo=Struts Online Banking - Account Information
title.accountdetail=Struts Online Banking - Account Detail
# Button Labels
label.button.login=Login
# Error messages
global.error.invalidlogin=<li>Invalid Access Number and/or Pin</li>
global.error.login.requiredfield=<li>The {0} is required for login</li>
# Images
image.logo=images/logo.gif
image.logo.alt=Struts Online Banking
image.logout=images/logout.gif
image.logout.alt=Logout
image.strutspower=images/struts-power.gif
image.strutspower.alt=Powered By Struts
image.transfer=images/transfer.gif
image.transfer.
image.clear=images/clear.gif

If you look back at the login.jsp page in Example 3-6, you can see how the messages from the bundle
are used. For example, the following fragment from the login page
illustrates the key title.login from Example 3-6 being used and inserted between the HTML
title tags in the page.

<title><bean:message key="title.login"/></title>

The Struts
org.apache.struts.taglib.bean.MessageTag
is one of several custom tags included in the framework that can take
advantage of the resource bundle. JSP pages can retrieve values from
the resource bundle using the MessageTag based on
a key, as shown in the login page in Example 3-6.
The key in the message tag must correspond to a value on the left
side of the equals sign in the bundle. Case is important, and the
value must match exactly.


A message resource bundle can be used for more than just
localizationit also can save you time during application
maintenance. For example, if you use the same text messages or labels
throughout various parts of your web site or application, when one or
more of these values need to change, you need make the change only in
a single location. Even if you don't have
internationalization requirements, you still should use resource
bundles.

With Struts 1.1, you can define multiple MessageResources for
an application. This allows you to isolate certain types of resources
into separate bundles. For example, you might want to store the image
resources for an application in one bundle and the rest of the
resources in another. How you organize your
application's resources is up to you, but you now
have the flexibility to separate them based on some criteria. For
example, some applications choose to separate along component lines:
all resources relating to the catalog go into one bundle, order and
shopping-cart resources go into another, and so on.


    / 181