Struts 2 Design and Programming A Tutorial
Struts 2 Design and Programming A Tutorial
by Budi Kurniawan
Publisher: BrainySoftware
Pub Date: January 25, 2008
Print ISBN-10: 0-9803316-0-9
Print ISBN-13: 978-0-9803316-0-8
Pages: 576
Overview
Offering both theoretical explanations and real-world applications, this in-depth guide
covers the 2.0 version of Struts, revealing how to design, build, and improve Java-based
Web applications within the Struts development framework. Feature functionality is
explained in detail to help programmers choose the most appropriate feature to accomplish
their objectives, while other chapters are devoted to file uploading, paging, and object
caching.
Editorial Reviews
Product Description
Offering both theoretical explanations and real-world applications, this in-depth guide
covers the 2.0 version of Struts, revealing how to design, build, and improve Java-based
Web applications within the Struts development framework. Feature functionality is
explained in detail to help programmers choose the most appropriate feature to accomplish
their objectives, while other chapters are devoted to file uploading, paging, and object
caching.
Introduction
Welcome to Struts 2 Design and Programming: A Tutorial.
Servlet technology and JavaServer Pages (JSP) are the main technologies for developing
Java web applications. When introduced by Sun Microsystems in 1996, Servlet technology
was considered superior to the reigning Common Gateway Interface (CGI) because servlets
stay in memory after responding to the first requests. Subsequent requests for the same
servlet do not require re-instantiation of the servlet's class, thus enabling better response
time.
The problem with servlets is it is very cumbersome and error-prone to send HTML tags to
the browser because HTML output must be enclosed in Strings, like in the following code.
This is hard to program. Even small changes in the presentation, such as a change to the
background color, will require the servlet to be recompiled.
Sun recognized this problem and came up with JSP, which allows Java code and HTML tags
to intertwine in easy to edit pages. Changes in HTML output require no recompilation.
Automatic compilation occurs the first time a page is called and after it is modified. A Java
code fragment in a JSP is called a scriptlet.
Even though mixing scriptlets and HTML seems practical at first thought, it is actually a bad
idea for the following reasons:
• Interweaving scriptlets and HTML results in hard to read and hard to maintain
applications.
• Writing code in JSPs diminishes the opportunity to reuse the code. Of course, you
can put all Java methods in a JSP and include this page from other JSPs that need to
use the methods. However, by doing so you're moving away from the object-
oriented paradigm. For one thing, you will lose the power of inheritance.
• It is harder to write Java code in a JSP than to do it in a Java class. Let's face it, your
IDE is designed to analyze Java code in Java classes, not in JSPs.
• It is easier to debug code if it is in a Java class.
• It is easier to test business logic that is encapsulated in a Java class.
• Java code in Java classes is easier to refactor.
In fact, separation of business logic (Java code) and presentation (HTML tags) is such an
important issue that the designers of JSP have tried to encourage this practice right from
the first version of JSP.
JSP 1.0 allowed JavaBeans to be used for encapsulating code, thereby supported code and
presentation separation. In JSP you use <jsp:useBean> and <jsp:setProperty> to create a
JavaBean and set its properties, respectively.
Unfortunately, JavaBeans are not the perfect solution. With JavaBeans, method names must
follow certain naming convention, resulting in occasionally clumsy names. On top of that,
there's no way you can pass arguments to methods without resorting to scriptlets.
To make code and HTML tags separation easier to accomplish, JSP 1.1 defines custom tag
libraries, which are more flexible than JavaBeans. The problem is, custom tags are hard to
write and JSP 1.1 custom tags have a very complex life cycle.
Later an effort was initiated to provide tags with specific common functionality. These tags
are compiled in a set of libraries named JavaServer Pages Standard Tag Libraries (JSTL).
There are tags for manipulating scoped objects, iterating over a collection, performing
conditional tests, parsing and formatting data, etc.
Despite JavaBeans, custom tags, and JSTL, many people are still using scriptlets in their
JSPs for the following reasons.
In a project involving programmers with different skill levels, it is difficult to make sure all
Java code goes to Java classes. To make scriptlet-free JSPs more achievable, JSP 2.0 added
a feature that allows software architects to disable scriptlets in JSPs, thus enforcing the
separation of code and HTML. In addition, JSP 2.0 provides a simpler custom tag life cycle
and allows tags to be built in tag files, if effect making writing custom tags easier.
The advent of JSP was first thought to be the end of the day for servlets. It turned out this
was not the case. JSP did not displace servlets. In fact, today real-world applications employ
both servlets and JSPs. To understand why servlets did not become obsolete after the
arrival of JSP, you need to understand two design models upon which you can build Java
web applications.
The first design model, simply called Model 1, was born right after the JSP was made
available. Servlets are not normally used in this model. Navigating from one JSP to another
is done by clicking a link on the page. The second design model is named Model 2. You will
learn shortly why Model 1 is not recommended and why Model 2 is the way to go.
• Navigation problem. If you change the name of a JSP that is referenced by other
pages, you must change the name in many locations.
• There is more temptation to use scriptlets in JSPs because JavaBeans are limited and
custom tags are hard to write. However, as explained above, mixing Java code and
HTML in JSPs is a bad thing.
• If you can discipline yourself to not write Java code in JSPs, you'll end up spending
more time developing your application because you have to write custom tags for
most of your business logic. It's faster to write Java code in Java classes.
Model 2
The second design model is simply called Model 2. This is the recommended architecture to
base your Java web applications on. Model 2 is another name for the Model-View-Controller
(MVC) design pattern. In Model 2, there are three main components in an application: the
model, the view, and the controller. This pattern is explained in detail in Chapter 1, "Model
2 Applications."
Note
The term Model 2 was first used in the JavaServer Pages Specification version 0.92.
In Model 2, you have one entry point for all pages and usually a servlet or a filter acts as
the main controller and JSPs are used as presentation. Compared to Model 1 applications,
Model 2 applications enjoy the following benefits.
Struts Overview
Now that you understand why Model 2 is the recommended design model for Java web
applications, the next question you'll ask is, "How do I increase productivity?"
This was also the question that came to servlet expert Craig R. McClanahan's mind before
he decided to develop the Struts framework. After some preliminary work that worked,
McClanahan donated his brainchild to the Apache Software Foundation in May 2000 and
Struts 1.0 was released in June 2001. It soon became, and still is, the most popular
framework for developing Java web applications. Its web site is http://struts.apache.org.
In the meantime, on the same planet, some people had been working on another Java open
source framework called WebWork. Similar to Struts 1, WebWork never neared the
popularity of its competitor but was architecturally superior to Struts 1. For example, in
Struts 1 translating request parameters to a Java object requires an "intermediary" object
called the form bean, whereas in WebWork no intermediary object is necessary. The
implication is clear, a developer is more productive when using WebWork because fewer
classes are needed. As another example, an object called interceptor can be plugged in
easily in WebWork to add more processing to the framework, something that is not that
easy to achieve in Struts 1.
Another important feature that WebWork has but Struts 1 lacks is testability. This has a
huge impact on productivity. Testing business logic is much easier in WebWork than in
Struts 1. This is so because with Struts 1 you generally need a web browser to test the
business logic to retrieve inputs from HTTP request parameters. WebWork does not have
this problem because business classes can be tested without a browser.
A superior product (WebWork) and a pop-star status (Struts 1) naturally pressured both
camps to merge. According to Don Brown in his blog
(www.oreillynet.com/onjava/blog/2006/10/my_history_of_struts_2.html), it all started at
JavaOne 2005 when some Struts developers and users discussed the future of Struts and
came up with a proposal for Struts Ti (for Titanium), a code name for Struts 2. Had the
Struts team proceeded with the original proposal, Struts 2 would have included coveted
features missing in version 1, including extensibility and AJAX. On WebWork developer
Jason Carreira's suggestion, however, the proposal was amended to include a merger with
WebWork. This made sense since WebWork had most of the features of the proposed Struts
Ti. Rather than reinventing the wheel, 'acquisition' of WebWork could save a lot of time.
Note
So, what does Struts offer? Struts is a framework for developing Model 2 applications. It
makes development more rapid because it solves many common problems in web
application development by providing these features:
Because Struts is a Model 2 framework, when using Struts you should stick to the following
unwritten rules:
• No Java code in JSPs, all business logic should reside in Java classes called action
classes.
• Use the Expression Language (OGNL) to access model objects from JSPs.
• Little or no writing of custom tags (because they are relatively hard to code).
Upgrading to Struts 2
If you have programmed with Struts 1, this section provides a brief introduction of what to
expect in Struts 2. If you haven't, feel free to skip this section.
• Instead of a servlet controller like the ActionServlet class in Struts 1, Struts 2 uses
a filter to perform the same task.
• There are no action forms in Struts 2. In Struts 1, an HTML form maps to an
ActionForm instance. You can then access this action form from your action class
and use it to populate a data transfer object. In Struts 2, an HTML form maps
directly to a POJO. You don't need to create a data transfer object and, since there
are no action forms, maintenance is easier and you deal with fewer classes.
• Now, if you don't have action forms, how do you programmatically validate user
input in Struts 2? By writing the validation logic in the action class.
• Struts 1 comes with several tag libraries that provides custom tags to be used in
JSPs. The most prominent of these are the HTML tag library, the Bean tag library,
and the Logic tag library. JSTL and the Expression Language (EL) in Servlet 2.4 are
often used to replace the Bean and Logic tag libraries. Struts 2 comes with a tag
library that covers all. You don't need JSTL either, even though in some cases you
may still need the EL.
• In Struts 1 you used Struts configuration files, the main of which is called struts-
config.xml (by default) and located in the WEB-INF directory of the application. In
Struts 2 you use multiple configuration files too, however they must reside in or a
subdirectory of WEB-INF/classes.
• Java 5 and Servlet 2.4 are the prerequisites for Struts 2. Java 5 is needed because
annotations, added to Java 5, play an important role in Struts 2. Considering that
Java 6 has been released and Java 7 is on the way at the time of writing, you're
probably already using Java 5 or Java 6.
• Struts 1 action classes must extend org.apache.struts.action.Action. In Struts 2
any POJO can be an action class. However, for reasons that will be explained in
Chapter 3, "Actions and Results" it is convenient to extend the ActionSupport class
in Struts 2. On top of that, an action class can be used to service related actions.
• Instead of the JSP Expression Language and JSTL, you use OGNL to display object
models in JSPs.
• Tiles, which started life as a subcomponent of Struts 1, has graduated to an
independent Apache project. It is still available in Struts 2 as a plug-in.
Overview of the Chapters
This book is for those wanting to learn to develop Struts 2 applications. However, this book
does not stop short here. It takes the extra mile to teach how to design effective Struts
applications. As the title suggests, this book is designed as a tutorial, to be read from cover
to cover, written with clarity and readability in mind.
Chapter 1, "Model 2 Applications" explains the Model 2 architecture and provides two
Model 2 applications, one using a servlet controller and one utilizing a filter dispatcher.
Chapter 2, "Starting with Struts" is a brief introduction to Struts. In this chapter you
learn the main components of Struts and how to configure Struts applications.
Struts solves many common problems in web development such as page navigation, input
validation, and so on. As a result, you can concentrate on the most important task in
development: writing business logic in action classes. Chapter 3, "Actions and Results"
explains how to write effective action classes as well as related topics such as the default
result types, global exception mapping, wildcard mapping, and dynamic method invocation.
Chapter 4, "OGNL" discusses the expression language that can be used to access the
action and context objects. OGNL is a powerful language that is easy to use. In addition to
accessing objects, OGNL can also be used to create lists and maps.
Struts ships with a tag library that provides User Interface (UI) tags and non-UI tags
(generic tags). Chapter 5, "Form Tags" deals with form tags, the UI tags for entering
form data. You will learn that the benefits of using these tags and how each tag can be
used.
Chapter 6, "Generic Tags" explains non-UI tags. There are two types of non-UI tags,
control tags and data tags.
HTTP is type-agnostic, which means values sent in HTTP requests are all strings. Struts
automatically converts these values when mapping form fields to non-String action
properties. Chapter 7, "Type Conversion" explains how Struts does this and how to
write your own converters for more complex cases where built-in converters are not able to
help.
Chapter 9, "Message Handling" covers message handling, which is also one of the
most important tasks in application development. Today it is often a requirement that
applications be able to display internationalized and localized messages. Struts has been
designed with internationalization and localization from the outset.
Chapter 10, "Model Driven and Prepare Interceptors" discusses two important
interceptors for separating the action and the model. You'll find out that many actions will
need these interceptors.
Chapter 11, "The Persistence Layer" addresses the need of a persistence layer to
store objects. The persistence layer hides the complexity of accessing the database from its
clients, notably the Struts action objects. The persistence layer can be implemented as
entity beans, the Data Access Object (DAO) pattern, by using Hibernate, etc. This chapter
shows you in detail how to implement the DAO pattern. There are many variants of this
pattern and which one you should choose depends on the project specification.
Chapter 12, "File Upload" discusses an important topic that often does not get enough
attention in web programming books. Struts supports file upload by seamlessly
incorporating the Jakarta Commons FileUpload library. This chapter discusses how to
achieve this programming task in Struts.
Chapter 13, "File Download" deals with file download and demonstrates how you can
send binary streams to the browser.
In Chapter 14, "Security" you learn how to configure the deployment descriptor to
restrict access to some or all of the resources in your applications. What is meant by
"configuration" is that you need only modify your deployment descriptor file—no
programming is necessary. In addition, you learn how to use the roles attribute in the
action element in your Struts configuration file. Writing Java code to secure web
applications is also discussed.
Chapter 15, "Preventing Double Submits" explains how to use Struts' built-in
features to prevent double submits, which could happen by accident or by the user's not
knowing what to do when it is taking a long time to process a form.
Debugging is easy with Struts. Chapter 16, "Debugging and Profiling" discusses how
you can capitalize on this feature.
Chapter 17, "Progress Meters" features the Execute and Wait interceptor, which can
emulate progress meters for long-running tasks.
Chapter 18, "Custom Interceptors" shows you how to write your own interceptors.
Struts supports various result types and you can even write new ones. Chapter 19,
"Custom Result Types" shows how you can achieve this.
Chapter 23, "Plug-ins" discusses how you can distribute Struts modules easily as plug-
ins.
Chapter 24, "The Tiles Plug-in" provides a brief introduction to Tiles 2, an open source
project for laying out web pages.
Chapter 25, "JFreeChart Plug-ins" discusses how you can easily create web charts
that are based on the popular JFreeChart project.
Chapter 26, "Zero Configuration" explains how to develop a Struts application that
does not need configuration and how the CodeBehind plug-in makes this feature even more
powerful.
AJAX is the essence of Web 2.0 and it is becoming more popular as time goes by. Chapter
27, "AJAX" shows Struts' support for AJAX and explains how to use AJAX custom tags to
build AJAX components.
Appendix B, "The JSP Expression Language" introduces the language that may help
when OGNL and the Struts custom tags do not offer the best solution.
Appendix C, "Annotations" discusses the new feature in Java 5 that is used extensively
in Struts.
Struts 2 is based on Java 5, Servlet 2.4 and JSP 2.0. All examples in this book are based on
Servlet 2.5, the latest version of Servlet. (As of writing, Servlet 3.0 is being drafted.) You
need Tomcat 5.5 or later or other Java EE container that supports Servlet version 2.4 or
later.
The source code and binary distribution of Struts can be downloaded from here:
http://struts.apache.org/downloads.html
There are different ZIP files available. The struts-VERSION-all.zip file, where VERSION is the
Struts version, includes all libraries, source code, and sample applications. Its size is about
86MB and you should download this if you have the bandwidth. If not, try struts-VERSION-
lib.zip (very compact at 4MB), which contains the necessary libraries only.
Once you download a ZIP, extract it. You'll find dozens of JARs in the lib directory. The
names of the JARs that are native to Struts 2 start with struts2. The name of each Struts
JAR contains version information. For instance, the core library is packaged in the struts2-
core-VERSION.jar file, where VERSION indicates the major and minor version numbers. For
Struts 2.1.0, the core library name is struts2-core-2.1.0.jar.
There are also dependencies that come from other projects. The commons JAR files are
from the Apache Jakarta Commons project. You must include these commons JARs. The
ognl- VERSION.jar contains the OGNL engine, an important dependency. The freemarker-
VERSION.jar contains the FreeMarker template engine. It is needed even if you use JSP as
your view technology because FreeMarker is the template language for Struts custom tags.
The xwork- VERSION.jar contains XWork, the framework Struts 2 depends on. Always
include this JAR.
The only JARs you can exclude are the plug-in files. Their names have this format:
struts2-xxx-plugin-VERSION.jar
Here, xxx is the plug-in name. For example, the Tiles plug-in is packaged in the struts2-
tiles-plugin-VERSION.jar file.
You do not need the Tiles JARs either unless you use Tiles in your application.
Sample Applications
The examples used in this book can be downloaded from this site.
http://jtute.com
appXXy
where XX is the two digit chapter number and y is a letter that represents the application
order in the chapter. Therefore, the second application in Chapter 1 is app01b.
Tomcat 6 was used to test all applications. All of them were run on the author's machine on
port 8080. Therefore, the URLs for all applications start with http://localhost:8080, followed
by the application name and the servlet path.
Chapter 1. Model 2 Applications
As explained in Introduction, Model 2 is the recommended architecture for all but the
simplest Java web applications. This chapter discusses Model 2 in minute detail and provides
two Model 2 sample applications. A sound understanding of this design model is crucial to
understanding Struts and building effective Struts applications.
Model 2 Overview
Model 2 is based on the Model-View-Controller (MVC) design pattern, the central concept
behind the Smalltalk-80 user interface. As the term "design pattern" had not been coined
yet at that time, it was called the MVC paradigm.
An application implementing the MVC pattern consists of three modules: model, view, and
controller. The view takes care of the display of the application. The model encapsulates the
application data and business logic. The controller receives user input and commands the
model and/or the view to change accordingly.
Note
In Model 2, you have a servlet or a filter acting as the controller of the MVC pattern. Struts
1 employs a servlet controller whereas Struts 2 uses a filter. Generally JavaServer Pages
(JSPs) are employed as the views of the application, even though other view technologies
are supported. As the models, you use POJOs (POJO is an acronym for Plain Old Java
Object). POJOs are ordinary objects, as opposed to Enterprise Java Beans or other special
objects. Figure 1.1 shows the diagram of a Model 2 application.
In a Model 2 application, every HTTP request must be directed to the controller. The
request's Uniform Request Identifier (URI) tells the controller what action to invoke. The
term "action" refers to an operation that the application is able to perform. The POJO
associated with an action is called an action object. In Struts 2, as you'll find out later, an
action class may be used to serve different actions. By contrast, Struts 1 dictates that you
create an action class for each individual action.
A seemingly trivial function may take more than one action. For instance, adding a product
would require two actions:
As mentioned above, you use the URI to tell the controller which action to invoke. For
instance, to get the application to send the "Add Product" form, you would use the following
URL:
http://domain/appName/Product_input.action
http://domain/appName/Product_save.action
The controller examines every URI to decide what action to invoke. It also stores the action
object in a place that can be accessed from the view, so that server-side values can be
displayed on the browser. Finally, the controller uses a RequestDispatcher object to
forward the request to the view (JSP). In the JSP, you use custom tags to display the
content of the action object.
In the next two sections I present two simple Model 2 applications. The first one uses a
servlet as the controller and the second one employs a filter.
The application can be used to enter product information and is named app01a. The user
Figure 1.2 and submit it. The application will then send a
will fill in a form like the one in
confirmation page to the user and display the details of the saved product. (See Figure
1.3)
Figure 1.2. The Product form
Figure 1.3. The product details page
1. Display the "Add Product" form. This action sends the entry form in Figure 1.2 to the
browser. The URI to invoke this action must contain the string
Product_input.action.
2. Save the product and returns the confirmation page in Figure 1.3. The URI to invoke
this action must contain the string Product_save.action.
1. A Product class that is the template for the action objects. An instance of this class
contains product information.
2. A ControllerServlet class, which is the controller of this Model 2 application.
3. Two JSPs (ProductForm.jsp and ProductDetails.jsp) as the views.
4. A CSS file that defines the styles of the views. This is a static resource.
A Product instance is a POJO that encapsulates product information. The Product class
(shown in Listing 1.1) has three properties: productName, description, and price. It
also has one method, save.
// forward to a view
String dispatchUrl = null;
if (action.equals("Product_input.action")) {
dispatchUrl = "/jsp/ProductForm.jsp";
} else if (action.equals("Product_save.action")) {
dispatchUrl = "/jsp/ProductDetails.jsp";
}
if (dispatchUrl != null) {
RequestDispatcher rd =
request.getRequestDispatcher(dispatchUrl);
rd.forward(request, response);
}
}
}
The process method in the ControllerServlet class processes all incoming requests. It
starts by obtaining the request URI and the action name.
Note
The .action extension in every URI is the default extension used in Struts 2 and is therefore
used here.
3. If an action object exists, call the action method. In this example, the save method
on the Product object is the action method for the Product_save action.
The part of the process method that determines what action to perform is in the following
if block:
// execute an action
if (action.equals("Product_input.action")) {
// there is nothing to be done
} else if (action.equals("Product_save.action")) {
// instantiate action class
...
}
There is no action class to instantiate for the action Product_input. For Product_save,
the process method creates a Product object, populates its properties, and calls its save
method.
The Product object is then stored in the HttpServletRequest object so that the view can
access it.
The Views
The application utilizes two JSPs for the views of the application. The first JSP,
ProductForm.jsp, is displayed if the action is Product_input.action. The second page,
ProductDetails.jsp, is shown for Product_save.action. ProductForm.jsp is given in
Listing 1.3 and ProductDetails.jsp in Listing 1.4.
The ProductForm.jsp page contains an HTML form for entering a product's details. The
ProductDetails.jsp page uses the JSP Expression Language (EL) to access the product
scoped object in the HttpServletRequest object. Struts 2 does not depend on the EL to
access action objects. Therefore, you can still follow the examples in this book even if you
do not understand the EL.
A servlet/JSP application, app01a needs a deployment descriptor (web.xml file). The one
for this application is shown in Listing 1.5.
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>app01a.ControllerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
</web-app>
In this application, as is the case for most Model 2 applications, you need to prevent the
JSPs from being accessed directly from the browser. There are a number of ways to achieve
this, including:
• Putting the JSPs under WEB-INF. Anything under WEB-INF or a subdirectory under
WEB-INF is protected. If you put your JSPs under WEB-INF you cannot access
them by using a browser, but the controller can still dispatch requests to those JSPs.
However, this is not a recommended approach since not all containers implement
this feature. BEA's WebLogic is an example that does not.
• Using a servlet filter and filter out requests for JSP pages.
• Using security restriction in your deployment descriptor. This is easier than using a
filter since you do not have to write a filter class. This method is chosen for this
application.
Assuming you are running the application on your local machine on port 8080, you can
invoke the application using the following URL:
http://localhost:8080/app01a/Product_input.action
When you submit the form, the following URL will be sent to the server:
http://localhost:8080/app01a/Product_save.action
Model 2 with A Filter Dispatcher
While a servlet is the most common controller in a Model 2 application, a filter can act as a
controller too. As a matter of fact, filters have life cycle methods similar to those of servlets.
These are life cycle methods of a filter.
• init. Called once by the web container just before the filter is put into service.
• doFilter. Called by the web container each time it receives a request with a URL that
matches the filter's URL pattern.
• destroy. Called by the web container before the filter is taken out of service, i.e.
when the application is shut down.
There is one distinct advantage of using a filter over a servlet as a controller. With a filter
you can conveniently choose to serve all the resources in your application, including static
ones. With a servlet, your controller only handles access to the dynamic part of the
application. Note that the url-pattern element in the web.xml file in the previous
application is
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>...</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
With such a setting, requests for static resources are not handled by the servlet controller,
but by the container. You wouldn't want to handle static resources in your servlet controller
because that would mean extra work.
A filter is different. A filter can opt to let through requests for static contents. To pass on a
request, call the filterChain.doFilter method in the filter's doFilter method. You'll learn
how to do this in the application to come.
Consequently, employing a filter as the controller allows you to block all requests to the
application, including request for static contents. You will then have the following setting in
your deployment descriptor:
<filter>
<filter-name>filterDispatcher</filter-name>
<filter-class>...</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDispatcher</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
What is the advantage of being able to block static requests? One thing for sure, you can
easily protect your static files from curious eyes. The following code will send an error
message if a user tries to view a JavaScript file:
It will not protect your code from the most determined people, but users can no longer type
in the URL of your static file to view it. By the same token, you can protect your images so
that no one can link to them at your expense.
On the other hand, using a servlet as the controller allows you to use the servlet as a
welcome page. This is an important feature since you can then configure your application so
that the servlet controller will be invoked simply by the user typing your domain name (such
as http://example.com) in the browser's address box. A filter does not have the
privilege to act as a welcome page. Simply typing the domain name won't invoke a filter
dispatcher. In this case, you will have to create a welcome page (that can be an HTML, a
JSP, or a servlet) that redirects to the default action.
The following example (app01b) is a Model 2 application that uses a filter dispatcher.
// forward to a view
String dispatchUrl = null;
if (action.equals("Product_input.action")) {
dispatchUrl = "/jsp/ProductForm.jsp";
} else if (action.equals("Product_save.action")) {
dispatchUrl = "/jsp/ProductDetails.jsp";
}
if (dispatchUrl != null) {
RequestDispatcher rd = request
.getRequestDispatcher(dispatchUrl);
rd.forward(request, response);
}
} else if (uri.indexOf("/css/") != -1
&& req.getHeader("referer") == null) {
res.sendError(HttpServletResponse.SC_FORBIDDEN);
} else {
// other static resources, let it through
filterChain.doFilter(request, response);
}
}
}
The doFilter method performs what the process method in app01a did, namely
Note that since the filter captures all requests, including those for static requests, we can
easily add extra processing for CSS files. By checking the referer header for requests for
CSS files, a user will see an error message if he or she types in the URL to the CSS file:
http://localhost:8080/app01b/css/main.css
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
</web-app>
http://localhost:8080/app01b/Product_input.action
Summary
In this chapter you learned the Model 2 architecture and how to write Model 2 applications,
using either a servlet controller or a filter dispatcher. These two types of Model 2
applications were demonstrated in app01a and app01b, respectively.
Practically the filter dispatcher in app01b illustrates the main function of the Struts 2
framework. However, what you've seen does not cover even 0.1% of what Struts can do.
You'll write your first Struts application in the next chapter and learn more features in
subsequent chapters.
Chapter 2. Starting with Struts
In Chapter 1, "Model 2 Applications" you learned the advantages of the Model 2 architecture
and how to build Model 2 applications. This chapter introduces Struts as a framework for
rapid Model 2 application development. It starts with a discussion of the benefits of Struts
and how it expedites Model 2 application development. It also discusses the basic
components of Struts: the filter dispatcher, actions, results, and interceptors.
Introducing Struts configuration is another objective of this chapter. Most Struts application
will have a struts.xml file and a struts.properties file. The former is the more important as it
is where you configure your actions. The latter is optional as there exists a
default.properties file that contains standard settings that work for most applications.
Note
Struts is an MVC framework that employs a filter dispatcher as the controller. When writing
a Model 2 application, it is your responsibility to provide a controller as well as write action
classes. Your controller must be able to do these:
The first benefit of using Struts is that you don't have to write a controller and can
concentrate on writing business logic in action classes. Here is the list of features that Struts
is equipped with to make development more rapid:
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
There's a lot that a filter dispatcher in a Model 2 application has to do and Struts' filter
dispatcher is by no means an exception. Since Struts has more, actually much more,
features to support, its filter dispatcher could grow infinitely in complexity. However, Struts
approaches this by splitting task processing in its filter dispatcher into subcomponents called
interceptors. The first interceptor you'll notice is the one that populates the action object
with request parameters. You'll learn more about interceptors in the section
"Interceptors" later in this chapter.
In a Struts application the action method is executed after the action's properties are
populated. An action method can have any name as long as it is a valid Java method name.
An action method returns a String value. This value indicates to Struts where control
should be forwarded to. A successful action method execution will forward to a different
view than a failed one. For instance, the String "success" indicates a successful action
method execution and "error" indicates that there's been an error during processing and an
error message should be displayed. Most of the time a RequestDispatcher will be used to
forward to a JSP, however JSPs are not the only allowed destination. A result that returns a
file for download does not need a JSP. Neither does a result that simply sends a redirection
command or sends a chart to be rendered. Even if an action needs to be forwarded to a
view, the view may not necessarily be a JSP. A Velocity template or a FreeMarker template
Chapter 20, "Velocity" explains the Velocity templating language and
can also be used.
Chapter 20, "FreeMarker" discusses FreeMarker.
Now that you know all the basic components in Struts, I'll continue by explaining how Struts
works. Since Struts uses a filter dispatcher as its controller, all activities start from this
object.
The Case for Velocity and FreeMarker
JSP programmers would probably mumble, "Why introduce new view technologies
and not stick with JSP?" Good question. The answer is, while you can get away
with just JSP, there's a compelling reason to learn Velocity and/or FreeMarker.
Velocity and FreeMarker templates can be packaged in a JAR, which is how Struts
plug-ins are distributed (Plug-ins are discussed in Chapter 23, "Plug-ins"). You
cannot distribute JSPs in a JAR, at least not easily, although you'll find a way to do
so if you're determined enough. For example, check out this thread in Sun's
developer forum:
http://forum.java.sun.com/thread.jspa?threadID=5132356
The first things that a filter dispatcher does is verify the request URI and determine what
action to invoke and which Java action class to instantiate. The filter dispatcher in app01b
did this by using a string manipulation method. However, this is impractical since during
development the URI may change several times and you will have to recompile the filter
each time the URI or something else changes.
For matching URIs with action classes, Struts uses a configuration file named struts.xml.
Basically, you need to create a struts.xml file and place it under WEB-INF/classes. You
define all actions in the application in this file. Each action has a name that directly
corresponds to the URI used to invoke the action. Each action declaration may specify the
fully qualified name of an action class, if any. You may also specify the action method name
unless its name is execute, the default method name Struts will assume in the absence of
an explicit one.
An action class must have at least one result to tell Struts what to do after it executes the
action method. There may be multiple results if the action method may return different
results depending on, say, user inputs.
The struts.xml file is read when Struts starts. In development mode, Struts checks the
timestamp of this file every time it processes a request and will reload it if it has changed
since the last time it was loaded. As a result, if you are in development mode and you
change the struts.xml file, you don't need to restart your web container. Saving you time.
Configuration file loading will fail if you don't comply with the rules that govern the
struts.xml file. If, or should I say when, this happens, Struts will fail to start and you must
restart your container. Sometimes it's hard to decipher what you've done wrong due to
unclear error messages. If this happens, try commenting out actions that you suspect are
causing it, until you isolate and fix the one that is impending development.
Note
I'll discuss Struts development mode when discussing the Struts configuration files in the
section "Configuration Files" later in this chapter.
Figure 2.1 shows how Struts processes action invocation. It does not include the reading
of the configuration file, that only happens once during application launch.
For every action invocation the filter dispatcher does the following:
1. Consult the Configuration Manager to determine what action to invoke based on the
request URI:
2. Run each of the interceptors registered for this action. One of the interceptors will
populate the action's properties.
3. Execute the action method.
4. Execute the result.
Note that some interceptors run again after action method execution, before the result is
executed.
Interceptors
As mentioned earlier, there are a lot of things a filter dispatcher must do. Code that would
otherwise reside in the filter dispatcher class is modularized into interceptors. The beauty of
interceptors is they can be plugged in and out by editing the Struts' configuration file. Struts
achieves a high degree of modularity using this strategy. New code for action processing
can be added without recompiling the main framework.
Table 2.1 lists Struts default interceptors. The words in brackets in the Interceptor
column are names used to register the interceptors in the configuration file. Yes, as you will
see shortly, you need to register an interceptor in the configuration file before you can use
it. For example, the registered name for the Alias interceptor is alias.
Table 2.1. Struts default interceptors
Interceptor Description
Alias (alias) Converts similar parameters that may have different names
between requests.
Chaining (chain) When used with the Chain result type, this interceptor
makes the previous action's properties available to the
current action. See Chapter 3, "Actions and Results" for
details.
Conversion Error Adds conversion errors to the action's field errors. See
(conversionError) Chapter 7, "Type conversion" for more details.
Create Session Creates an HttpSession object if one does not yet exist for
(createSession) the current user.
File Upload (fileUpload) Supports file upload. See Chapter 12, "File Upload" for
details.
9, "Message Handling."
Message Store (store) Stores and retrieves action messages or action errors or
field errors for action objects whose classes implement
ValidationAware.
Model Driven Supports for the model driven pattern for action classes that
(modelDriven) implement ModelDriven. See Chapter 10, "The Model
Driven Pattern" for details.
Scoped Model Driven Similar to the Model Driven interceptor but works for
(scopedModelDriven) classes that implement ScopedModelDriven.
Scope (scope) Provides a mechanism for storing action state in the session
or application scope.
Token (token) Verifies that a valid token is present. See Chapter 15,
"Preventing Double Submits" for details.
Token Session Verifies that a valid token is present. See Chapter 15,
(tokenSession) "Preventing Double Submits" for details.
Parameter Filter (n/a) Removes parameters from the list of those available to the
action.
There are quite a number of interceptors, and this can be confusing to a beginner. The thing
is you don't have to know about interceptors intimately before you can write a Struts
application. Just know that interceptors play a vital role in Struts and we will revisit them
one at a time in subsequent chapters.
Most of the time the default interceptors are good enough. However, if you need non-
standard action processing, you can write your own interceptor. Writing custom interceptors
is discussed in Chapter 18, "Custom Interceptors."
It is possible to have no configuration file at all. The zero configuration feature, discussed in
Chapter 26, "Zero Configuration," is for advanced developers who want to skip this
mundane task.
In struts.xml you define all aspects of your application, including the actions, the
interceptors that need to be called for each action, and the possible results for each action.
Interceptors and result types used in an action must be registered before they can be used.
Happily, Struts configuration files support inheritance and default configuration files are
included in the struts2-core- VERSION.jar file. The struts-default.xml file, one of such
default configuration files, registers the default result types and interceptors. As such, you
can use the default result types and interceptors without registering them in your own
struts.xml file, making it cleaner and shorter.
The default.properties file, packaged in the same JAR, contains settings that apply to all
Struts applications. As a result, unless you need to override the default values, you don't
need to have a struts.properties file.
The struts.xml file is an XML file with a struts root element. You define all the actions in
your Struts application in this file. Here is the skeleton of a struts.xml file.
...
</struts>
The more important elements that can appear between <struts> and </struts> are
discussed next.
Since Struts has been designed with modularity in mind, actions are grouped into packages.
Think packages as modules. A typical struts.xml file can have one or many packages:
<struts>
<package name="package-1" namespace="namespace-1"
extends="struts-default">
<action name="..."/>
<action name="..."/>
...
</package>
<package name="package-2" namespace="namespace-2">
extends="struts-default">
<action name="..."/>
<action name="..."/>
...
</package>
...
A package element must have a name attribute. The namespace attribute is optional and
if it is not present, the default value "/" is assumed. If the namespace attribute has a non-
default value, the namespace must be added to the URI that invokes the actions in the
package. For example, the URI for invoking an action in a package with a default
namespace is this:
/context/actionName.action
To invoke an action in a package with a non-default namespace, you need this URI:
/context/namespace/actionName.action
A package element almost always extends the struts-default package defined in struts-
default.xml. By doing so, all actions in the package can use the result types and
interceptors registered in struts-default.xml. Appendix A, "Struts Configuration" lists
all the result types and interceptors in struts-default. Here is the skeleton of the struts-
default package. The interceptors have been omitted to save space.
<struts>
<package name="struts-default">
<result-types>
<result-type name="chain" class="com.opensymphony.
xwork2.ActionChainResult"/>
<result-type name="dispatcher"
class="org.apache.struts2.dispatcher.ServletDispatcherResult"
default="true"/>
<result-type name="freemarker"
class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader"
class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect"
class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirect-action"
class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream"
class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity"
class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt"
class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plaintext"
class="org.apache.struts2.dispatcher.PlainTextResult"/>
</result-types>
<interceptors>
[all interceptors]
</interceptors>
</package>
</struts>
A large application may have many packages. In order to make the struts.xml file easier to
manage for a large application, it is advisable to divide it into smaller files and use include
elements to reference the files. Each file would ideally include a package or related
packages.
A struts.xml file with multiple include elements would look like this.
<struts>
</struts>
Each module.xml file would have the same DOCTYPE element and a struts root element.
Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
Note
Most sample applications in this book only have one struts.xml file. The only sample
application that splits the struts.xml file into smaller files can be found in Chapter 25,
"The JFreeChart Plug-in."
An action element is nested within a package element and represents an action. An action
must have a name and you may choose any name for it. A good name reflects what the
action does. For instance, an action that displays a form for entering a product's details may
be called displayAddProductForm. By convention, you are encouraged to use the
combination of a noun and a verb. For example, instead of calling an action
displayAddProductForm, name it Product_input. However, it is totally up to you.
An action may or may not specify an action class. Therefore, an action element may be as
simple as this.
<action name="MyAction">
An action that does not specify an action class will be given an instance of the default action
class. The ActionSupport class is the default action class and is discussed in Chapter 3,
"Actions and Results."
If an action has a non-default action class, however, you must specify the fully class name
using the class attribute. In addition, you must also specify the name of the action method,
which is the method in the action class that will be executed when the action is invoked.
Here is an example.
<result> is a subelement of <action> and tells Struts where you want the action to be
forwarded to. A result element corresponds to the return value of an action method.
Because an action method may return different values for different situations, an action
element may have several result elements, each of which corresponds to a possible return
value of the action method. This is to say, if a method may return "success" and "input,"
you must have two result elements. The name attribute of the result element maps a
result with a method return value.
Note
If a method returns a value without a matching result element, Struts will try to find a
matching result under the global-results element (See the discussion of this element
below). If no corresponding result element is found under global-results, an exception
will be thrown.
For example, the following action element contains two result elements.
The first result will be executed if the action method save returns "success," in which case
the Confirm.jsp page will be displayed. The second result will be executed if the method
returns "input," in which case the Product.jsp page will be sent to the browser.
By the way, the type attribute of a result element specifies the result type. The value of
the type attribute must be a result type that is registered in the containing package or a
parent package extended by the containing package. Assuming that the action
Product_save is in a package that extends struts-default, it is safe to use a Dispatcher
result for this action because the Dispatcher result type is defined in struts-default.
If you omit the name attribute in a result element, "success" is implied. In addition, if the
type attribute is not present, the default result type Dispatcher is assumed. Therefore,
these two result elements are the same.
<result name="success" type="dispatcher">/jsp/Confirm.jsp</result>
<result>/jsp/Confirm.jsp</result>
An alternative syntax that employs the param element exists for the Dispatcher result
element. In this case, the parameter name to be used with the param element is location.
In other words, this result element
<result>/test.jsp</result>
<result>
<param name="location">/test.jsp</param>
</result>
You'll learn more about the param element later in this section.
A package element may contain a global-results element that contains results that act as
general results. If an action cannot find a matching result under its action declaration, it will
search the global-results element, if any.
<global-results>
<result name="error">/jsp/GenericErrorPage.jsp</result>
<result name="login" type="redirect-action">Login</result>
</global-results>
There are five interceptor-related elements that may appear in a struts.xml file:
interceptors, interceptor, interceptor-ref, interceptor-stack, and default-
interceptor-ref. They are explained in this section.
An action element must contain a list of interceptors that will process the action object.
Before you can use an interceptor, however, you have to register it using an interceptor
element under <interceptors>. Interceptors defined in a package can be used by all
actions in the package.
For example, the following package element registers two interceptors, validation and
logger.
<package name="main" extends="struts-default">
<interceptors>
<interceptor name="validation" class="..."/>
<interceptor name="logger" class="..."/>
</interceptors>
</package>
To apply an interceptor to an action, use the interceptor-ref element under the action
element of that action. For instance, the following configuration registers four interceptors
and apply them to the Product_delete and Product_save actions.
With these settings every time the Product_delete or Product_save actions are invoked,
the four interceptors will be given a chance to process the actions. Note that the order of
appearance of the interceptor-ref element is important as it determines the order of
invocation of registered interceptors for that action. In this example, the alias interceptor
will be invoked first, followed by the i18n interceptor, the validation interceptor, and the
logger interceptor.
With most Struts application having multiple action elements, repeating the list of
interceptors for each action can be a daunting task. In order to alleviate this problem,
Struts allows you to create interceptor stacks that group required interceptors. Instead of
referencing interceptors from within each action element, you can reference the interceptor
stack instead.
For instance, six interceptors are often used in the following orders: exception,
servletConfig, prepare, checkbox, params, and conversionError. Rather than
referencing them again and again in your action declarations, you can create an interceptor
stack like this:
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
<default-interceptor-ref name="defaultStack"/>
If an action needs a combination of other interceptors and the default stack, you must
redefine the default stack as the default-interceptor-ref element will be ignored if an
interceptor element can be found within an action element.
The param element can be nested within another element such as action, result-type,
and interceptor to pass a value to the enclosing object.
The param element has a name attribute that specifies the name of the parameter. The
format is as follows:
<param name="property">value</param>
Used within an action element, param can be used to set an action property. For example,
the following param element sets the siteId property of the action.
<action name="customer" class="...">
<param name="siteId">california01</param>
</action>
And the following param element sets the excludeMethod of the validation interceptor-
ref:
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
The excludeMethods parameter is used to exclude certain methods from invoking the
enclosing interceptor.
In addition to the struts.xml file, you can have a struts.properties file. You create the
latter if you need to override one or more key/value pairs defined in the
default.properties file, which is included in the struts2-core-VERSION.jar file. Most of
the time you won't need a struts.properties file as the default.properties file is good
enough. Besides, you can override a setting in the default.properties file using the
constant element in the struts.xml file.
The constant element has a name attribute and a value attribute. For example, the
struts.devMode setting determines whether or not the Struts application is in development
mode. By default, the value is false, meaning the application is not in development mode.
<struts>
<constant name="struts.devMode" value="true"/>
...
</struts>
struts.devMode = true
A struts.properties file must reside in the classpath or in WEB-INF/classes. Appendix
A, "Struts Configuration" provides the complete list of key/value pairs that may appear in a
struts.properties file.
To avoid creating a new file, you can use constant elements in the struts.xml file.
Alternatively, you can use the init-param element in the filter declaration of the Struts
filter dispatcher:
<filter>
<filter-name>struts</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
<init-param>
<param-name>struts.devMode</param-name>
<param-value>true</param-value>
</init-param>
</filter>
The deployment descriptor is given in Listing 2.1 and the Struts configuration file in
Listing 2.2.
<filter>
<filter-name>struts2</filter-name>
<filter-
class>org.apache.struts2.dispatcher.FilterDispatcher</filter-
class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Restrict direct access to JSPs.
For the security constraint to work, the auth-constraint
and login-config elements must be present -->
<security-constraint>
<web-resource-collection>
<web-resource-name>JSPs</web-resource-name>
<url-pattern>/jsp/*</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
</web-app>
<struts>
<package name="app02a" namespace="/" extends="struts-default">
<action name="Product_input">
<result>/jsp/ProductForm.jsp</result>
</action>
The struts.xml file defines a package (app02a) that has two actions, Product_input and
Product_save. The Product_input action does not have an action class. Invoking
Product_input simply forwards control to the ProductForm.jsp page. This page contains
an entry form for entering product information.
Note
During development you can add these two constant elements on top of your package
element.
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="true" />
The first constant disables dynamic method invocation, explained in Chapter 3, "Actions
and Results." The second constant element causes Struts to switch to development mode.
The Product class in Listing 2.3 is the action class for action Product_save. The class
has three properties (productName, description, and price) and one action method,
execute.
This application is a Struts replica of the applications in Chapter 1. To invoke the first
action, use the following URL (assuming Tomcat is used)
http://localhost:8080/app02a/Product_input.action
Dependency Injection
Before we continue, I'd like to introduce a popular design pattern that is used extensively in
Struts: dependency injection. Martin Fowler wrote an excellent article on this pattern. His
article can be found here:
http://martinfowler.com/articles/injection.html
Before Fowler coined the term "dependency injection," the phrase "inversion of control" was
often used to mean the same thing. As Fowler notes in his article, the two are not exactly
the same. This book therefore uses "dependency injection."
Overview
public class A {
public void importantMethod() {
B b = ... // get an instance of B
b.usefulMethod();
...
}
...
As a more concrete example, consider the following PersistenceManager class that can be
used to persist objects to a database.
} catch (SQLException e) {
}
}
Dependency injection dictates that dependency should be injected to the using component.
In the context of the PersistenceManager example here, a DataSource object should be
passed to the PersistenceManager instead of forcing PersistenceManager to create one.
One way to do it is by providing a constructor that accepts the dependency, in this case a
DataSource:
} catch (SQLException e) {
}
}
}
Injecting dependency through the constructor is not the only form of dependency injection.
Dependency can also be injected through a setter method. Back to the
PersistenceManager example, the class author may opt to provide this method:
In addition, as explained in Fowler's article, you can also use an interface for dependency
injection.
Struts uses setter methods for its dependency injection strategy. For example, the
framework sets action properties by injecting HTTP request parameters' values. As a result,
you can use an action's properties from within the action method, without having to worry
about populating the properties.
Note
Java 5 EE supports dependency injection at various levels. Feel free to visit this site:
http://java.sun.com/developer/technicalArticles/J2EE/injection/
Summary
In this chapter you have learned what Struts offers to speed up Model 2 application
development. You have also learned how to configure Struts applications and written your
first Struts application.
Chapter 3. Actions and Results
As Struts ships with interceptors and other components that solve common problems in web
application development, you can focus on writing business logic in the action class. This
chapter discusses topics you need to know to write effective action classes, including the
ActionSupport convenience class and how to access resources. In addition, it explains
related subjects such as the standard result types, global exception mapping, wildcard
mapping, and dynamic method invocation.
Action Classes
Every operation that an application can perform is referred to as an action. Displaying a
Login form, for example, is an action. So is saving a product's details. Creating actions is
the most important task in Struts application development. Some actions are as simple as
forwarding to a JSP. Others perform logic that needs to be written in action classes.
An action class is an ordinary Java class. It may have properties and methods and must
comply with these rules.
• A property must have a get and a set methods. Action property names follow the
same rules as JavaBeans property names. A property can be of any type, not only
String. Data conversion from String to non-String happens automatically.
• An action class must have a no-argument constructor. If you don't have a
constructor in your action class, the Java compiler will create a no-argument
constructor for you. However, if you have a constructor that takes one or more
arguments, you must write a no-argument constructor. Or else, Struts will not be
able to instantiate the class.
• An action class must have at least one method that will be invoked when the action
is called.
• An action class may be associated with multiple actions. In this case, the action class
may provide a different method for each action. For example, a User action class
may have login and logout methods that are mapped to the User_login and
User_logout actions, respectively.
• Since Struts 2, unlike Struts 1, creates a new action instance for every HTTP request,
an action class does not have to be thread safe.
• Struts 2, unlike Struts 1, by default does not create an HttpSession object.
However, a JSP does. Therefore, if you want a completely session free action, add
this to the top of all your JSPs:
<%@page session="false"%>
The Employee class in Listing 3.1 is an action class. It has four properties (firstName,
lastName, birthDate, and emails) and one method (register).
Listing 3.1. The Employee action class
package app03a;
import java.util.Collection;
import java.util.Date;
// do something here
return "success";
}
}
As you can see in Listing 3.1, an action class does not have to extend a certain parent
class or implement an interface. Having said that, most of your action classes will
implement the com.opensymphony.xwork2.Action interface indirectly by extending a
convenience class named ActionSupport. I'll explain ActionSupport in the section "The
ActionSupport Class" later in this chapter.
If you implement Action, you will inherit the following static fields:
• SUCCESS. Indicates that the action execution was successful and the result view
should be shown to the user.
• NONE. Indicates that the action execution was successful but no result view should
be shown to the user.
• ERROR. Indicates that that action execution failed and an error view should be sent
to the user.
• INPUT. Indicates that input validation failed and the form that had been used to
take user input should be shown again.
• LOGIN. Indicates that the action could not execute because the user was not logged
in and the login view should be shown.
You need to know the values of these static fields as you will use the values when
configuring results. Here they are.
Note
One thing to note about the Struts action is you don't have to worry about how the view will
access it. Unlike in the app01a and app01b applications where values had to be stored in
scoped attributes so that the view could access them, Struts automatically pushes actions
and other objects to the Value Stack, which is accessible to the view. The Value Stack is
explained in Chapter 4, "OGNL."
Accessing Resources
From an action class, you can access resources such as the ServletContext, HttpSession,
HttpServletRequest, and HttpServletResponse objects either through the
ServletActionContext object or by implementing Aware interfaces. The latter is an
implementation of dependency injection and is the recommended way as it will make your
action classes easier to test.
There are two classes that provide access to the aforementioned resources,
com.opensymphony.xwork2.ActionContext and
org.apache.struts2.ServletActionContext. The latter wraps the former and is the easier
to use between the two. ServletActionContext provides the following static methods that
you will often use in your career as a Struts developer. Here are some of them.
public static javax.servlet.http.HttpServletRequest getRequest()
You can obtain the HttpSession object by calling one of the getSession methods on the
HttpServletRequest object. The HttpSession object will be created automatically if you
use the basicStack or defaultStack interceptor stack.
Note
You should not call the methods on the ServletActionContext from an action class's
constructor because at this stage the underlying ActionContext object has not been
passed to it. Calling ServletActionContext.getServletContext from an action's
constructor will return null.
Aware Interfaces
Struts provides four interfaces that you can implement to get access to the
ServletContext, HttpServletRequest, HttpServletResponse, and HttpSession objects,
respectively: The interfaces are
• org.apache.struts2.util.ServletContextAware
• org.apache.struts2.interceptor.ServletRequestAware
• org.apache.struts2.interceptor.ServletResponseAware
• org.apache.struts2.interceptor.SessionAware
I discuss these interfaces in the following subsections and provide an example of an action
that implements these interfaces in the next section.
ServletContextAware
When an action is invoked, Struts will examine if the associated action class implements
ServletContextAware. If it does, Struts will call the action's setServletContext method
and pass the ServletContext object prior to populating the action properties and executing
the action method. In your setServletContext method you need to assign the
ServletContext object to a class variable. Like this.
You can then access the ServletContext object from any point in your action class through
the servletContext variable.
ServletRequestAware
void setServletRequest(javax.servlet.http.HttpServletRequest
servletRequest)
In the implementation of the setServletRequest method, you need to assign the passed
HttpServletRequest object to a class variable:
Now you can access the HttpServletRequest object via the servletRequest reference.
ServletResponseAware
void setServletResponse(javax.servlet.http.HttpServletResponse
servletResponse)
Implement this interface if you need to access the HttpServletResponse object from your
action class. When an action is invoked, Struts checks to see if the action class implements
ServletResponseAware. If it does, Struts calls its setServletResponse method passing
the current HttpServletResponse object. You need to assign the passed object to a class
variable. Here is an example of how to do it.
You can now access the HttpServletResponse object via the servletResponse variable.
SessionAware
If you need access to the HttpSession object from within your action class, implementing
the SessionAware interface is the way to go. The SessionAware interface is a little
different from its three other counterparts discussed earlier. Implementing SessionAware
does not give you the current HttpSession instance but a java.util.Map. This may be
confusing at first, but let's take a closer look at the SessionAware interface.
This interface only has one method, setSession, whose signature is this.
Struts will call the setSession method of an implementing action class when the action is
invoked. Upon doing so, Struts will pass an instance of
org.apache.struts2.dispatcher.SessionMap, which extends java.util.AbstractMap,
which in turn implements java.util.Map.SessionMap is a wrapper for the current
HttpSession object and maintains a reference to the HttpSession object.
The reference to the HttpSession object inside SessionMap is protected, so you won't be
able to access it directly from your action class. However, SessionMap provides methods
that make accessing the HttpSession object directly no longer necessary. Here are the
public methods defined in the SessionMap class.
Invalidates the current HttpSession object. If the HttpSession object has not been
created, this method exits gracefully.
Removes all attributes in the HttpSession object. If the HttpSession object has not been
created, this method does not throw an exception.
Returns the session attribute associated with the specified key. It returns null if the
HttpSession object is null or if the key is not found.
Stores a session attribute in the HttpSession object and returns the attribute value. If the
HttpSession object is null, it will create a new HttpSession object.
Removes the specified session attribute and returns the attribute value. If the HttpSession
object is null, this method returns null.
For example, to invalidate the session object, call the invalidate method on the
SessionMap:
Note
Unfortunately, the SessionMap class does not provide access to the session identifier. In
the rare cases where you need the identifier, use the ServletActionContext to obtain the
HttpSession object.
Note
For this interface to work, the Servlet Config interceptor must be enabled. Since this
interceptor is part of the default stack, by default it is already on.
Using Aware Interfaces to Access Resources
The app03a application shows how to use Aware interfaces to access resources. The
application defines three actions as shown in Listing 3.3.
The User_login and User_logout actions are based on the User action class in Listing
3.4. This class has two properties (userName and password) and implements
ServletContextAware, ServletRequestAware, ServletResponseAware, and
SessionAware to provide access to resources. Note that to save space the get and set
methods for the properties are not shown.
/*
* The onlineUserCount is accurate only if we also
* write a javax.servlet.http.HttpSessionListener
* implementation and decrement the
* onlineUserCount attribute value in its
* sessionDestroyed method, which is called by the
* container when a user session is inactive for
* a certain period of time.
*/
public String logout() {
if (sessionMap instanceof SessionMap) {
((SessionMap) sessionMap).invalidate();
}
int onlineUserCount = 0;
synchronized (servletContext) {
try {
onlineUserCount = (Integer) servletContext
.getAttribute("onlineUserCount");
} catch (Exception e) {
}
servletContext.setAttribute("onlineUserCount",
onlineUserCount - 1);
}
return "success";
}
The User class can be used to manage user logins and maintain the number of users
currently logged in. In this application a user can log in by typing in a non-empty user name
and a non-empty password in a Login form.
You can access the HttpServletRequest object because the User class implements
ServletRequestAware. As demonstrated in the login method, that gets invoked every
time a user logs in, you retrieve the referer header by calling the getHeader method on
the servletRequest object. Verifying that the referer header is not null makes sure that
the action was invoked by submitting the Login form, not by typing the URL of the
User_input action. Next, the login method increments the value of the application
attribute onlineUserCount.
The logout method invalidates the HttpSession object and decrements onlineUserCount.
Therefore, the value of onlineUserCount reflects the number of users currently logged in.
You can test this application by invoking the User_input action using this URL:
http://localhost:8080/app03a/User_input.action
You will see the Login form like the one in Figure 3.1. You can log in by entering a non-
empty user name and a non-empty password. When you submit the form, the User_login
action will be invoked. If login is successful, you'll see the second page that looks like the
one in Figure 3.2. The number of users online is displayed here.
Figure 3.1. The Login form
Request parameters are mapped to action properties. However, there's another way of
assigning values to action properties: by passing the values in the action declaration.
An action element in a struts.xml file may contain param elements. Each param element
corresponds to an action property. The Static Parameters (staticParams) interceptor is
responsible for mapping static parameters to action properties.
Every time the action MyAction is invoked, its siteId property will be set to "california0l" and
its siteType property to "retail."
Since ActionSupport implements the Action interface, you can use the static fields
ERROR, INPUT, LOGIN, NONE, and SUCCESS from a class that extends it. There's
already an implementation of the execute method, inherited from Action, that simply
returns Action.SUCCESS. If you implement the Action interface directly instead of
extending ActionSupport, you have to provide an implementation of execute yourself.
Therefore, it's more convenient to extend ActionSupport than to implement Action.
In addition to execute, there are other methods in ActionSupport that you can override or
use. For instance, you may want to override validate if you're writing code for validating
user input. And you can use one of the many overloads of getText to look up localized
messages in properties files. Input validation is discussed in Chapter 8, "Input Validation"
and we'll look at the getText methods when we discuss internationalization and localization
in Chapter 9, "Message Handling."
Results
An action method returns a String that determines what result to execute. An action
declaration must contain result elements that each corresponds to a possible return value
of the action method. If, for example, an action method returns either Action.SUCCESS or
Action.INPUT, the action declaration must have two result elements like these
<action ... >
<result name="success"> ... </result>
<result name="input"> ... </result>
</action>
• name. The name of the result that matches the output of the action method. For
example, if the value of the name attribute is "input," the result will be used if the
action method returns "input." The name attribute is optional and its default value is
"success."
• type. The result type. The default value is "dispatcher," a result type that forwards
to a JSP.
The default values of both attributes help you write shorter configuration. For example,
these result elements
<result>/Product.jsp</result>
<result name="input">/ProductForm.jsp</result>
The first result element does not have to contain the name and type attributes as it uses
the default values. The second result element needs the name attribute but does not need
the type attribute.
Dispatcher is the most frequently used result type, but it's not the only type available.
Table 3.1 shows all standard result types. The words in brackets in the Result Type
column are names used to register the result types in the configuration file. That's right,
you must register a result type before you can use it.
Dispatcher (dispatcher) The default result type, used for JSP forwarding
Chain
The Chain result type is there to support action chaining, whereby an action is forwarded to
another action and the state of the original action is retained in the target action. The
Chaining interceptor makes action chaining possible and since this interceptor is part of
defaultStack, you can use action chaining right away.
If action-x is chained to action-y, action-x will be pushed to the Value Stack, followed by
action-y, making action-y the top object in the Object Stack. As a result, both actions can
be accessed from the view. If action-x and action-y both have a property that shares the
same name, you can access the property in action-y (the top object) using this OGNL
expression:
[0].propertyName
or
propertyName
[1].propertyName
Use action chaining with caution, though. Generally action chaining is not recommended as
it may turn your actions into spaghetti code. If action1 needs to be forwarded to action2,
for example, you need to ask yourself if there's code in action2 that needs to be pushed
into a method in a utility class that can be called from both action1 and action2.
Dispatcher
The Dispatcher result type is the most frequently used type and the default type. This result
type has a location parameter that is the default parameter. Since it is the default
parameter, you can either pass a value to it by using the param element like this:
<result name="...">
<param name="location">resource</param>
</result>
or by passing the value to the result element.
<result name="...">resource</result>
Use this result type to forward to a resource, normally a JSP or an HTML file, in the same
application. You cannot forward to an external resource and its location parameter cannot
be assigned an absolute URL. To direct to an external resource, use the Redirect result type.
As almost all accompanying applications in this book utilize this result type, a separate
example is not given here.
FreeMarker
This result type forwards to a FreeMarker template. See Chapter 21, "FreeMarker" for
details.
HttpHeader
This result type is used to send an HTTP status to the browser. For example, the app03a
application has this action declaration:
<default-action-ref name="CatchAll"/>
<action name="CatchAll">
<result type="httpheader">
<param name="status">404</param>
</result>
</action>
The default-action-ref element is used to specify the default action, which is the action
that will be invoked if a URI does not have a matching action. In the example above, the
CatchAll action is the default action. CatchAll uses a HttpHeader result to send a 404
status code to the browser. As a result, if there's no matching action, instead of getting
Struts' error messages:
the user will get a 404 status report and will see a default page from the container.
Redirect
This result type redirects, instead of forward, to another resource. This result type accepts
these parameters
The main reason to use a redirect, as opposed to a forward, is to direct the user to an
external resource. A forward using Dispatcher is preferable when directing to an internal
resource because a forward is faster. Redirection would require a round trip since the client
browser would be forced to re-send a new HTTP request.
Having said that, there is a reason why you may want to redirect to an internal resource.
You normally redirect if you don't want a page refresh invokes the previously invoked
action. For instance, in a typical application, submitting a form invokes a Product_save
action, that adds a new product to the database. If this action forwards to a JSP, the
Address box of the browser will still be showing the URL that invoked Product_save. If the
user for some reason presses the browser's Reload or Refresh button, the same action will
be invoked again, potentially adding the same product to the database. Redirection removes
the association with the previous action as the redirection target has a new URL.
When redirecting to an internal resource, you specify a URI for the resource. The URI can
point to an action. For instance,
Note also that you need to encode special characters such as & and + . For example, if the
target is http://www.test.com?user=l&site=4, you must change the & to &.
<result name="login" type="redirect">
http://www.test.com?user=1&site=4
</result>
Redirect Action
• actionName. Specifies the name of the target action. This is the default attribute.
• namespace. The namespace of the target action. If no namespace parameter is
present, it is assumed the target action resides in the same namespace as the
enclosing action.
For example, the following Redirect Action result redirects to a User_input action.
<result type="redirect-action">
<param name="actionName">User_input</param>
</result>
And since actionName is the default parameter, you can simply write:
<result type="redirect-action">User_input</result>
Note that the value of the redirection target is an action name. There is no .action suffix
necessary as is the case with the Redirect result type.
In addition to the two parameters, you can pass other parameters as request parameters.
For example, the following result type
<result type="redirect-action">
<param name="actionName">User_input</param>
<param name="userId">xyz</param>
<param name="area">ga</param>
</result>
User_input.action?userId=xyz&area=ga
Stream
This result type does not forward to a JSP. Instead, it sends an output stream to the
browser. See Chapter 13, "File Download" for examples.
Velocity
This result type forwards to a Velocity template. See Chapter 20, "Velocity" for details.
XSLT
This result type uses XML/XSLT as the view technology. This result type is explained further
in Chapter 22, "XSLT."
PlainText
A PlainText result is normally used for sending a JSP's source. For example, the action
Source_show below displays the source of the Menu.jsp page.
In a perfect world, all computer programs would be bug-free. In the real world, however,
this is not the case. No matter how you take care to handle your code, some bugs might
still try to creep out. Sometimes it's not even your fault. Third-party components you use in
your code may have bugs that are not known at the time you deploy your application. Any
uncaught exception will result in an embarrassing HTTP 500 code (internal error).
Fortunately for Struts programmers, Struts lets you catch whatever you cannot catch in
your action classes by using the exception-mapping element in the configuration file.
This exception-mapping element has two attributes, exception and result. The exception
attribute specifies the exception type that will be caught. The result attribute specifies a
result name, either in the same action or in the global-results declaration, that will be
executed if an exception is caught. You can nest one or more exception-mapping elements
under your action declaration. For example, the following exception-mapping element
catches all exceptions thrown by the User_save action and executes the error result.
You can also provide a global exception mapping through the use of the global-exception-
mappings element. Any exception-mapping declared under the global-exception-mappings
element must refer to a result in the global-results element. Here is an example of global-
exception-mappings.
<global-results>
<result name="error">/jsp/Error.jsp</result>
<result name="sqlError">/jsp/SQLError.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.sql.SQLException"
result="sqlError"/>
<exception-mapping exception="java.lang.Exception"
result="error"/>
</global-exception-mappings>
Behind the scenes is the Exception interceptor that handles all exceptions caught. Part of
the default stack, this exception adds two objects to the Value Stack (which you'll learn in
Chapter 4, "OGNL"), for every exception caught by an exception-mapping element.
This way, you can display the exception message or the stack trace in your view, if you so
choose. The property tag that you will learn in Chapter 5, "Form Tags" can be used for this
purpose:
<s:property value="exception.message"/>
<s:property value="exceptionStack"/>
Wildcard Mapping
A large application can have dozens or even a hundred action declarations. These
declarations can clutter the configuration file and make it less readable. To ease this
situation, you can use wildcard mapping to merge similar mappings to one mapping.
You can invoke the Book_add action by using this URI that contains the combination of the
package namespace and the action name:
/wild/Book_add.action
However, if there is no action with the name Book_add, Struts will match the URI with any
action name that includes the wildcard character *. For example, the same URI will invoke
the action named *_add if Book_add does not exist.
The action in the package above can be invoked using any URI that contains the correct
namespace and _add, including
/wild/Book_add.action
/wild/Author_add.action
/wild/_add.action
/wild/Whatever_add.action
If more than one wildcard match was found, the last one found prevails. In the following
example, the second action will always get invoked.
If multiple matches were found, the pattern that does not use a wildcard character wins.
Look at these action declarations again:
The URI /wild/Book_add.action matches both actions. However, since the first action
declaration does not use a wildcard character, it will take precedence over the second.
The part of the URI that was matched by the wildcard is available as {1}. What it means is
if you use the URI /wild/MyAction_add.action and it matches an action whose name is
*_add, {1} will contain MyAction. You can then use {1} to replace other parts of the
configuration.
Using /wild/Author_add.action, on the other hand, will also invoke the action *_add, where
"Author" was matched by *. The class name will be app03a.Author and the JSP to forward
to will be Author.jsp.
If you try /wild/Whatever_add.action, it will still match the action *_add. However, it will
throw an exception because there are no Whatever class and Whatever.jsp JSP.
You've seen that Book_add and Author_add can be combined into *_add. By extension,
Book_edit and Author_edit can also merge, and so can Book_delete and Author_delete. If
you note that an action name contains the combination of the action class name and the
action method name and realizing that {1} contains the first replacement and {2} the
second replacement, you can shorten the six action declarations above into this.
For example, the URI /wild/Book_edit.action will match *_*. The replacement for the first *
is Book and the replacement for the second * is edit. Therefore, {1} will contain Book and
{2} will contain edit. /wild/Book_edit.action consequently will invoke the app03a.Book class
and execute its edit method.
Note
Note also that * matches zero or more characters excluding the slash ('/') character. To
include the slash character, use **. To escape a character, use the '\' character.
In Struts jargon the '!' character is called the bang notation. It is used to invoke a method
dynamically. The method may be different from the one specified in the action element for
that action.
For example, this action declaration does not have a method attribute.
As a result, the execute method on Book will be invoked. However, using the bang notation
you can invoke a different method in the same action. The URI /Book!edit.action, for
example, will invoke the edit method on Book.
You are not recommended to use dynamic method invocation because of security concerns.
You wouldn't want your users to be able to invoke methods that you do not expose.
struts.enable.DynamicMethodInvocation = true
To disable this feature, set this key to false, either in a struts.properties file or in a
struts.xml file using a constant element like this:
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
Since action classes are POJO classes, testing action classes is easy. All you need is
instantiate the class, set its properties, and call its action method. Here is an example.
Summary
Struts solves common problems in web application development such as page navigation,
input validation, and so on. As a result, you can concentrate on the most important task in
development: writing business logic in action classes. This chapter explained how to write
effective action classes as well as related topics such as the default result types, global
exception mapping, wildcard mapping, and dynamic method invocation.
Chapter 4. OGNL
The view in the Model-View-Controller (MVC) pattern is responsible for displaying the model
and other objects. To access these objects from a JSP, you use OGNL (Object-Graph
Navigation Language), the expression language Struts inherits from WebWork.
• Bind GUI elements (text fields, check boxes, etc) to model objects and converts
values from one type to another.
• Bind generic tags with model objects.
• Create lists and maps on the fly, to be used with GUI elements.
• Invoke methods. You can invoke any method, not only getters and setters.
OGNL is powerful, but only part of its power is relevant to Struts developers. This chapter
discusses OGNL features that you will need for Struts projects. If you're interested in
learning other features of OGNL, visit these websites.
http://www.opensymphony.com/ognl
http://www.ognl.org
Note
After reading this chapter the first time, do not worry if you don't get a firm understanding
of OGNL. Just skip to the next chapter and see how OGNL is used in form tags and generic
tags. Once you've started using it, you can revisit this chapter for reference.
There are two logical units inside the Value Stack, the Object Stack and the Context Map, as
illustrated in Figure 4.1. Struts pushes the action and related objects to the Object Stack
and pushes various maps to the Context Map.
Figure 4.1. The Value Stack
Note
The term Value Stack is often used to refer to the Object Stack in the Value Stack.
The following are the maps that are pushed to the Context Map.
• parameters. A Map that contains the request parameters for the current request.
• request. A Map containing all the request attributes for the current request.
• session. A Map containing the session attributes for the current user.
• application. A Map containing the ServletContext attributes for the current
application.
• attr. A Map that searches for attributes in this order: request, session, and
application.
You can use OGNL to access objects in the Object Stack and the Context Map. To tell the
OGNL engine where to search, prefix your OGNL expression with a # if you intend to access
the Context Map. Without a #, search will be conducted against the Object Stack.
Note
A request parameter always returns an array of Strings, not a String. Therefore, to access
the number of request parameters, use this
#parameters.count[0]
and not
#parameters.count
To access the property of an object in the Object Stack, use one of the following forms:
object.propertyName
object['propertyName' ]
object["propertyName" ]
Object stack objects can be referred to using a zero-based index. For example, the top
object in the Object Stack is referred to simply as [0] and the object right below it as [1].
For example, the following expression returns the value of the message property of the
object on top:
[0].message
To read the time property of the second object in the stack, you can use [1].time or
[1]["time"] or [1]['time'].
For example, the property tag, one of the many tags you'll learn in Chapter 5, "Form Tags,"
is used to print a value. Using the property tag to print the time property of the first stack
object, you can write any of the following:
<s:property value="[0].time"/>
<s:property value="[0]['time']"/>
<s:property value='[0]["time"]'/>
[0].name
The index [n] specifies the starting position for searching, rather than the object to search.
The following expression searches from the third object in the stack for the property user.
[2]["user"]
If you want a search to start from the top object, you can remove the index entirely.
Therefore,
[0].password
is the same as
password
Note also that if the returned value has properties, you can use the same syntax to access
the properties. For instance, if a Struts action has an address property that is returns an
instance of Address, you can use the following expression to access the streetNumber
property of the address property of the action.
[0].address.streetNumber
To access the property of an object in the Context Map, use one of these forms.
#object.propertyName
#object['propertyName' ]
#object["propertyName" ]
For example, the following expression returns the value of the session attribute code.
#session.code
This expression returns the contactName property of the request attribute customer.
#request["customer"]["contactName"]
The following expression tries to find the lastAccessDate attribute in the request object. If
no attribute is found, the search will continue to the session and application objects.
#attr['lastAccessDate']
@fullyQualifiedClassName@fieldName
@fullyQualifiedClassName@methodName(argumentList)
As an example, this expression accesses the static field DECEMBER in java.util.Calendar:
@java.util.Calendar@DECEMBER
To call the static method now in the app04.Util class (shown in Listing 4.1), use this:
@app04a.Util@now()
object.fieldName
object.methodName(argumentList)
Here object represents a reference to an Object Stack object. You use the same syntax as
when accessing a property. For example, this refers to the first object in the stack:
[0]
To call the datePattem field in app04.Test2Action (shown in Listing 4.2), use this
expression.
[0].datePattern
[0].repeat(3, "Hello")
Listing 4.2. The repeat method
You can access individual elements by using the same notation you use to access a Java
array element. For instance, this returns the first color in colors:
colors[0]
You can also call an array's length field to find out how many elements it has. For example,
this returns 3.
colors.length
You can access individual elements in a list by using the same notation you would use to
access an array element. For instance, this returns the first country in countries:
countries[0]
You can enquiry about a List's size by calling its size method or the special keyword size.
The following returns the number of elements in countries.
countries.size
countries.size()
The isEmpty keyword or a call to its isEmpty method tells you whether or not a List is
empty.
countries.isEmpty
countries.isEmpty()
You can also use OGNL expressions to create Lists. This feature will come in handy when
you're working with form tags that require options such as select and radio. To create a
list, you use the same notation as when declaring an array in Java. For example, the
following expression creates a List of three Strings:
The following creates a List of two Integers. The primitive elements will be automatically
converted to Integers.
{6, 8}
Working with Maps
Referencing a Map property returns all its key/value pairs in this format:
For example, the cities property whose getter is shown in Listing 4.5 returns this.
map[key]
cities["CA"]
or
cities['CA']
You can use size or size() to get the number of key/value pairs in a Map.
cities.size
cities.size()
cities.isEmpty
cities.isEmpty()
And yes, you can access the Maps in the Context Map too. Just don't forget to use a #
prefix. For example, the following expression accesses the application Map and retrieves the
value of "code":
#application["code"]
There can be empty spaces between a key and the colon and between a colon and a value.
For example, the cities Map can be rewritten by this OGNL expression:
This will be useful when you have started working with tags that need options, such as radio
and select.
There are times when OGNL and the Struts custom tags are not the best choice. For
example, to print a model object on a JSP, you use the property tag that is included in the
Struts tag library. Like this:
<s:property value="serverValue"/>
However, you can achieve the same using this shorter JSP Expression Language expression:
${serverValue}
Also, there's no easy way to use Struts custom tags to print a request header. With EL, it's
easy. For instance, the following EL expression prints the value of the host header:
${header.host}
You will therefore find it practical to use OGNL and EL together. The EL is explained in
Appendix B, "The Expression Language."
Summary
The view in the Model-View-Controller (MVC) pattern is responsible for displaying the model
and other objects and you use OGNL to access the objects. This chapter discussed the Value
Stack that stores the action and context objects and explained how to use OGNL to access
them and create arrays, lists, and maps.
Chapter 5. Form Tags
Struts ships with a tag library that incorporates two types of tags: User Interface (UI) tags
and non-UI tags. The UI tags are further categorized into two groups, those used for data
entry and those for displaying error messages. The UI tags in the first group are called the
form tags and are the subject of discussion of this chapter. The UI tags for displaying error
messages are explained in Chapter 8, "Input Validation." Non-UI tags help with control flow
and data access and are covered in Chapter 6, "Generic Tags." In addition, there are also
tags that assist with AJAX programming and are discussed in Chapter 27, "AJAX."
form is the main tag in the form tags category. This tag is rendered as an HTML form
element. Other form tags are rendered as input elements. The main benefit of using the
form tags is when input validation fails and the form is returned to the user. With manual
HTML coding, you have to worry about repopulating the input fields with the values the user
previously entered. With the form tags, this is taken care of for you.
Another advantage of using the form tags is that they help with layout and there are several
layout templates for each tag. These layout templates are organized into themes and Struts
comes with several themes, giving you flexibility to choose a layout that is suitable for your
application.
This chapter explains each of the form tags in a separate section. Before you learn the first
tag, however, it is beneficial to discuss how to use the Struts tags and peruse the common
attributes shared by all the tags. After some basic tags, three attributes—list, listKey, and
listValue— are given a separate section because of their importance in tags that use
options, including radio, combobox, select, checkboxlist, and doubleselect. After all form
tags are covered, themes are explained at the end of this chapter.
You can use the UI and non-UI tags by declaring this taglib directive at the top of your JSP.
A tag attribute can be assigned a static value or an OGNL expression. If you assign an OGNL
expression, the expression will be evaluated if you enclose it with %{ and }. For instance,
the following label attribute is assigned the String literal "userName"
label="userName"
This one is assigned an OGNL expression userName, and the value will be whatever the
value of the userName action property is:
label="%{userName}"
This one assigns the label attribute the value of the session attribute userName:
label="%{#session.userName}"
value="%{1 + 5}"
Common Attributes
Tag classes of all Struts tags are part of the org.apache.struts2.components package
and all UI tags are derived from the UIBean class. This class defines common attributes
that are inherited by the UI tags. Table 5.1 lists the attributes.
label* String Specifies the label for a form element in the xhtml and ajax
theme.
labelPosition* String Specifies the label position in the xhtml and ajax theme.
Allowed values are top and left (default).
key String The name of the property this input field represents. It is a
shortcut for the name and label attributes
name String Specifies the HTML name attribute that in an input element
Table 5.1. The Common attributes
Name Data Description
Type
required* boolean In the xhtml theme this attribute indicates whether or not
an asterisk (*) should be added to the label.
An attribute name with an asterisk indicates that the attribute is only available if a non-
simple theme is used. Themes are explained toward the end of this chapter.
The name attribute is probably the most important one. In an input tag it maps to an action
property. Other important attributes include value, label, and key. The value attribute
holds the user value. You seldom use this attribute in an input tag unless the input tag is a
hidden field.
By default, each input tag is accompanied by a label element. The label attribute specifies
the text for the label element. The key attribute is a shortcut for the name and label
attributes. If the key attribute is used, the value assigned to this attribute will be assigned
to the name attribute and the value returned from the call to getText(key) will be
assigned to the label attribute. In other words,
key="aKey"
is the same as
name="aKey" label="%{getText('aKey')}"
If both the key and name attributes are present, the explicit value for name takes
precedence and the label attribute is assigned the result of getText(key). If the key
attribute and the label attribute are present, the value assigned to the label attribute will
be used.
tooltipDelay String The delay (in milliseconds) from the time the mouse hovers over
the tooltip icon to the time the tooltip is shown. The default
value is 500.
The form tag renders an HTML form. Its attributes are given in Table 5.5. All attributes
are optional.
<s:form>
...
</s:form>
By default a form tag is rendered as an HTML form laid out in a table:
</table>
</form>
An input field nested within a form tag is rendered as a table row. The row has two fields,
one for a label and one for the input element. A submit button is translated into a table row
with a single cells that occupies two columns. For instance, the following tags
<s:form action="...">
<s:textfield name="userName" label="User Name"/>
<s:password name="password" label="Password"/>
<s:submit/>
</s:form>
are rendered as
You can change the default layout by changing the theme. Themes are discussed in the
section "Themes" near the end of this chapter.
The password tag extends textfield by adding a showPassword attribute. This attribute
takes a boolean value and its default value is false. It determines whether or not the
entered value will be redisplayed when the containing form fails to validate. A value of true
redisplays the password when control is redirected back to the form.
For example, the following password tag has its showPassword attribute set to true.
<s:form action="Product_save">
<s:password key="password" showPassword="true"/>
. . .
</s:form>
The TextField action in the app05a application shows how you can use the textfield,
password, and hidden tags. The action is associated with the TextFieldTestAction class
in Listing 5.1 and is forwarded to the TextField.jsp page in Listing 5.2.
Listing 5.1. The TextFieldTestAction class
package app05a;
import com.opensymphony.xwork2.ActionSupport;
public class TextFieldTestAction extends ActionSupport {
private String userName;
private String password;
private String code;
http://localhost:8080/app05a/TextField.action
The rendered form and input elements are shown in Figure 5.1. The tooltip attribute for
each input tag results in the default tooltip icon to be displayed.
Figure 5.1. Using textfield, password, and hidden
The attributes for the submit tag are listed in Table 5.7.
Table 5.7. submit tag attributes
Name Data Default Description
Type Value
type String input The type of the rendered element. The value can be
input, button, or image.
<s:submit value="Login"/>
type String input The type of the rendered element. The value can be
input or button.
The label tag is rendered as an HTML label element. Its attribute is given in Table 5.9.
This tag is rendered as a textarea element. Its attributes are shown in Table 5.10.
For example, the TextAreaTestAction class in Listing 5.3 has a property that is mapped
to a textarea tag on the TextArea.jsp page in Listing 5.4.
http://localhost:8080/app05a/TextArea.action
Like other input elements, an HTML checkbox adds a request parameter to the HTTP request
when the containing form is submitted. The value of a checked checkbox is "on." If the
name of the checkbox element is subscribe, for example, the key/value pair of the
corresponding request parameter is
subscribe=on
However, an unchecked checkbox does not add a request parameter. It would be good if it
sent this:
subscribe=off
And here lies the problem: There's no way for the server to know if a checked checkbox has
been unchecked. Consider an object in the HttpSession that has a boolean property linked
with a checkbox. A value of "on" (when the check box is checked) would invoke the
property setter and set the value to true. An unchecked checkbox would not invoke the
property setter and, as a result, if the previous value was true, it would remain true.
The checkbox tag overcomes this limitation by creating an accompanying hidden value. For
example, the following checkbox tag
is rendered as
If the checkbox is checked when the containing form is submitted, both values (the check
box and the hidden value) will be sent to the server. If the checkbox is not checked, only
the hidden field is sent, and the absence of the checkbox parameter indicates that the
checkbox was unchecked. The Checkbox interceptor helps make sure the property setter
gets invoke regardless the state of the checkbox. A checked checkbox will pass the String
literal "true" to the property setter and an unchecked one will pass the String literal
"false."
http://localhost:8080/app05a/CheckBox.action
The last checkbox is disabled and its value cannot be changed. Sometimes you may want to
display a disabled checkbox to show the user a default selection that cannot be changed.
The checkbox tag has a fieldValue attribute that specifies the actual value that is sent to
the server when the containing form of a checked checkbox is submitted. If no fieldValue
attribute is present, the value of the checkbox is either "true" or "false." If it is present
and the checkbox was checked, the value of the fieldValue is sent. If the fieldValue
attribute is present and the checkbox is unchecked, no request parameter associated with
the checkbox will be sent.
This attribute can be used to send selected values of a series of checkboxes. For example,
the CheckBoxTest2Action class in Listing 5.7 has a getter that returns a list of
Magazine objects. You can use the checkbox tag and the fieldValue attribute to
construct the same number of checkboxes as the number of magazines on the list, as
shown in the CheckBox2.jsp page in Listing 5.8. Each checkbox is assigned a magazine
code.
Listing 5.7. The CheckBoxTest2Action class
package app05a;
import com.opensymphony.xwork2.ActionSupport;
import java.util.ArrayList;
import java.util.List;
class Magazine {
private String code;
private String name;
public Magazine(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}
The iterator tag will iterate over the magazine list and will be explained in Chapter 6,
"Generic Tags." The whole form will be rendered as
<form ...>
<input type="checkbox" name="magazines" value="034" .../>
<input type="hidden" name="__checkbox_magazines" value="034" />
<input type="checkbox" name="magazines" value="122" .../>
<input type="hidden" name="__checkbox_magazines" value="122" />
<input type="checkbox" name="magazines" value="434" />
<input type="hidden" name="__checkbox_magazines" value="434" />
<input type="checkbox" name="magazines" value="906" />
<input type="hidden" name="__checkbox_magazines" value="906" />
</form>
All checkboxes have the same name (magazines) which means their values are linked to an
array or a collection. If a checkbox is checked, its value (magazine code) will be sent. If it is
not, its value will not be sent. As such, you'll know which magazines have been selected.
http://localhost:8080/app05a/CheckBox2.action
The checkboxes are shown in Figure 5.4. Note that there are four checkboxes
constructed since there are four magazines on the list.
The checkboxlist tag renders multiple checkboxes too, but its layout is fixed. Using
checkbox tags, on the other hand, gives you more flexibility in laying out the rendered
elements.
A radio set, for example, needs options. Consider these HTML input tags that are rendered
as radio buttons shown in Figure 5.5.
As you can see, the radio set has a set of values (1, 2, 3) and a set of labels (Atlanta,
Chicago, Detroit). The value/label pairs are as follows.
1 - Atlanta
2 - Chicago
3 - Detroit
Select elements also need options. This select element (shown in Figure 5.6) features the
same options as the radio set.
Note
In a select element, the value attribute is optional. If it is not present, the label will be sent
as the value when the corresponding option is selected. With radio buttons, the value
attribute is not required but when the value attribute is absent, "on" will be sent, and not
the label. Therefore, a radio button must always have the value attribute.
This section explains how you can use the list, listKey, and listValue attributes in the
radio, select, and other tags that require options. When you use these tags, you need to
have label/value pairs as the source of your options. Of the three attributes, the list
attribute is required and the other two are optional. You can assign a String, an array, a
java.util.Enumeration, a java.util.Iterator, a java.util.Map, or a Collection to the list
attribute. The object can be placed in an action object, in the session object, or the
ServletContext object.
Note
If the object you dynamically assign to the list attribute has no options, you must return an
empty array/Collection/Map instead of null.
Assigning A String
You can assign a String representation of an array. For example, the following select tag is
assigned a string.
<select>
<option value="Atlanta">Atlanta</option>
<option value="Chicago">Chicago</option>
<option value="Detroit">Detroit</option>
</select>
Note that each string element is used as both the value and the label.
Most of the time, you want to use values that are different from labels for your options. In
this case, the syntax is this:
is rendered as
<select>
<option value="l">Atlanta</option>
<option value="2">Chicago</option>
<option value="3">Detroit</option>
</select>
Assigning a Map
You use a Map as the source for your options if the value of each option needs to be
different from the label. Using a Map is very straightforward. Put the values as the Map
keys and the labels as the Map values. For example, here is how to populate a Map called
cities with three cities:
If cities is an action property, you can assign it to the list attribute. Like this:
<s:select list="cities"/>
<s:select list="#application.cities"/>
You use an array or a Collection of objects as the source for options. In this case, you need
to use the list, listKey, and listValue attributes. Assign the array or Collection to the list
attribute. Assign to listKey the object property that will supply the value of each option and
to listValue the object property that will supply the label of each option.
For example, assuming that the action object's getCities method return a List of City
objects with an id and a name properties, you would use the following to assign the List to
a select tag.
The radio tag adds three attributes listed in Table 5.12. * indicates a required attribute.
listKey String The property of the object in the list that will supply the
option values.
listValue String The property of the object in the list that will supply the
option labels.
The following example uses two radio tags to get the user type and the income level on a
club membership form. The first tag gets its options from a hardcoded list and the second
tag gets its options from a Map.
The RadioTestAction class in Listing 5.9 is the action class for this example. Note that
the incomeLevels Map is a static variable that is populated inside a static block so that it's
only populated once for all instances of the action class.
Listing 5.9. The RadioTestAction class
package app05a;
import java.util.SortedMap;
import java.util.TreeMap;
import com.opensymphony.xwork2.ActionSupport;
A SortedMap is used instead of a Map to guarantee that the options are rendered in the
same order as the key. Using a Map does not provide the same guarantee.
http://localhost:8080/app05a/Radio.action
Note that the first radio tag is rendered as two radio buttons, in accordance with the
number of hardcoded options. The second radio tag translates into four radio buttons
because it's linked to a Map with four elements.
The select Tag
The select tag renders a select element. Its attributes are given in Table 5.13.
headerKey String The key for the first item in the list.
headerValue String The value for the first item in the list.
listKey String The property of the object in the list that will
supply the option values.
listValue String The property of the object in the list that will
supply the option labels.
The headerKey and headerValue attributes can be used to insert an option. For instance,
the following select tag inserts a header.
The following example is used to let the user select a country and a city using two select
elements. The first select element displays three countries (US, Canada, Mexico) from a
Map in the ServletContext object. You normally put a selection of options in a
ServletContext if you intend to use the options from many different points in your
application. You use the ServletContextListener in Listing 5.11 to populate the Map.
The second select tag dynamically displays cities in the selected country. If the selected
country is US, the select element displays Atlanta, Chicago, and Detroit. If the selected
country is Canada, Vancouver, Toronto, and Montreal are displayed. Because the cities are
dynamic, the options are generated in the action class. Note that the selection is presented
in an array of City object. The City class has two properties, id and name. The action
class and the City class are shown in Listing 5.12.
} else if (country == 3) {
cities = new City[2];
cities[0] = new City(7, "Mexico City");
cities[1] = new City(8, "Tijuana");
} else {
cities = new City[0];
}
return cities;
}
public int getCity() {
return city;
}
public void setCity(int city){
this.city = city;
}
public int getCountry() {
return country;
}
public void setCountry(int country) {
this.country = country;
}
}
class City {
private int id;
private String name;
public City(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setld(int id) {
this.id = id;
}
public String getName () {
return name;
}
public void setName(String name) {
this.name = name;
}
The country select tag has its emptyOption attribute set to true to provide an empty
option and its list attribute set to the countries scoped variable in the application implicit
object. In addition, its onchange attribute is assigned a Javascript function that will submit
the containing form when the value of the select element changes. This way, when the user
selects a country, the form will be submitted and invokes the action object that prepares
the city options in the getCities method.
http://localhost:8080/app05a/Select.action
Figure 5.8 shows the city options when US is selected and Figure 5.9 shows what
cities the user can choose when the country is Canada.
Figure 5.8. The city options for US
listKey String The property of the object in the list that will supply the
option values.
listValue String The property of the object in the list that will supply the
option labels.
For example, the OptGroupTestAction class in Listing 5.14 is an action class that has
three Map properties, usCities, canadaCities, and mexicoCities.
The OptGroup.jsp page in Listing 5.15 shows how to use the optgroup tag to group
options in the select element in this example.
</s:select>
<s:submit/>
</s:form>
</div>
</body>
</html>
http://localhost:8080/app05a/OptGroup.action
If you're curious, you can view the source and see that the select element is rendered as
these HTML tags.
<optgroup label="Canada">
<option value="4">Vancouver</option>
<option value="6">Montreal</option>
<option value="5">Toronto</option>
</optgroup>
<optgroup label="Mexico">
<option value="8">Tijuana</option>
<option value="7">Mexico City</option>
</optgroup>
</select>
The checkboxlist Tag
The checkboxlist tag is rendered as a group of check boxes. Its attributes are listed in
Table 5.15.
listKey String The property of the object in the list that will supply the
option values.
listValue String The property of the object in the list that will supply the
option labels.
The following example shows how you can use the checkboxlist tag. The property
underlying the checkboxlist is an array of integers. The options come from a List of
Interest objects.
Listing 5.16 shows the CheckBoxListTestAction class, the action class for this
example, and the Interest class.
}
class Interest {
private int id;
private String description;
public Interest(int id, String description) {
this.id = id;
this.description = description;
}
// getters and setters not shown
}
Listing 5.17 shows the CheckBoxList.jsp page that uses a checkboxlist tag.
You can run the action by directing your browser to this URL:
http://localhost:8080/app05a/CheckBoxList.action
headerValue String Text that will be added as a select option but is not
intended to be selected
listKey String The property of the object in the list that will supply
Table 5.16. combobox tag attribute
Name Data Default Description
Type Value
listValue String The property of the object in the list that will supply
the option labels.
Unlike the select tag, the options for a combo box normally do not need keys. Also, the
label of the selected option, and not the value, is sent when the containing form is
submitted.
http://localhost:8080/app05a/ComboBox.action
allowMoveDown boolean true Indicates whether the move down button will be
displayed.
allowSelectAll boolean true Indicates whether the select all button will be
displayed.
headerKey String The key for the first item on the list.
headerValue String The value for the first item on the list.
listKey String The property of the object in the list that will
supply the option values.
listValue String The property of the object in the list that will
supply the option labels.
Note
When the form containing the updownselect tag fails to validate, the previously selected
value(s) of the updownselect tag is not retained.
Listing
The following example shows how to use updownselect to select multiple colors.
5.20 shows an action class (UpDownSelectTestAction) for this example and Listing
5.21 the JSP that uses the tag.
http://localhost:8080/app05a/UpDownSelect.action
addAllToLeftLabel String The label for the Add All To Left button
addAllToRightLabel String The label for the Add All To Right button
first selection.
Note
Only selected (highlighted) options are sent to the server. Simply transferring an option to
the right select element does not make the option selected.
emptyOption="true"
doubleList="{'English'}"
doubleName="selectedLanguages"
doubleHeaderKey="doubleHeaderKey"
doubleMultiple="true"
doubleSize="5"
/>
<s:submit/>
</s:form>
</div>
</body>
</html>
http://localhost:8080/app05a/OptionTransferSelect.action
doubleCssClass String The CSS class for the second select element.
doubleCssStyle String The CSS style for the second select element.
doubleHeaderKey String The header key for the second select element.
headerKey String The header key for the first select element.
listKey String The property of the object in the first list that
will supply the option values.
listValue String The property of the object in the first list that
will supply the option labels.
<s:submit/>
</s:form>
</div>
</body>
</html>
http://localhost:8080/app05a/DoubleSelect.action
Themes
Each UI tag in the Struts Tag Library is rendered to an HTML element or HTML elements.
Struts lets you choose how the rendering should happen. For instance, by default the form
tag is rendered as an HTML form element and a table element. Therefore,
<s:form></s:form>
is translated into
</table>
</form>
The table element is great for formatting because every input tag, such as textfield,
checkbox, and submit, will be rendered as an input element contained within a tr element
and td elements, accompanied by a label.
<tr>
<td class="tdLabel">
<label for="..." class="label">My Label:</label>
</td>
<td>
<input type="text" name="..." id="..."/>
</td>
</tr>
Since most forms are formatted in a table, this kind of rendering helps.
However, sometimes you do not want your textfield tag to be rendered as an input element
in tr and td's and, instead, want it to be translated as a lone <input> because you want to
apply your own formatting. Can you do this?
You can because each UI tag comes with several rendering templates you can choose. One
template renders <s:form> as a form and a table elements, but another translates the
same form tag into a form element, without a <table>. These templates are written in
FreeMarker, but you don't have to know FreeMarker to use these templates.
Similar templates are packaged together into a theme. A theme therefore is a collection of
templates that produce the same look and feels for all UI tags. There are currently four
themes available:
• simple. Templates in the simple theme translate UI tags into their simplest HTML
equivalents and will ignore the label attribute. For example, using this theme a
<s:form> is rendered as a form element, without a table element. A textfield tag
translates into an input element without bells and whistles.
• xhtml. The xhtml theme is the default theme. Templates in this collection provides
automatic formatting using a layout table. That's why a <s:form> is rendered as a
<form> and a <table>.
• css_xhtml. Templates in this theme are similar to those in the xhtml theme but
rewritten to use CSS for layout.
• ajax. This theme contains templates based on xhtml templates but provides
advanced AJAX features. AJAX programming will be discussed in Chapter 27, "AJAX".
All the templates from the four themes are included in the struts-core-VERSION.jar file,
under the template directory.
Now that you know how UI tags are rendered, it's time to learn how to choose a theme for
your UI tags.
As mentioned earlier, if you don't specify a theme, the templates in the xhtml theme will be
used. To easiest way to change a theme for a UI tag is by using the theme attribute of that
tag. For example, the following textfield tag uses the simple theme:
<s:form theme="css_xhtml">
<s:checkbox theme="simple" name="daily" label="Daily news alert"/>
<s:checkbox name="weekly" label="Weekly reports"/>
<s:checkbox theme="simple" name="monthly" label="Monthly reviews"
value="true" disabled="true"
/>
<s:submit/>
</s:form>
In addition to using the theme attribute, there are two other ways to select a theme:
Summary
Struts comes with a tag library that include UI and non-UI tags. Some of the UI tags are
used for entering form values and are referred to as the form tags. In this chapter you have
learned all the tags in the form tags.
Chapter 6. Generic Tags
As explained in Chapter 5, "Form Tags," Struts comes bundled with a tag library that
contains UI and non-UI tags. In this chapter we look at the non-UI tags, which are also
known as generic tags.
There two types of generic tags, data tag and control tag. The following are the data tags:
• a
• action
• bean
• date
• debug
• i18n
• include
• param
• push
• set
• text
• url
• property
Note
Chapter 9,
The i18n and text tags are related to internationalization and discussed in
"Message Handling." The debug tag is used for debugging and explained in Chapter
16, "Debugging and Profiling."
• if
• elself
• else
• append
• generator
• iterator
• merge
• sort
• subset
Each of the generic tags is discussed in the following sections. The accompanying samples
can be found in the app06a application.
You use the property tag to print an action property. Its attributes are listed in Table
6.1. All attributes are optional.
Table 6.1. property tag attributes
Name Type Default Description
For instance, this property tag prints the value of the customerId action property:
<s:property value="customerId"/>
<s:property value="#session.userName"/>
If the value attribute is not present, the value of the object at the top of the Value Stack
will be printed. By default, the property tag escapes HTML special characters in Table
6.2 before printing a value.
" "
& &
< <
> >
Note that in many cases, the JSP Expression Language provides shorter syntax. For
example, the following EL expression prints the customerId action property.
${customerId}
The Property action in app06a demonstrates the use of the property tag. The action is
associated with the PropertyTestAction class (in Listing 6.1) that has a property
named temperature.
The Property.jsp page in Listing 6.2 prints the value of the temperature property and
the value of the degreeSymbol application attribute. If the degreeSymbol attribute is not
found, the default °F will be used.
http://localhost:8080/app06a/Property.action
The a Tag
The a tag renders an HTML anchor. It can accept all attributes that the a HTML element can.
For example, this a tag creates an anchor that points to www.example.com.
This tag is of not much use, however the a tag in the AJAX tag library, discussed in Chapter
27, "AJAX," is very powerful.
component tag.
For example, the following action tag causes the MyAction action to be executed. The
action object will also be accessible through the obj variable in the Value Stack's context
map.
name String The name of the parameter to be passed to the containing tag.
value String The value of the parameter to be passed to the containing tag.
The value attribute is always evaluated even if it is written without the %{ and }. For
example, the value of the following param tag is the userName action property:
It is the same as
To send a String literal, enclose it with single quotes. For example, the value of this param
tag is naomi.
The value attribute can also be written as text between the start and the end tags.
Therefore, instead of writing
<s:param name="...">[value]</s:param>
The second form allows you to pass an EL expression. For example, the following passes the
current host to the host parameter:
<s:param name="host">${header.host}</s:param>
This will not work:
name* String The fully qualified class name of the JavaBean to be created.
var String The name used to reference the value pushed into the Value
Stack's context map.
In the following example, the DegreeConverter class in Listing 6.3 provides methods to
convert Celcius to Fahrenheit and vice versa. The Bean.jsp page in Listing 6.4 uses the
bean tag to instantiate the class.
http://localhost:8080/app06a/Bean.action
The date tag formats a Java Date object. Its attributes are given in Table 6.6.
Table 6.6. date tag attributes
Name Type Default Description
var String The name used to reference the value pushed to the value
stack.
The format attribute conforms to the date and time patterns defined for the
java.text.SimpleDateFormat class. For example, the Date.jsp page in Listing 6.5 uses
date tags to format dates.
</div>
</body>
</html>
http://localhost:8080/app06a/Date.action
The result is shown in Figure 6.3.
scope String default The scope of the target variable. The value can be application,
session, request, page, or default.
The following example, based on the SetTestAction class in Listing 6.6, shows the
benefit of using set.
}
class Customer {
private String contact;
private String email;
// getters and setters not shown
}
The SetTestAction class's execute method inserts a Customer object to the Session
object. You could display the contact and email properties of the Customer object using
these property tags:
<s:property value="#session.customer.contact"/>
<s:property value="#session.customer.email"/>
However, as you can see from the Set.jsp page in Listing 6.7, you could also push the
variable customer to represents the Customer object in the Session map.
You can then refer to the Customer object simply by using these property tags.
<s:property value="#customer.contact"/>
<s:property value="#customer.email"/>
http://localhost:8080/app06a/Set.action
The push tag only has one attribute, value, described in Table 6.9.
For example, the PushTestAction class in Listing 6.8 has an execute method that
places an Employee object in the HttpSession object.
class Employee {
private int id;
private String firstName;
private String lastName;
// getters and setters not shown
}
The Push.jsp page in Listing 6.9 uses a push tag to push an Employee object to the
Value Stack.
http://localhost:8080/app06a/Push.action
This tag creates a URL dynamically. Its attributes are listed in Table 6.10.
action String The action that the created URL will target.
The url tag can be very useful. For example, this url tag creates a URL for the HTTPS
protocol and includes all the parameters in the current URL.
For instance, this if tag tests if the ref request parameter is null:
And this trims the name property and tests if the result is empty.
In the following example, an if tag is used to test if the session attribute loggedIn exists. If
it is not found, a login form is displayed. Otherwise, a greeting is shown. The example relies
on the IfTestAction class in Listing 6.10 and the If.jsp page in Listing 6.11.
http://localhost:8080/app06a/If.action
status org.apache.struts2.views.jsp.
IteratorStatus
first boolean The value is true if the current element is the first element in the
iterable object.
last boolean The value is true if the current element is the last element in the
iterable object.
modulus int This property takes an integer and returns the modulus of count.
For example, the IteratorTestAction class in Listing 6.12 presents an action class with
two properties, interests and interestOptions, that return an array and a List,
respectively. The Iterator.jsp page in Listing 6.13 shows how to use the iterator tag to
iterate over an array or a Collection.
class Interest {
private int id;
private String description;
public Interest(int id, String description) {
this.id = id;
this.description = description;
}
// getters and setters not shown
}
http://localhost:8080/app06a/Iterator.action
Another helpful use of iterator is to simulate a loop, similar to the for loop in Java. This is
easy to do since all an iterator needs is an array or another iterable object. The following
code creates a table containing four rows. The cells in each row contain two textfield tags
whose names are user[n].firstName and user[n].lastName, respectively. This is useful
when you need to generate a variable number of input boxes.
<table>
<s:iterator value="new int[3]" status="stat">
<tr>
<td><s:textfield
name="%{'users['+#stat.index+'].firstName'}"/></td>
<td><s:textfield
name="%{'users['+#stat.index+'].lastName'}"/></td>
</tr>
</s:iterator>
</table>
This is the same as writing
<table>
<tr>
<td><s:textfield name="users[0].firstName"/></td>
<td><s:textfield name="users[0].lastName"/></td>
</tr>
<tr>
<td><s:textfield name="users[1].firstName"/></td>
<td><s:textfield name="users[1].lastName"/></td>
</tr>
<tr>
<td><s:textfield name="users[2].firstName"/></td>
<td><s:textfield name="users[2].lastName"/></td>
</tr>
</table>
In this case, we generate an array of four ints. We do not need to initialize the array
elements since we're only using the array's status.count attribute.
The following example employs the modulus property of the IteratorStatus object to
format iterated elements in a four-column table.
<table border="1">
<s:iterator id="item" value="myList" status="status">
<s:if test="#status.modulus(4)==1">
<tr>
</s:if>
<td>${item}</td>
<s:if test="#status.modulus(4)==0">
</tr>
</s:if>
</s:iterator>
• List 1, element 1
• List 1, element 2
• List 1, element 3
• List 2, element 1
• List 2, element 2
• List 2, element 3
The append tag adds one attribute, var, which is described in Table 6.14.
var String The variable that will be created to reference the appended
iterators.
For example, the code in Listing 6.14 uses the append tag to concatenate two lists:
<s:append var="allLists">
<s:param value="#list1"/>
<s:param value="#list2"/>
</s:append>
<s:iterator value="#allLists">
<s:property/><br/>
</s:iterator>
one
two
1
2
3
Also, see the merge tag, which is very similar to append. If you replace append with
merge in the example above, you will get
one
1
two
2
3
The merge Tag
The merge tag merges lists and reads an element from each list in succession. Therefore, if
you have two lists with 3 elements each, the new list will have these elements:
• List 1, element 1
• List 2, element 1
• List 1, element 2
• List 2, element 2
• List 1, element 3
• List 2, element 3
The merge tag adds an attribute, var, which is described in Table 6.15.
var String The variable that will be created to reference the appended
iterators.
In the following example, the action class MergeTestAction provides three properties that
each returns a List: americanCars, europeanCars, and japaneseCars. The action class
is given in Listing 6.15.
The Merge.jsp page in Listing 6.16 shows the merge tag in action.
http://localhost:8080/app06a/Merge.action
converter Converter The converter to convert the String entry parsed from val
into an object.
separator* String The separator for separating the val into entries of the
iterator.
When used, the converter attribute must be set to an action property of type Converter,
an inner interface defined in the org.apache.struts2.util.IteratorGenerator class.
The use of the converter is depicted in the second example of this section.
The Generator.jsp page in Listing 6.17 illustrates the use of generator to create a list
of Strings (car makes).
<s:generator id="cameras"
count="3"
val="%{'Canon,Nikon,Pentax,FujiFilm'}"
separator=",">
</s:generator>
<s:iterator value="#attr.cameras">
<s:property/>
</s:iterator>
</div>
</body>
</html>
To test the example, direct your browser here:
http://localhost:8080/app06a/Generator.action
In a generator tag that has a converter, each element of the generated iterator will be
passed to this method.
Listing 6.18. The GeneratorConverterTestAction class
package app06a;
import org.apache.struts2.util.IteratorGenerator;
import com.opensymphony.xwork2.ActionSupport;
public class GeneratorConverterTestAction extends ActionSupport {
public IteratorGenerator.Converter getMyConverter() {
return new IteratorGenerator.Converter() {
public Object convert(String value) throws Exception {
return value.toUpperCase();
}
};
}
}
You can test the example directing your browser to this URL.
http://localhost:8080/app06a/GeneratorConverter.action
As you can see in Figure 6.10, all elements were converted to upper case.
Figure 6.10. The generator converter example
This tag sorts the elements of an iterator. Its attributes are given in Table 6.17.
Note
It is a good design choice to leave data sorting to the presentation layer, even though it
may be easier to sort data at the model or data level using the ORDER BY clause in the SQL
statement. This is a design decision that should be considered carefully.
For example, the SortTestAction class in Listing 6.20 provides a property of type
Comparator that is used by the sort tag in the Sort.jsp page (See Listing 6.21.)
<h4>Cameras</h4>
<s:generator id="cameras"
val="%{'Canon,Nikon,Pentax,FujiFilm'}"
separator=",">
</s:generator>
<s:sort source="#attr.cameras" id="sortedCameras"
comparator="myComparator">
</s:sort>
<s:iterator value="#attr.sortedCameras">
<s:property/>
</s:iterator>
</div>
</body>
</html>
To see the elements in the iterators sorted, direct your browser to this URL:
http://localhost:8080/app06a/Sort.action
This tag creates a subset of an iterator. Its attributes are listed in Table 6.18.
start Integer The starting index of the source iterator to be included in the
subset.
You tell the subset tag how to create a subset of an iterator by using an instance of the
Decider class, which is an inner class of org.apache.struts2.util.SubsetIteratorFilter.
For example, the SubsetTestAction class in Listing 6.22 is a Decider. It will cause a
subset tag to include an element if the String representation of the element is more than
four characters long. The Subset.jsp page in Listing 6.23 employs a subset tag that
uses the Decider.
http://localhost:8080/app06a/Subset.action
From an HTML form to an action object, conversions are from strings to non-strings. All
form inputs are sent to the server as request parameters and each form input is either a
String or a String array because HTTP is type agnostic. At the server side, the web
developer or the framework converts the String to another data type, such as an int or a
java.util.Date.
As you will learn in this chapter, Struts supports type conversions seamlessly. In addition,
this feature is extensible, so you can build your own type converters. Custom converters are
covered in this chapter too.
The Parameters interceptor, one of the interceptors in the default stack, is responsible for
mapping request parameters with action properties. Since all request parameters are
Strings, and not all action properties are of type String, type conversions must be
performed on any non-String action properties. The Parameters interceptor uses the OGNL
API to achieve this. To be precise, if you happen to be interested in the Struts source code,
it is the ognl.OgnlRuntime class, which in turn relies on Java reflection. For every property
that needs to be set, OgnlRuntime creates a java.lang.reflection.Method object and calls its
invoke method.
With the Method class, Strings are automatically converted to other types, enabling user
inputs to be assigned to action properties of type int, java.util.Date, boolean, and others.
The String "123" mapped to an int property will be converted to 123, "12/12/2008" mapped
to a Date property will be converted to December 12, 2008.
Note
As for conversion from String to Date, the date pattern for parsing the String is determined
by the locale of the HTTP request. In the United States, the format is MM/dd/yyyy. To
accept dates in a different pattern from the locale, you have to use a custom converter.
Type conversions, however, run the risk of failing. Trying to assign "abcd" to a Date
property will definitely fail. So will assigning a formatted number such as 1,200 to an int. In
the latter, the comma between 1 and 2 causes it to fail. It is imperative that the user gets
notified when a conversion fails so that he or she may correct the input. It's the
programmer's job to alert the user, but how do you do that?
A failed conversion will leave a property unchanged. In other words, int will retain the value
of 0 and a Date property will remain null. A zero or null value may be an indication that a
type conversion has failed, but it will not be a clear indicator if zero or null is an allowable
value for a property. If zero or null is a valid property value, there's no way you can find out
that a conversion has produced an error other than by comparing the property value with
the corresponding request parameter. Doing so, however, is not recommended. Not only is
checking the request parameter an inelegant solution, it also defeats the purpose of using
Struts because Struts is capable of mapping request parameters to action properties.
A failed type conversion will not necessarily stop Struts. There are two possible outcomes
for this misbehavior. Which one will happen depends on whether or not your action class
implements the com.opensymphone.xwork2.ValidationAware interface.
If the action class does not implement this interface, Struts will continue by invoking the
action method upon failed type conversions, as if nothing bad had happened.
If the action class does implement ValidationAware, Struts will prevent the action method
from being invoked. Rather, Struts will enquiry if the corresponding action element
declaration contains an input result. If so, Struts will forward to the page defined in the
result element. If no such result was found, Struts will throw an exception.
You can override the default error message by providing a key/value pair of this format:
Here, fieldName is the name of the field for which a custom error message is provided. The
key/value pair must be added to a ClassName.properties file, where ClassName is the
name of the class that contains the field that is the target of the conversion. Further, the
ClassName.properties file must be located in the same directory as the Java class.
In addition to customizing an error message, you can also customize its CSS style. Each
error message is wrapped in an HTML span element, and you can apply formatting to the
message by overriding the errorMessage CSS style. For example, to make type conversion
error messages displayed in red, you can add this to your JSP:
<style>
.errorMessage {
color:red;
}
</style>
A type conversion error customization example is given in the app07a application. The
directory structure of this application is shown in Figure 7.1.
The Transaction action class in Listing 7.1 has four properties: accountId (String),
transactionDate (Date), amount (double), and transactionType (int). More
important, Transaction extends the ActionSupport class, thus indirectly implementing
ValidationAware.
Note
We could use java.util.Currency for amount, but using a double serves as a good
example for the type conversions in this example.
There are two actions in this example, Transaction1 and Transaction2. The following are
the declarations for the actions in the struts.xml file.
<action name="Transaction1">
<result>/jsp/Transaction.jsp</result>
</action>
<action name="Transaction2" class="app07a.Transaction">
<result name="input">/jsp/Transaction.jsp</result>
<result name="success">/jsp/Receipt.jsp</result>
</action>
Transaction1 simply displays the Transaction.jsp page, which contains a form and is
shown in Listing 7.2. Transaction2 has two result branches. The first one is executed if
the action method returns "input," as is the case when there is a type conversion error. The
second one is executed if no type conversion error occurs and forwards to the Receipt.jsp
page in Listing 7.3.
The Transaction.properties file, shown in Listing 7.4, overrides the type conversion
error message for the transactionDate field. This file must be located in the same
directory as the Transaction action class.
To test this example, invoke the Transaction1 action by directing your browser here:
http://localhost:8080/app07a/Transaction1.action
You'll see the form with four input boxes as in Figure 7.2.
Figure 7.2. The Transaction.jsp page
To test the type conversion feature in Struts, I deliberately enter incorrect values in the
Transaction Date and Amount boxes. In the Transaction Date box I enter abcd and in the
Amount box I type 14,999.95. After the form is submitted, you will see the same form as
shown in Figure 7.3.
Figure 7.3. Failed type conversions
What happened was abcd could not be converted to a Date. 14,999.50 looks like a valid
numerical value, but its formatting makes it a bad candidate for a double, the type of the
amount property. Had I entered 14999.50, Struts would happily have converted it to a
double and assigned it to the amount property.
The Transaction Date field is being adorned with the custom error message specified in the
Transaction.properties file. The Amount field is being accompanied by a default error
message since the Transaction.properties file does not specify one for this field.
An important thing to notice is that the wrong values are re-displayed. This is an important
feature since the user can easily see what is wrong with his/her form.
Custom Type Converters
Sophisticated as they may be, the built-in type converters are not adequate. They do not
allow formatted numbers (such as 1,200) to be converted to a java.lang.Number or a
primitive. They are not smart enough to permit an arbitrary date pattern to be used. To
overcome this limitation, you need to build your own converter. Happily, this is not hard to
do.
The TypeConverter interface has only one method, convertValue, whose signature is as
follows. Struts invokes this method and passes the necessary parameters whenever it needs
the converter's service.
• context. The OGNL context under which the conversion is being performed.
• target. The target object in which the property is being set
• member. The class member (constructor, method, or field) being set
• propertyName. The name of the property being set
• value. The value to be converted.
• toType. The type to which the value is to be converted.
The context argument is very useful as it contains references to the Value Stack and
various resources. For example, to retrieve the Value Stack, use this code:
And, of course, once you have a reference to the Value Stack, you can obtain a property
value by using the findValue method:
valueStack.findValue(propertyName);
context.get(StrutsStatics.SERVLET_CONTEXT);
context.get(StrutsStatics.HTTP_REQUEST);
context.get(StrutsStatics.HTTP_RESPONSE);
For a custom converter to function, you need to provide code that works for each supported
type conversion. Typically, a converter should support at least two type conversions, from
String to another type and vice versa. For instance, a currency converter responsible for
converting String to double and double to String would implement convertValue like
this:
Before you can use a custom type converter in your application, you must configure it.
Configuration can be either field-based or class-based.
Field-based configuration allows you to specify a custom converter for each property in an
action. You do this by creating a file that must be named according to the following format.
ActionClass-conversion.properties
Here, ActionClass is the name of the action class. For instance, to configure custom
converters for an action class called User, create a filed named User-
conversion.properties. The content of this file would look something like this.
field1=customConverter1
field2=customConverter2
...
In addition, the configuration file must reside in the same directory as the action class. The
app07b application shows how you can write a field-based configuration file for your
custom converters.
In class-based configuration you specify the converter that will convert a request parameter
to an instance of a class. In this case, you create an xwork-conversion.properties file
under WEB-INF/classes and pair a class with a converter. For example, to use
CustomConverter1 for a class, you'll write
fullyQualifiedClassName=CustomConverter1
...
The date converter is encapsulated in the MyDateConverter class in Listing 7.7. Only
conversions from String to Date are catered for. Date to String is not important since you
can use the date tag to format and print a Date property.
Listing 7.7. The MyDateConverter class
package app07b.converter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.struts2.StrutsStatics;
import ognl.DefaultTypeConverter;
import com.opensymphony.xwork2.util.TypeConversionException;
You can use any date pattern for formatting the dates and parsing the Strings. You pass
the date pattern as an initial parameter to the ServletContext object. If you open the
web.xml file of app07b, you'll see this context-param element, which indicates that the
date pattern is yyyy-MM-dd.
<context-param>
<param-name>datePattern</param-name>
<param-value>yyyy-MM-dd</param-value>
</context-param>
As you can see in the if block in Listing 7.7, you first need to obtain the date pattern
from the ServletContext object. After that you employ a java.text.DateFormat to
convert a String to a Date.
http://localhost:8080/app07b/Transaction1.action
Extending StrutsTypeConverter
Since in most type converters you need to provide implementation for String to non-String
conversions and the other way around, it makes sense to provide an implementation class
of TypeConverter that separates the two tasks into two different methods. The
StrutsTypeConverter class, a child of DefaultTypeConverter, is such a class. There are
two abstract methods that you need to implement when extending StrutsTypeConverter,
convertFromString and convertToString. See the StrutsTypeConverter class definition
in Listing 7.9.
The directory structure of app07c is shown in Figure 7.6. There are two actions defined
in it, Design1 and Design2, as described in the struts.xml file accompanying app07c.
The action declarations are printed in Listing 7.11.
Figure 7.6. app07c directory structure
The Design1 action is used to take a design from the user. A design is modeled as an
instance of the Design class in Listing 7.12. It is a simple class that has two properties,
designName and color.
color=app07c.converter.MyColorConverter
http://localhost:8080/app07c/Design1.action
You will see a form with two text fields like the one in Figure 7.7. Enter a design name
and a color.
This sample application has two actions, Admin1 and Admin2, that can be used to add an
Employee to the database. Every time a new employee is added, the admin id must also be
noted because there are multiple users in the admin role. The action declarations in the
struts.xml are shown in Listing 7.15.
The Admin class (See Listing 7.16) has two properties, adminId and employee,
adminId is a String, but employee is of type Employee, another class (shown in Listing
7.17) with its own properties (firstName, lastName, and birthDate). With one HTML
form, how do you populate an Admin and an Employee and at the same time use a
custom converter for the birthDate property?
Listing 7.16. The Admin class
package app07d;
import com.opensymphony.xwork2.ActionSupport;
public class Admin extends ActionSupport {
private Employee employee;
private String adminId;
// getters and setters not shown
return SUCCESS;
}
}
The answer is simple: OGNL. A form tag can be mapped to a property's property. For
example, to map a field to the firstName property of the employee property of the action,
use the OGNL expression employee.firstName. The Admin.jsp page in Listing 7.18
shows the form whose fields map to two objects.
The Confirmation.jsp page in Listing 7.19 shows how to display the adminId property
as well as the properties of the employee property.
Last but not least, the birthDate property of the Employee class must be configured to
use the MyDateConverter converter. Listing 7.20 shows the Admin-
conversion.properties file that registers MyDateConverter for birthDate.
http://localhost:8080/app07d/Admin1.action
The Admin1 action displays the form for entering two employees and Admin2 inserts the
employees to the database and displays the added data. The Admin1b action is additional
and allows any number of employees. Admin1b will be discussed at the end of this section.
The Admin class and the Employee class are given in Listing 7.22 and Listing 7.23,
respectively. Note that the Admin class contains an employee property that is of
Collection type.
Listing 7.22. The Admin class
package app07e;
import java.util.Collection;
import com.opensymphony.xwork2.ActionSupport;
The Admin.jsp page in Listing 7.24 contains a form that allows you to enter two
employees. The first employee will become the first element of the Collection employees
property in the Admin action. It is denoted by employees [0], and the second employee is
employees [1]. Consequently, the textfield tag mapped to the lastName property of the
first employee has its name property assigned employees[0].lastName.
The Confirmation.jsp page, shown in Listing 7.25, uses the iterator tag to iterate over
the employees property in the Admin action. It also employs the date tag to format the
birthdates.
You can test this example by directing your browser to this URL.
http://localhost:8080/app07e/Admin1.action
Go ahead and add data to the form and submit it. Figure 7.12 shows the data displayed.
Figure 7.12. Displaying added employees
Being able to add two employees is great, but you probably want more. The rest of the
section discusses how get more flexibility.
Instead of hardcoding the text fields for employees as we did in the Admin.jsp page, we
use an iterator tag to dynamically build text fields. For example, to create four sets of
fields, you need an iterator tag with four elements like this.
Or, better still, you can pass a count request parameter to the URL and use the value to
build the iterator:
new int[#parameters.count[0]]
Note that the [0] is necessary because parameters always returns an array of Strings,
not a String.
Here are the tags that build text fields on the fly. You can find them in the Admin1b.jsp
page in app07e.
http://localhost:8080/app07e/Admin1b.action?count=n
where n is the number of rows you want created. You can now enter as many employees as
you want in one go.
The action declarations, shown in Listing 7.26, are similar to those in app07e. Admin1
displays a multiple record entry form, Admin2 displays the entered data, and Admin1b
can be used to add any number of employees.
Listing 7.26. The action declaration
<package name="app07f" extends="struts-default">
<action name="Admin1">
<result>/jsp/Admin.jsp</result>
</action>
<action name="Admin2" class="app07f.Admin">
<result name="input">/jsp/Admin.jsp</result>
<result name="success">/jsp/Confirmation.jsp</result>
</action>
<action name="Admin1b">
<result>/jsp/Admin1b.jsp</result>
</action>
</package>
The Admin class is given inListing 7.27. Note that the employees property is a Map.
The Employee class is presented in Listing 7.28 and is a template for employees.
To populate a Map property, which employees is, you need to tell Struts what class to
instantiate for each entry. The Admin-conversion.properties file in Listing 7.29 is a
field-based configuration file that indicates that every element of the employees property
is an instance of app07f.Employee and that it should create a new Map if employees is
null.
Listing 7.29. The Admin-conversion.properties file
Element_employees=app07f.Employee
CreateIfNull_employees=true
On top of that, we want to use a date converter for the birthDate property in Employee.
Listing 7.30 shows the field-based configuration file for the Employee class.
The Admin.jsp page in Listing 7.31 contains a form for entering two employees,
employees['user0'].lastName indicates the lastName property of the entry in the
employees Map whose key is user0.
Listing 7.32 shows the Confirmation.jsp page that displays entered data by iterating
over the employees Map.
http://localhost:8080/app07f/Admin1.action
You will see a form like the one in Figure 7.14. Enter values in the text fields and submit
the form, and you will see the entered data displayed, as shown in Figure 7.15.
To have a form for entering n employees, use the technique described in app07e.
Summary
Struts performs type conversions when populating action properties. When a conversion
fails, Struts also displays an error message so that the user knows how to correct the input.
You've learned in this chapter how to override the error message.
Sometimes default type conversions are not sufficient. For example, if you have a complex
object or you want to use a different format than the default, you need to write custom
converters. This chapter has also shown how to write various custom converters and
configure them.
Chapter 8. Input Validation
A robust web application must ensure that user input is valid. For instance, you may want to
make sure that user information entered in a form will only be stored in the database if the
selected password is at least n characters long and the birth date is a date that is no later
than today's date. Struts makes input validation easy by providing built-in validators that
are based on the XWork Validation Framework. Using these validators does not require
programming. Instead, you declare in an XML file how a validator should work. Among the
things to declare are what field needs to be validated and what message to send to the
browser if a validation fails.
In more complex scenarios, built-in validators can help little and you have to write code to
validate input. This is called programmatic validation and, along with built-in validators, is
discussed in this chapter.
Validator Overview
There are two types of validators, field validators and plain validators (non-field validators).
A field validator is associated with a form field and works by verifying a value before the
value is assigned to an action property. Most bundled validators are field validators. A plain
validator is not associated with a field and is used to test if a certain condition has been
met. The validation interceptor, which is part of the default stack, is responsible for loading
and executing registered validators.
2. Write a validator configuration file. The file name must follow one of these two
patterns:
90
ActionClass-validation.xml
ActionClass-alias-validation.xml
The first pattern is more common. However, since an action class can be used by
multiple actions, there are cases whereby you only want to apply validation on certain
actions. For example, the UserAction class may be used with User_create and
User_edit actions. If both actions are to be validated using the same rules, you can
simply declare the rules in a UserAction-validation.xml file. However, if User_create
and User_edit use different validation rules, you must create two validator
configuration files, UserAction-User_create-validation.xml and UserAction-
User_edit-validation.xml.
3. Determine where the user should be forwarded to when validation fails by defining a
<result name="input"> element in the struts.xml file. Normally, the value of the result
element is the same JSP that contains the validated form.
All bundled validators are registered by default and can be used without you having to
worry about registration. Registration becomes an issue if you're using a custom validator.
If this is the case, read the section "Writing Custom Validators" later in this chapter.
Validator Configuration
The task of configuring validators centers around writing validator configuration files, which
are XML documents that must comply with the XWork validator DTD.
The root element of a validator configuration file is validators. <validators> may have any
number of field and validator elements. A field element represents a form field to which one
or more field validators will be applied. A validator element represents a plain validator.
Here is the skeleton of a typical validator configuration file.
...
<validator type="...">
...
</validator>
<validator type="...">
...
</validator>
...
</validators>
The name attribute in a field element specifies the form field to be validated.
You can apply any number of validators to a form field by nesting field-validator elements
within the field element. For instance, the following field element indicates that the
userEmail field must be validated by required and email validators.
<field name="userEmail">
<field-validator type="required">
</field-validator>
<field-validator type="email">
</field-validator>
</field>
<field name="userEmail">
<field-validator type="required" short-circuit="true">
</field-validator>
<field-validator type="email">
</field-validator>
</field>
You can pass parameters to a validator by nesting param elements within the field-validator
element. You can also define a validation error message by using the message element
within the field-validator element. As an example, this stringlength field validator receives
two parameters, minLength and maxLength, and the error message that must be displayed
when validation fails.
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">14</param>
<message>
User name must be between 6 and 14 characters long
</message>
</field-validator>
A field-validator element can have zero or more param element and at most one message
element.
The validator element is used to represent a plain validator. It can also contain multiple
param element and a message element. For example, the following validator element
dictates that the max field must be greater than the min field or validation will fail.
<validator type="expression">
<param name="expression">
max > min
</param>
<message>
Maximum temperature must be greater than Minimum temperature
</message>
</validator>
Like field-validator, the validator element must have a type attribute and may have a short-
circuit attribute.
Bundled Validators
Struts comes with these built-in validators.
• required validator.
• requiredstring validator
• int validator
• date validator
• expression validator
• fieldexpression validator
• email validator
• url validator
• visitor validator
• conversion validator
• stringlength validator
• regex validator
required Validator
This validator makes sure that a field value is not null. An empty string is not null and
therefore will not raise an exception.
For instance, the RequiredTestAction class in Listing 8.1 has two properties,
userName and password, and employs a validator configuration file presented in Listing
8.2.
Listing 8.1. The RequiredTestAction class
package app08a;
import com.opensymphony.xwork2.ActionSupport;
public class RequiredTestAction extends ActionSupport {
private String userName;
private String password;
// getters and setters not shown
}
<validators>
<field name="userName">
<field-validator type="required">
<message>Please enter a user name</message>
</field-validator>
</field>
<field name="password">
<field-validator type="required">
<message>Please enter a password</message>
</field-validator>
</field>
</validators>
When you submit a form to RequiredTestAction, two fields are required. Listing 8.3
shows the JSP used to demonstrate the required validator. The userName textfield tag
has been commented out to trigger the validator.
http://localhost:8080/app08a/Required2.action
Figure 8.1 shows the form after a failed validation. It is rejected since the userName
field is missing.
requiredstring validator
The requiredstring validator ensures a field value is not null and not empty. It has a trim
parameter that by default has a value of true. If trim is true, the validated field will be
trimmed prior to validation. If trim is false, the value of the validated field will not be
trimmed. The trim parameter is described in Table 8.1.
Table 8.1. requiredstring validator parameter
Name Data Description
Type
trim boolean Indicates whether or not trailing spaces will be trimmed prior to
validation.
With trim true, a field that contains only spaces will fail to be validated.
The following example validates the fields associated with the properties of the
RequiredStringTestAction class in Listing 8.4. The validation configuration file in
Listing 8.5 assigns the requiredstring validator to the userName and password fields.
<validators>
<field name="userName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>Please enter a user name</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<param name="trim">false</param>
<message>Please enter a password</message>
</field-validator>
</field>
</validators>
Note that the requiredstring validator for the userName has its trim parameter set to true,
which means a space or spaces do not qualify. The RequiredString.jsp page in Listing
8.6 shows the form for this example.
http://localhost:8080/app08a/RequiredString1.action
Submitting the form without first entering values to the fields will result in the form being
returned.
Figure 8.2. Using requiredstring
stringlength Validator
You use stringlength to validate that a non-empty field value is of a certain length. You
specify the minimum and maximum lengths through the minLength and maxLength
parameters. The complete list of parameters is given in Table 8.2.
minLength int The maximum length allowed. If this parameter is not present,
there will be no maximum length restriction for the associated
field.
maxLength int The minimum length allowed for the associated field. If this
parameter is not present, there will be no minimum length
restriction for the field.
Table 8.2. stringlength validator parameters
Name Data Description
Type
trim boolean Indicates whether or not trailing spaces will be trimmed prior to
validation.
For example, the StringLengthTestAction class in Listing 8.7 defines two properties,
userName and password. A user name must be between six to fourteen characters long
and the stringlength validator is used to ensure this. The validator configuration file for this
example is presented in Listing 8.8. The StringLength.jsp page in Listing 8.9 shows
the form whose field is mapped to the userName property.
<validators>
<field name="userName">
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">14</param>
<message>
User name must be between 6 and 14 characters long
</message>
</field-validator>
</field>
</validators>
Listing 8.9. The StringLength.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>stringlength Validator Example</title>
<style type="text/css">@import url(css/main.css);</style>
<style>
.errorMessage {
color:red;
}
</style>
</head>
<body>
<div id="global" style="width:480px">
<h3>Select a user name</h3>
<s:form action="StringLength2">
<s:textfield name="userName"
label="User Name (6-14 characters)"/>
<s:submit/>
</s:form>
</div>
</body>
</html>
http://localhost:8080/app08a/StringLength1.action
int Validator
The int validator checks if a field value can be converted into an int and, if the min and max
parameters are used, if its value falls within the specified range. The int validator's
parameters are listed in Table 8.3.
min int The maximum value allowed. If this parameter is not present, there's
no maximum value.
max int The minimum value allowed. If this parameter is not present, there's no
minimum value.
The validator configuration file in Listing 8.11 guarantees that any year value submitted
to an IntTestAction object must be between 1990 and 2009 (inclusive).
<validators>
<field name="year">
<field-validator type="int">
<param name="min">1990</param>
<param name="max">2009</param>
<message>Year must be between 1990 and 2009</message>
</field-validator>
</field>
</validators>
The Int.jsp page in Listing 8.12 shows a form with a textfield tag named year. Upon
the form submit, the validator will kick in to make sure the value of year is within the
prescribed range.
http://localhost:8080/app08a/Int1.action
This validator checks if a specified date field falls within a certain range. Table 8.4 lists all
possible parameters of the date validator.
max date The maximum value allowed. If this parameter is not present, there will
be no maximum value.
min date The minimum value allowed. If this parameter is not present, there will
be no minimum value.
Note
The date pattern used to validate a date is dependant on the current locale.
<validators>
<field name="birthDate">
<field-validator type="date">
<param name="max">1/1/2000</param>
<message>
You must have been born before the year 2000 to register
</message>
</field-validator>
</field>
</validators>
The configuration file specifies that the year value must be before January 1, 2000. The
date pattern used here is US_en.
The Date.jsp page in Listing 8.15 contains a form that submits the birthDate field to
the DateTestAction action.
http://localhost:8080/app08a/Date1.action
The form will be shown in your browser and will look like that in Figure 8.5.
Figure 8.5. Using the date validator
email Validator
The email validator can be used to check if a String evaluates to an email address. This
validator uses the Java Regular Expression API and use the following pattern:
"\\b(^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@([A-Za-z0-9-])+(\\.[A-Za-z0-9-
]+)*((\\.[A-Za-z0-9]{2,})|(\\.[A-Za-z0-9]{2,}\\.[A-Za-z0-9]{2,}))$)\\b"
This means an email can start with any combination of letters and numbers that is followed
by any number of periods and letters and numbers. It must have a @ character followed by
a valid host name.
<validators>
<field name="email">
<field-validator type="email">
<message>Invalid email</message>
</field-validator>
</field>
</validators>
http://localhost:8080/app08a/Email1.action
Figure 8.6 shows the form that contains a textfield tag named email.
Figure 8.6. Using the email validator
url Validator
The url validator can be used to check if a String qualifies as a valid URL. The validator
does it work by trying to create a java.net.URL object using the String. If no exception is
thrown during the process, validation is successful.
http://www.google.com
https://hotmail.com
ftp://yahoo.com
file:///C:/data/V3.doc
java.com
As an example, consider the UrlTestAction class in Listing 8.19 has a url property that
Listing
will be validated using the url validator. The validation configuration file is given in
8.20.
Listing 8.19. The UrlTestAction class
package app08a;
import com.opensymphony.xwork2.ActionSupport;
public class UrlTestAction extends ActionSupport {
private String url;
// getter and setter not shown
}
The Url.jsp page in Listing 8.21 contains a form with a textfield tag named url.
http://localhost:8080/app08a/Url1.action
Figure 8.7 shows the form.
regex Validator
This validator checks if a field value matches the specified regular expression pattern. Its
parameters are listed in Table 8.5. See the documentation for the
java.lang.regex.Pattern class for more details on Java regular expression patterns.
caseSensitive boolean Indicates whether or not the matching should be done in a case
sensitive way. The default value is true.
trim boolean Indicates whether or not the field should be trimmed prior to
validation. The default value is true.
expression and fieldexpression Validators
The expression and fieldexpression validators are used to validate a field against an OGNL
expression, expression and fieldexpression are similar, except that the former is not a field
validator whereas the latter is. The other difference is a failed validation of the expression
validator will generate an action error, fieldexpression will raise a field error on a failed
validation. The parameter for these validators is given in Table 8.6.
expression* String The OGNL expression that governs the validation process.
There are two examples in this section. The first one deals with the expression validator, the
second with the fieldexpression validator.
The ExpressionTestAction class in Listing 8.22 has two properties, min and max, that
will be used in the OGNL expression of an expression validator instance. Listing 8.23
shows a validator configuration file that uses the expression validator and specifies that the
value of the max property must be greater than the value of min. Listing 8.24 shows a
JSP with a form with two fields.
<validators>
<validator type="expression">
<param name="expression">
max > min
</param>
<message>
Maximum temperature must be greater than Minimum
temperature
</message>
</validator>
</validators>
http://localhost:8080/app08a/Expression1.action
You'll see a form like the one in Figure 8.8. You can only submit the form successfully if
you entered integers in the input fields and the value of min was less than the value of
max.
Figure 8.8. Using expression
The FieldExpressionTestAction class in Listing 8.25 defines two properties, min and
max, that will have to meet a certain criteria, namely min must be less than max. The
Listing 8.26 specifies an OGNL expression for the
validator configuration file in
fieldexpression validator. Listing 8.27 shows the JSP used in this example.
http://localhost:8080/app08a/FieldExpression1.action
conversion Validator
The conversion validator tells you if the type conversion for an action property generated a
conversion error. The validator also lets you add a custom message on top of the default
conversion error message. Here is the default message for a conversion error:
For example, the ConversionTestAction class in Listing 8.28 has one property, age,
which is an int. The validator configuration file in Listing 8.29 configures the conversion
validator for the age field and adds an error message for a failed conversion.
Listing 8.28. The ConversionTestAction class
package app08a;
import com.opensymphony.xwork2.ActionSupport;
public class ConversionTestAction extends ActionSupport {
private int age;
// getter and setter deleted
}
<validators>
<field name="age">
<field-validator type="conversion">
<message>
An age must be an integer.
</message>
</field-validator>
</field>
</validators>
The Conversion.jsp page in Listing 8.30 contains a form with a field mapped to the age
property.
You can test this example by directing your browser to this URL.
http://localhost:8080/app08a/Conversion1.action
Figure 8.10 shows the conversion validator in action. There are two error messages
displayed, the default one and the one that you added using the conversion validator.
visitor Validator
The visitor validator introduces some level of reusability, enabling you to use the same
validator configuration file with more than one action. Consider this scenario.
Suppose you have an action class (say, Customer) that has an address property of type
Address, which in turn has five properties (streetName, streetNumber, city, state, and
zipCode). To validate the zipCode property in an Address object that is a property of the
Customer action class, you would write this field element in a Customer-validation.xml
file.
<field name="address.zipCode">
<field-validator type="requiredstring">
<message>Zip Code must not be empty</message>
</field-validator>
</field>
This is redundant and the visitor validator can help you isolate identical validation rules into
a file. Every time you need to use the validation rules, you simply need to reference the file.
In this example, you would isolate the validation rules for the Address class into an
Address-validation.xml file. Then, in your Customer-validation.xml file you would write
<field name="address">
<field-validator type="visitor">
<message>Address: </message>
</field-validator>
</field>
This field element says, for the address property, use the validation file that comes with
the property type (Address). In other words, Struts would use the Address-
validation.xml file for validating the address property. If you use Address in multiple
action classes, you don't need to write the same validation rules in every validator
configuration file for each action.
Another feature of the visitor validator is the use of context. If one of the actions that use
Address needs other validation rules than the ones specified the Address-validation.xml
file, you can create a new validator configuration file just for that action. The new validator
configuration file would be named:
Address-context-validation.xml
Here, context is the alias of the action that needs specific validation rules for the Address
class. If the AddEmployee action needed special validation rules for its address property,
you would have this file:
Address-AddEmployee-validation.xml
That's not all. If the context name is different from the action alias, for example, if the
AddManager action also requires the validation rules in the Address-AddEmployee-
validaton.xml instead of the ones in Address-validation.xml, you can tell the visitor
validator to look at a different context by writing this field element.
<field name="address">
<field-validator type="visitor">
<param name="context">specific</param>
<message>Address: </message>
</field-validator>
</field>
This indicates to the visitor validator that to validate the address property, it should use
Address-specific-validation.xml and not Address-AddManager-validation.xml.
Now let's look at the three sample applications (app08b, app08c, and app08d) that
illustrate the use of the visitor validator. The app08b application shows a Customer action
that has an address property of type Address and uses a conventional way to validate
address. The app08c application features the same Customer and Address classes, but
use the visitor validator to validate the address property. The app08d application employs
the visitor validator and uses a different context.
In this example, a Customer class has an address property of type Address. It is shown
how you can validate a complex object with the help of OGNL expressions. The example is
Figure 8.11. The Customer
given in app08b and its directory structure is shown in
class and the Address class are shown in Listings 8.31 and 8.32, respectively.
To validate the Customer action class, use the Customer-validation.xml file in Listing
8.33. Note that you can specify the validators for the properties in the Address object
here.
<validators>
<field name="firstName">
<field-validator type="requiredstring">
<message>First Name must not be empty</message>
</field-validator>
</field>
<field name="lastName">
<field-validator type="requiredstring">
<message>Last Name must not be empty</message>
</field-validator>
</field>
<field name="address.streetName">
<field-validator type="requiredstring">
<message>Street Name must not be empty</message>
</field-validator>
</field>
<field name="address.streetNumber">
<field-validator type="requiredstring">
<message>Street Number must not be empty</message>
</field-validator>
</field>
<field name="address.city">
<field-validator type="requiredstring">
<message>City must not be empty</message>
</field-validator>
</field>
<field name="address.state">
<field-validator type="requiredstring">
<message>State must not be empty</message>
</field-validator>
</field>
<field name="address.zipCode">
<field-validator type="requiredstring">
<message>Zip Code must not be empty</message>
</field-validator>
</field>
</validators>
The Customer.jsp page in Listing 8.34 contains a form with fields that map to the
properties in the Customer action.
http://localhost:8080/app08b/Customer1.action
app08c, whose directory structure is shown in Figure 8.13, is similar to app08b. It has
Address and Customer classes and a Customer.jsp page that are identical to the ones in
app08b. However, the validation rules for the Address class have been moved to an
Admin-validation.xml file (See Listing 8.35).
Figure 8.13. app08c directory structure
<validators>
<field name="streetName">
<field-validator type="requiredstring">
<message>Street Name must not be empty</message>
</field-validator>
</field>
<field name="streetNumber">
<field-validator type="requiredstring">
<message>Street Number must not be empty</message>
</field-validator>
</field>
<field name="city">
<field-validator type="requiredstring">
<message>City must not be empty</message>
</field-validator>
</field>
<field name="state">
<field-validator type="requiredstring">
<message>State must not be empty</message>
</field-validator>
</field>
<field name="zipCode">
<field-validator type="requiredstring">
<message>Zip Code must not be empty</message>
</field-validator>
</field>
</validators>
The Customer-validation.xml file (shown in Listing 8.36) is now shorter, since the
validation rules for the address property are no longer here. Instead, it uses the visitor
validator to point to the Address-validation.xml file.
<validators>
<field name="firstName">
<field-validator type="requiredstring">
<message>First Name must not be empty</message>
</field-validator>
</field>
<field name="lastName">
<field-validator type="requiredstring">
<message>Last Name must not be empty</message>
</field-validator>
</field>
<field name="address">
<field-validator type="visitor">
<message>Address: </message>
</field-validator>
</field>
</validators>
http://localhost:8080/app08c/Customer1.action
app08d is similar to app08c and its directory structure is shown in Figure 8.14. Its
Address-validation.xml and Customer-validation.xml files are the same as the ones in
app08c.
Figure 8.14. app08d directory structure
In addition to the Customer class, there is an Employee class that has an address
property. There is a new validator configuration file for the Address class, Address-
specific-validation.xml, which is shown in Listing 8.37.
<validators>
<field name="zipCode">
<field-validator type="regex">
<param name="expression">
<![CDATA[\d\d\d\d\d]]>
</param>
<message>
Invalid zip code or invalid format
</message>
</field-validator>
</field>
</validators>
<validators>
<field name="firstName">
<field-validator type="requiredstring">
<message>First Name must not be empty</message>
</field-validator>
</field>
<field name="lastName">
<field-validator type="requiredstring">
<message>Last Name must not be empty</message>
</field-validator>
</field>
<field name="address">
<field-validator type="visitor">
<param name="context">specific</param>
<message>Address: </message>
</field-validator>
</field>
</validators>
http://localhost:8080/app08d/Employee1.action
The package names in Figure 8.15 have been omitted. The Validator, FieldValidator,
and ShortCircuitableValidator interfaces belong to the
com.opensymphony.xwork2.validator package. The rest are part of the
com.opensymphony.xwork2.validator.validators package. The Validator interface is
printed in Listing 8.39.
Listing 8.39. The Validator interface
package com.opensymphony.xwork2.validator;
public interface Validator {
void setDefaultMessage(String message);
String getDefaultMessage();
String getMessage(Object object);
void setMessageKey(String key);
String getMessageKey();
void setValidatorType(String type);
String getValidatorType();
The Validation interceptor is responsible for loading and executing validators. After it loads a
validator, the interceptor will call the validator's setValidatorContext method and pass the
current ValidatorContext, which will allow access to the current action. The interceptor will
then invoke the validate method, passing the object to be validated. The validate method
is the method you need to override when writing a custom validator.
The ValidatorSupport class adds several methods, of which three are convenience
methods you can call from your validation class.
From your validate method you call the addActionError when a plain validator fails or the
addFieldError when a field validator fails.
FieldValidatorSupport extends ValidatorSupport and adds two properties,
propertyType and fieldName.
Listing 8.40 shows the RequiredStringValidator class, the underlying class for the
requiredstring validator.
if (doTrim) {
s = s.trim();
}
if (s.length() == 0) {
addFieldError(fieldName, object);
}
}
}
}
The requiredstring validator can accept a trim parameter, therefore its underlying class
needs to have a trim property. The setter will be called by the validation interceptor if a
trim parameter is passed to the validator.
The validate method does the validation. If validation fails, this method must call the
addFieldError method.
Registration
As mentioned at the beginning of this chapter, bundled validators are already registered so
you don't need to register them before use. They are registered in the
com/opensymphony/xwork2/validator/validators/default.xml file (shown in
Listing 8.41), which is included in the xwork jar file. If you are using a custom or third
party validator, you need to register it in a validators.xml file deployed under WEB-
INF/classes or in the classpath.
Note
The Struts website maintains, at the time of writing, that if you have a validators.xml file
in your classpath, you must register all bundled validators in this file because Struts will not
load the default.xml file. My testing revealed otherwise. You can still use the bundled
validators without registering them in a validators.xml file.
<validators>
<validator name="required"
class="com.opensymphony.xwork2.validator.validators.
RequiredFieldValidator"/>
<validator name="requiredstring"
class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/
>
<validator name="int"
class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
<validator name="double"
class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator
"/>
<validator name="date"
class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/
>
<validator name="expression"
class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
<validator name="fieldexpression"
class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"
/>
<validator name="email"
class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
<validator name="url"
class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
<validator name="visitor"
class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
<validator name="conversion"
class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValid
ator"/>
<validator name="stringlength"
class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidato
r"/>
<validator name="regex"
class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
</validators>
Example
The following example teaches you how to write a custom validator and register it. This
example showcases a strongpassword validator that checks the strength of a password. A
password is considered strong if it contains at least one digit, one lowercase character, and
one uppercase character. In addition, the validator can accept a minLength parameter that
the user can pass to set the minimum length of an acceptable password.
<validators>
<validator name="strongpassword"
class="app08e.validator.StrongPasswordValidator"/>
</validators>
Now that you've registered your custom validator, you can use it the same way you would a
bundled validator. For example, the User class in Listing 8.44 has a password property
that can only be assigned a strong password. The User-validation.xml file in Listing
8.45 configures the validators for the User class.
<validators>
<field name="userName">
<field-validator type="requiredstring">
<message>User Name must not be empty</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>Password must not be empty</message>
</field-validator>
</field>
<field name="password">
<field-validator type="strongpassword">
<param name="minLength">8</param>
<message>
Password must be at least 8 characters long
and contains at least one lower case character,
one upper case character, and a digit.
</message>
</field-validator>
</field>
</validators>
Listing 8.46. The User.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Select user name and password</title>
<style type="text/css">@import url(css/main.css);</style>
<style>
.errorMessage {
color:red;
}
</style>
</head>
<body>
<div id="global" style="width:350px">
<h3>Please select your user name and password</h3>
<s:form action="User2">
<s:textfield name="userName" label="User Name"/>
<s:password name="password" label="Password"/>
<s:submit/>
</s:form>
</div>
</body>
</html>
The User.jsp page in Listing 8.47 features a form that accepts a user name and a
password. Direct your browser here to test this example.
http://localhost:8080/app08e/User1.action
void validate()
If an action class implements Validateable, Struts will call its validate method. You write
code that validates user input within this method. Since the ActionSupport class
implements this interface, you don't have to implement Validateable directly if your class
extends ActionSupport.
The app08f application demonstrates how to write programmatic validation rules. The User
action (See Listing 8.47) overrides the validate method and adds a field error if the
userName value entered by the user is already in the userNames list.
Listing 8.47. The User class
package app08f;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
public class User extends ActionSupport {
private String userName;
private String password;
private static List<String> userNames = new ArrayList<String>();
static {
userNames.add("harry");
userNames.add("sally");
}
// getters and setters not shown
public void validate() {
if (userNames.contains(userName)) {
addFieldError("userName",
"'" + userName + "' has been taken.");
}
}
}
Even when employing programmatic validation, you can still use the bundled validators. In
this example, the userName field is also "guarded" by a stringrequired validator, as shown
in the User-validation.xml file in Listing 8.48.
<validators>
<field name="userName">
<field-validator type="requiredstring">
<message>User Name must not be empty</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>Password must not be empty</message>
</field-validator>
</field>
</validators>
http://localhost:8080/app08f/User1.action
Summary
Input validation is one of the features Struts offer to expedite web application development.
In fact, Struts comes with built-in validators that are available for use in most cases. As
you've learned in this chapter, you can also write custom validators to cater for validations
not already covered by any of the bundled validators. In addition, you can perform
programmatic validation in more complex situations.
Chapter 9. Message Handling and
Internationalization
Message handling is an important task in application development. For example, it is almost
always mandatory that text and messages be editable without source recompile. In
addition, nowadays it is often a requirement that an application be able to "speak" many
languages. A technique for developing applications that support multiple languages and data
formats without having to rewrite programming logic is called internationalization.
Internationalization is abbreviated i18n because the word starts with an i and ends with an
n, and there are 18 characters between the first i and the last n. In addition, localization is
a technique for adapting an internationalized application to support a specific locale. A locale
is a specific geographical, political, or cultural region. An operation that takes a locale into
consideration is said to be locale-sensitive. For example, displaying a date is locale-
sensitive because the date must be in the format used by the country or region of the user.
The 15th day of November 2005 is written as 11/15/2005 in the US, but printed as
15/11/2005 in Australia. Localization is abbreviated l10n because the word starts with an l
and ends with an n and there are 10 letters between the l and the n.
With internationalization, you can change visual text in an application quickly and easily.
Java has built-in supports for internationalization and Struts makes use of this feature and
has been designed from the outset to support easy message handling and
internationalization. For instance, the com.opensymphony.xwork2.ActionSupport class,
which was introduced in Chapter 3, "Actions and Results," has getText methods for
reading messages from a text file and selecting messages in the correct language. A custom
tag can display a localized message simply by calling one of these methods.
This chapter explains how to use Struts' support for internationalization and localization.
Two tags, text and i18n, are also discussed.
Note
Even if you're developing a monolingual site, you should take advantage of the Struts
internationalization support for better message handling.
The variant argument is a vendor- or browser-specific code. For example, you use WIN for
Windows, MAC for Macintosh, and POSIX for POSIX. If there are two variants, separate
them with an underscore, and put the most important one first. For example, a Traditional
Spanish collation might construct a locale with parameters for the language, the country,
and the variant as es, ES, Traditional_WIN, respectively.
The language code is a valid ISO 639 language code. Table 9.1 displays some of the
country codes. The complete list can be found at
http://www.w3.org/WAI/ER/IG/ert/iso639.htm.
de German
el Greek
en English
es Spanish
fr French
hi Hindi
it Italian
ja Japanese
nl Dutch
pt Portuguese
ru Russian
zh Chinese
The country argument is also a valid ISO code, which is a two-letter, uppercase code
specified in ISO 3166. Table 9.2 lists some of the country codes in ISO 3166. The
complete list can be found at http://userpage.chemie.fu-
berlin.de/diverse/doc/ISO_3166.html or
http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-
code-lists/list-en1.html.
Australia AU
Brazil BR
Canada CA
China CN
Egypt EG
France FR
Germany DE
India IN
Mexico MX
Switzerland CH
Taiwan TW
United Kingdom GB
United States US
An internationalized application stores its textual elements in a separate properties file for
each locale. Each file contains key/value pairs, and each key uniquely identifies a locale-
specific object. Keys are always strings, and values can be strings or any other type of
object. For example, to support American English, German, and Chinese, you will have
three properties files, all with the same keys.
Here is the English version of the properties file. Note that it has two keys: greetings and
farewell:
greetings = Hello
farewell = Goodbye
greetings = Hallo
farewell = Tschüβ
And the properties file for the Chinese language would be this:
greetings=\u4f60\u597d
farewell=\u518d\u89c1
1. Using your favorite Chinese text editor, create a text file like this:
65
greetings=
farewell=
2. Convert the content of the text file into the Unicode representation.
Normally, a Chinese text editor has a feature for converting Chinese
characters into Unicode codes. You will get this end result.
65
greetings=\u4f60\u597d
farewell=\u518d\u89c1
This is the content of the properties file you use in your Java application.
Note
With Struts you don't need to know any more than writing properties files in multiple
languages. However, if interested, you may want to learn about the
java.util.ResourceBundle class and study how it selects and reads a properties file
specific to the user's locale.
basename_languageCode_countryCode
For example, if the base name is MyAction and you define three locales US-en, DE-de,
CN-zh, you would have these properties files:
• MyAction_en_US.properties
• MyAction_de_DE.properties
• MyAction_zh_CN.properties
Gets the message associated with the key and returns null if the message cannot be found.
Gets the message associated with the key and returns the specified default value if the
message cannot be found.
Gets the message associated with the key and formats it using the specified arguments in
accordance with the rules defined in java.text.MessageFormat.
Gets the message associated with the key and formats it using the specified arguments in
accordance with the rules defined in java.text.MessageFormat. If the message cannot be
found, this method returns the specified default value.
Gets the message associated with the key and formats it using the specified arguments in
accordance with the rules defined in java.text.MessageFormat. If the message cannot be
found, this method returns the specified default value.
When you call a getText method, it searches for the appropriate properties file in this
order.
1. The action class properties file, i.e. one whose basename is the same as the name of
the corresponding action class and located in the same directory as the action class.
For example, if the action class is app09a.Customer, the relevant file for the
default locale is Customer.properties in WEB-INF/classes/app09a.
2. The properties file for each interface that the action class implements. For example,
if the action class implements a Dummy interface, the default properties file that
corresponds to this interface is Dummy.properties.
3. The properties file for each of its parent class followed by each interface the parent
class implements. For instance, if the action class extends ActionSupport, the
ActionSupport.properties file will be used. If the message is not found, the search
moves up to the next parent in the hierarchy, up to java.lang.Object.
4. If the action class implements com.opensymphony.xwork2.ModelDriven, Struts
calls the getModel method and does a class hierarchy search for the class of the
model object. ModelDriven is explained in Chapter 10, "Model Driven and Prepare
Interceptors."
5. The default package properties file. If the action class is app09a.Customer, the
default package ResourceBundle is package in app09a.
6. The package resource bundle in the next parent package.
7. Global resources
You can display a localized message using the property tag or the label attribute of a form
tag by calling getText. The syntax for calling it is
%{getText('key')}
For example, to use a textfield tag to retrieve the message associated with key
customer.name, use this:
The following property tag prints a message associated with the key customer.contact.
<s:property value="%{getText('customer.contact')}"/>
The following sample application shows how to use the message handling feature in a
monolingual application. It is shown here how easy it is to change messages across the
application by simply editing properties files.
The application centers around the Customer action class, which implements an interface
named Dummy. This interface does not define any method and is used to demonstrate the
order of properties file search.
The Customer class is given in Listing 9.1 and the Customer.jsp page in Listing 9.2.
http://localhost:8080/app09a/Customer.action
You can experiment with the localized messages by editing the properties files.
var String The name of the variable that references the value to pushed to the
stack context.
For example, the following text tag prints the message associated with the key greetings:
<s:text name="greetings"/>
If the var attribute is present, however, the message is not printed but pushed to the Value
Stack's context map. For instance, the following pushes the message associated with
greetings to the context map and creates a variable named msg that references the
message.
You can then use the property tag to access the message.
You can pass parameters to a text tag. For example, if you have the following key in a
properties file
greetings=Hello {0}
<s:text name="greetings">
<s:param>Visitor</s:param>
</s:text>
Hello Visitor
A parameter can be a dynamic value too. For example, the following code passes the value
of the firstName property to the text tag.
<s:text name="greetings">
<s:param><s:property value="firstName"/></s:param>
</s:text>
The app09b application shows how to use the text tag in a multilingual site. Three
languages are supported: English (default), German, and Chinese.
Note that three properties files correspond to the Main class. The properties files are given
in Listings 9.3 to 9.5.
The Main class is shown in Listing 9.6 and the Main.jsp page in Listing 9.7.
<s:text name="greetings">
<s:param>Jon</s:param>
</s:text>.
<s:text name="farewell"/>
</div>
</body>
</html>
http://localhost:8080/app09b/Main.action
• You want to use a ListResourceBundle so that you can associate a key with a non-
String object.
• You wish to pre-process a key.
• The message comes from an unconventional source.
The tag falls back to the default resource bundle if the specified custom ResourceBundle
cannot be found.
The i18n tag has one attribute, name, which is described in Table 9.4.
For example, the app09c application features two custom ResourceBundles that extend
ListResourceBundle, MyCustomResourceBundle and MyCustomResourceBundle_de.
The custom ResourceBundles are shown in Listings 9.8 and 9.9, respectively. These
ResourceBundles return one of two message arrays. If the current time is before 12 am, it
will return the first array. Otherwise, the second array will be returned. Therefore, the user
will get a different message depending on the current time.
The Main.jsp page in Listing 9.10 uses an i18n tag to select a custom
ResourceBundle and employs two text tags to display the localized messages.
<s:i18n name="app09c.resourcebundle.MyCustomResourceBundle">
<s:text name="greetings">
<s:param>Jon</s:param>
</s:text>.
<s:text name="farewell"/>
</s:i18n>
</div>
</body>
</html>
You can test the application by directing your browser to this URL.
http://localhost:8080/app09c/Main.action
Manually Selecting A Resource Bundle
The ResourceBundle that gets picked up depends on the browser's locale. If you want to
let the user select one that is not browser-dependant, you can. You just need to pass a
request parameter request_locale. For example, the following request parameter indicates
to the server that the user wanted to be served in German language.
request_locale=de
As an example, the app09d application illustrates how you can create an application that
lets the user select a language. The actions in this application are declared in Listing
9.11.
The first action, Language, displays the Language.jsp page (shown in Listing 9.12)
that shows two links that let the user select a language.
Selecting the first link invokes the Main1 action and passes the request_locale=en
request parameter to the server. Selecting the second link invokes Main2 and passes
request_locale=de. The Main1.jsp and Main2.jsp pages, associated with actions Main1
and Main2, are shown in Listing 9.13 and 9.14, respectively.
http://localhost:8080/app09d/Language.action
Summary
Message handling is one of the most important tasks in application development. Today
applications also often require that applications be able to display internationalized and
localized messages. Struts has been designed with i18n and l10n in mind, and the tags in
the Struts tag library support internationalized message handling.
Chapter 10. Model Driven and Prepare
Interceptors
This chapter explains the Model Driven and Prepare interceptors, two very important
interceptors that help with separating the action and the model. It starts with a discussion
of why separating the action and the model is a good idea and continues with two sample
applications that illustrate the roles of the interceptors.
Struts resides mainly in the presentation tier and since you can write business logic in
Struts actions, you can argue that Struts encapsulates the logic tier too. In an enterprise
application, however, it is less often that you write business logic in action classes. Rather,
you will call methods in another tier from your action classes.
A Struts action has methods and properties and can definitely act as a transfer object.
However, is it really appropriate to send an action object to another tier?
The answer is no. An action class has methods that are useful only in the presentation tier.
What would an execute method that returns "success" do in an EJB container, for example?
Transferring an action object to another tier is not only awkward but could be dangerous
too.
Now, if you accept this, you'll acknowledge that there needs to be a clear separation
between the action and the model in an enterprise application that uses Struts as the front-
end. There will be action classes that don't represent model objects and whose functions are
limited to serve the presentation tier. The names of such action classes should end with
Action. Model classes, on the other hand, should have no suffix. An action class that
manages products should be called ProductAction whereas an instance of a Product class
should be used as a transfer object that encapsulates information about a product.
By now you've probably grown used to receiving the Struts service that maps form fields
with action properties. You'll probably ask, if you are to create a model that is not an
instance of the action class, how do you map form fields with the transfer object's
properties? The answer is by employing the Model Driven interceptor.
An action class that implements ModelDriven must override the getModel method. As an
example, the ProductAction class inListing 10.2 implements ModelDriven and its
getModel method returns an instance of the Product class (given in Listing 10.3).
When the user invokes the ProductAction action, the Model Driven interceptor will call its
getModel method on ProductAction and push the returned model (in this case, an
instance of Product) to the Value Stack. If the basic stack or the default stack has been
configured to kick in after the Model Driven interceptor, the Parameters interceptor will then
map form fields to the properties of the objects in the Value Stack. Since now the model
(the Product object) is at the top of the Value Stack, it will get populated. If a field does
not have a matching property in the model, the Param interceptor will try the next object in
the Value Stack. In this case, the ProductAction object will be used.
As an example, the app10a application shows how you can separate an action and a model.
This simple application manages employees and comes with two actions:
The action declarations for this application are given in Listing 10.4.
As you can see in Listing 10.4, both actions are handled by the EmployeeAction class.
The list method is used to handle the Employee_list action and the create method is for
creating a new employee.
The model used in this application is the Employee class in Listing 10.6.
public Employee() {
}
public Employee(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
Note that a model class must have a no-argument constructor. Since the Employee class
has a constructor that accepts three arguments, a no-argument constructor must be
explicitly defined. The Employee class itself is very simple with three properties, id,
firstName, and lastName.
Both the list and create methods in EmployeeAction rely on an EmployeeManager class
that hides the complexity of the business logic that manages employees. In a real-world
solution, EmployeeManager could be a business service that reads from and writes to a
database. In this application, EmployeeManager provides a simple repository of
Employee objects in a List.
Note
Chapter 11, "Persistence Layer" explains the Data Access Object design pattern for
data access.
You can run the application by directing your browser to this URL:
http://localhost:8080/app10a/Employee_list.action
If you click the Submit button, the create method in the action object will be invoked. A
validation file (named EmployeeAction-Employee_create-validation.xml) is used to
make sure that the first name and the last name are not empty. Listing 10.8 shows the
EmployeeAction-Employee_create-validation.xml file.
Listing 10.8. The EmployeeAction-Employee_create-validation.xml file
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="firstName">
<field-validator type="requiredstring">
<message>Please enter a first name</message>
</field-validator>
</field>
<field name="lastName">
<field-validator type="requiredstring">
<message>Please enter a last name</message>
</field-validator>
</field>
</validators>
Now, pay attention to the result elements for the Employee_create action in the
configuration file:
After a successful create, the user will be redirected to the Employee_list action. Why
didn't we do a forward that would have been faster?
/Employee_create.action
If we had used a forward, then the URI would have remained the same after the action and
result were executed. As a result, if the user clicked the browser's Refresh/Reload button,
the form (and its contents) would be submitted again and a new employee would be
created.
By redirecting, the URI after Employee_create will be the following, which will not cause
another create if the user (accidentally) reloads the page.
/Employee_list.action
The Preparable Interceptor
As you can see in the preceding section, the getModel method of a ModelDriven action
always returns a new object. However, as models are sometimes retrieved from a database,
you cannot simply return a new instance every time you override getModel. In the latter
case, the Preparable interceptor can help. This interceptor calls the prepare method of any
action object whose class implements the com.opensymphony.xwork2.Preparable
interface. This interface is shown in Listing 10.9.
• Employee_edit
• Employee_update
• Employee_delete
The declarations for the actions in app10b are given in Listing 10.10.
The EmployeeAction class, shown in Listing 10.11, handles all the actions in app10b.
Note that the prepare method in the EmployeeAction class will create a new Employee
object only if employeeId is 0. If an action invocation populates the employeeId property
of the action object, the prepare method will attempt to find an Employee object through
the EmployeeManager class.
This is why the Employee_edit action uses the paramsPrepareParamsStack stack that
calls the Params interceptor twice, as shown below:
<interceptor-stack name="paramsPrepareParamsStack">
...
<interceptor-ref name="params"/>
...
<interceptor-ref name="prepare"/>
<interceptor-ref name="model-driven"/>
...
<interceptor-ref name="params"/>
...
</interceptor-stack>
The first time the Parameters interceptor is invoked, it populates the employeeId property
on the EmployeeAction object, so that the prepare method knows how to retrieve the
Employee object to be edited. After the Prepare and Model Driven interceptors are invoked,
the Parameters interceptor is called again, this time giving it the opportunity to populate the
model.
The model class (Employee) for this application is exactly the same as the one in app10a
and will not be reprinted here. However, the EmployeeManager class has been modified
and is given in Listing 10.12.
Listing 10.12. The EmployeeManager class
package app10b;
import java.util.ArrayList;
import java.util.List;
}
}
You can invoke the application by using this URL:
http://localhost:8080/app08b/Employee_list.action
Figure 10.2 shows the list of employees. It's similar except that there are now Edit and
Delete links for each employee.
Without a mapping tool, you have other options in hand. These include the Data Access
Object (DAO) pattern, Java Data Objects (JDO), open source libraries such as Hibernate,
and so on. Of these, the DAO pattern in the easiest to learn and is sufficient in most
applications. This chapter shows you how to implement the DAO pattern for data
persistence.
Also note that because many parts of an application may need to persist objects, a good
design dictates that you create a dedicated layer for data persistence. This persistence layer
provides methods that can be called by any component that needs to persist objects. In
addition to simplifying your application architecture (because now object persistence is
handled by only one component), the persistence layer also hides the complexity of
accessing the relational database. The persistence layer is depicted in Figure 11.1.
The persistence layer provides public methods for storing, retrieving, and manipulating
value objects, and the client of the persistence layer does not have to know how the
persistence layer accomplishes this. All they care is their data is safe and retrievable.
There are many variants of the DAO pattern. You will learn the three most common
variants: from the most basic to the most flexible.
In this implementation, a client instantiates the DAO class directly and call its methods.
Figure 11.2 shows the ProductDAO class in this variant of the DAO pattern.
When a Struts action object needs to access product information, it instantiates the
ProductDAO class and calls its methods.
A typical Struts application has more than one DAO class. The instances of the DAO classes
need a uniform way of getting a connection object to access the data source. It is therefore
convenient to have a DAO interface that provides the getConnection method and a
DAOBase class that provides the implementation of the method. All the DAO classes then
extend the DAOBase class, as depicted in Figure 11.3.
Figure 11.3. DAO pattern with a DAO interface
Each method in the DAO class accesses the database by using an SQL statement.
Unfortunately, the SQL statement may vary depending on the database type. For example,
to insert a record into a table, Oracle databases support the notion of sequences to
generate sequential numbers for new records. Therefore, in Oracle, you would perform two
operations: generate a sequential number and insert a new record. MySQL, by contrast,
supports auto numbers that get generated when new records are inserted. In this case, an
insert method will depend on the database it is persisting data to. To allow your application
to support multiple databases, you can modify your DAO pattern implementation to employ
the Abstract Factory pattern. Figure 11.4 shows the CustomerDAO interface that
defines the methods that need to exist in a CustomerDAO object. A CustomerDAO
implementation will be tied to a database type. In Figure 11.4 two implementation
classes are available, CustomerDAOMySQLImpl and CustomerDAOOracleImpl, which
supports persisting objects to the MySQL database and the Oracle database, respectively.
Figure 11.4. DAO pattern with Abstract Factory pattern
In order to discuss the application thoroughly, I split the applications into subsections.
Note
To run the app11a application, you need to have a MySQL database installed on your
machine and run the MySQLScript.sql file included in the app11a application to create the
Customers table in the test database.
The DAO Interface and the DAOBase Class
DAO is an interface that all DAO classes must implement, either directly or indirectly. There
is only one method defined in the DAO interface, getConnection. The DAO interface is
given in Listing 11.1.
try {
Context context = new InitialContext();
DataSource dataSource = (DataSource)
context.lookup(dataSourceJndiName);
...
JNDI lookups are expensive operations, and, as such, obtaining a DataSource is resource
intensive. Therefore, you may want to cache this object and the ServletContext object will
be an ideal location to cache it. In app11a we use the application listener in Listing 11.3
to obtain a DataSource object and store it in the ServletContext object. Afterwards, in
the DAOBase class in Listing 11.2 you can obtain a DataSource by using this code:
ServletContext ServletContext = ServletActionContext.
getServletContext();
DataSource dataSource = (DataSource)
ServletContext.getAttribute("dataSource");
The Context element above facilitates the creation of a DataSource object from
which you can get java.sql.Connection objects from the pool. The specifics of the
DataSource object are given in the parameter elements of the
ResourceParams element. The username and password parameters specify
the user name and password used to access the database, the driverClassName
parameter specifies the JDBC driver, and the url parameter specifies the database
URL for accessing the MySQL database. The url parameter indicates that the
database server resides in the same machine as Tomcat (the use of localhost in
the URL) and the database the DataSource object references is the test database.
Also, for your DAO implementation, you may want to extend the java.lang.Exception class
to have your own DAO-specific exception. Methods in DAO objects can throw this specific
exception so that you can provide code that deals with data access and data manipulation
failures.
The app11a application uses one DAO class, EmployeeDAO. To support multiple
databases, EmployeeDAO is written as an interface that defines the methods for
EmployeeDAO objects. Listing 11.5 presents the EmployeeDAO interface.
The SQL statements for all the methods, except searchEmployees, are defined as static
final Strings because they will never change. Making them static final avoids creating the
same Strings again and again. Also, all those methods use a PreparedStatement instead
of a java.sql.Statement even though the PreparedStatement object is only executed
once. The use of PreparedStatement saves you from having to check if one of the
arguments contains a single quote. With a Statement, you must escape any single quote in
the argument.
The searchEmployees method, on the other hand, is based on a dynamic SQL statement.
This necessitates us to use a Statement object. Consequently, you must check for single
quotes in the arguments using the DbUtil class's fixSqlFieldValue method. Listing 11.7
presents the fixSqlFieldValue method.
Note
You could replace the fixSqlFieldValue method with the replaceAll method of the String
class like this.
However, this method is compute intensive because it uses regular expressions and should
be avoided in applications designed to be scalable.
The DAOFactory class helps the client instantiate a DAO class. Also, the necessity for a
DAOFactory class in the application stems from the fact that the implementation class
name is not known at design time, e.g. whether it is EmployeeDAOMySQLImpl or
EmployeeDAOOracleImpl. As such, the DAOFactory class hides the complexity of
creating a DAO object.
You can use the DAOFactory if you know the implementation classes for all your DAOs
when the application is written. This means, if you are thinking of only supporting two
databases, MySQL and Oracle, you know beforehand the type for the EmployeeDAO class
is either EmployeeDAOMySQLImpl or EmployeeDAOOracleImpl. If in the future your
application needs to support Microsoft SQL Server, you must rewrite the DAOFactory class,
i.e. add another if statement in the getCustomerDAO class.
You can add support of more databases without recompiling the DAOFactory class if you
use reflection to create the DAO object. Instead of the dbType parameter in your web.xml
file, you'd have employeeDAOType. Then, you would have the following code in your
DAOFactory class's getCustomerDAO method.
The EmployeeManager class (shown in Listing 11.9) is the client of the DAO classes.
This class provides another layer between the Struts actions and the DAO classes.
The app11a application provides the action classes for creating a new employee, updating
and deleting an existing employee, and searching for employees. The main entry point is
the Employee_list action. To invoke this action, use the following URL.
http://locahost:8080/app11a/Employee_list.action
You will see something similar to Figure 11.5.
When you run this application for the first time, you will not see the list of existing
employees.
Hibernate
Hibernate has gained popularity in the past few years as an add-on for Java EE and other
applications. Its web site (www.hibernate.org) advertises this free product as "a
powerful, ultra-high performance object/relational persistence and query service for Java."
Using Hibernate, you do not need to implement your own persistence layer. Instead, you
use a tool to create databases and related tables and determine how your objects should be
persisted. Hibernate virtually supports all kinds of database servers in the market today,
and its Hibernate Query Language provides "an elegant bridge between the object and
relational worlds".
More people will be using Hibernate in the near future. If you have time, invest in it.
Summary
Most applications need a persistence layer for persisting value objects. The persistence layer
hides the complexity of accessing the database from its clients, notably the action objects.
The persistence layer can be implemented as entity beans, the DAO pattern, by using
Hibernate, etc.
This chapter shows you in detail how to implement the DAO pattern. There are many
variants of this pattern and which one you choose depends on the project specification. The
most flexible DAO pattern is preferable because you can extend your application easily
should it need to change in the future.
Chapter 12. File Upload
HTTP file upload is specified in Request For Comments (RFC) 1867. Struts' File Upload
interceptor supports HTTP file upload by seamlessly incorporating the Jakarta Commons
FileUpload library that contains a multipart parser. This chapter discusses file upload in
general and how you can do single and multiple file uploads in Struts.
To enable the user to select a file you must have an <input type="file"> field. Here is an
example of a form used for selecting a file. In addition to a file field, the form also contains
a text box named description and a submit button.
Figure 12.1 shows how the file input field is rendered as a text box and a Browse
button.
Without Struts or the Java Commons FileUpload library, you would have to call the
getInputStream method on HttpServletRequest and parse the resulting InputStream
object to retrieve the uploaded file. This is a tedious and error-prone task. Luckily, Struts
makes it very easy to retrieve uploaded files.
File Upload in Struts
In Struts, the File Upload interceptor and the Jakarta Commons FileUpload library help parse
uploaded files. Basically, there are only two things you need to do.
First, use the file tag in a form on your JSP. Give it a descriptive name such as attachment
or upload. For multiple file upload, use multiple file tags and give them the same name. For
instance, the following form contains three file tags named attachment.
<s:form action="File_multipleUpload"
enctype="multipart/form-data">
<s:file name="attachment" label="Attachment 1"/>
<s:file name="attachment" label="Attachment 2"/>
<s:file name="attachment" label="Attachment 3"/>
<s:submit />
</s:form>
A file tag will be rendered as the following input element in the browser:
Second, create an action class with three properties. The properties must be named
according to these patterns:
• [inputName] File
• [inputName]FileName
• [inputName]ContentType
Here [inputName] is the name of the file tag(s) on the JSP. For example, if the file tag's
name is attachment, you will have these properties in your action class:
• attachmentFile
• attachmentFileName
• attachmentContentType
For single file upload, the type of [inputName] File is java.io.File and references the
uploaded file. The second and third properties are String and refer to the uploaded file name
and the content type, respectively.
For multiple file upload, you can either use arrays or java.util.Lists. For instance, the
following properties are arrays of Files and Strings.
If you decide to use Lists, you must assign an empty list to each of the properties:
private List<File> attachmentFile = new ArrayList<File>();
private List<String> attachmentFileName = new ArrayList<String>();
private List<String> attachmentContentType =
new ArrayList<String>();
You can access these properties from your action method. Normally, you would want to
save the uploaded file into a folder or a database and you would iterate over the File array,
if an array is being used:
ServletContext servletContext =
ServletActionContext.getServletContext();
String dataDir = servletContext.getRealPath("/WEB-INF");
for (int i=0; i < attachment.length; i++) {
File savedFile = new File(dataDir, attachmentFileName[i]);
attachment[i].renameTo(savedFile);
}
Since you often need to access both the uploaded file and the file name at each iteration,
using arrays is easier because an array lets you iterate over its elements by index. On the
other hand, iterating over a list would be more difficult.
This interceptor is responsible for file upload and is included in the default stack. Even if you
know nothing about this interceptor, you can still manage uploaded files easily. However,
understanding how this interceptor works allows you to make full use of the file upload
feature in Struts.
There are two important properties that you may want to set on the interceptor. You can
limit the size of the uploaded file as well as determine the allowable content type by setting
the following properties of the File Upload interceptor.
• maximumSize. The maximum size (in bytes) of the uploaded file. The default is
about 2MB.
• allowedTypes. A comma-separated list of allowable content types.
For example, the following action imposes a size limit and the type of the uploaded file. Only
files up to 1,000,000 bytes in size and JPEG, GIF, and PNG files can be uploaded.
There are two actions in this application, one for displaying a file upload form and one for
receiving the uploaded file. The action declarations are printed in Listing 12.1.
Listing 12.1. The struts.xml file
<package name="app12a" extends="struts-default">
<action name="File">
<result>/jsp/SingleUpload.jsp</result>
</action>
<action name="File_singleUpload"
class="app12a.SingleFileUploadAction" method="upload">
<interceptor-ref name="fileUpload">
<param name="maximumSize">100000</param>
<param name="allowedTypes">
image/gif,image/jpeg,image/png
</param>
</interceptor-ref>
<interceptor-ref name="basicStack"/>
<result name="input">/jsp/SingleUpload.jsp</result>
<result>/jsp/SingleUpload.jsp</result>
</action>
</package>
The SingleUpload.jsp page (shown in Listing 12.2) contains a form with a file tag.
When the user submits the form, the File_singleUpload action will be invoked. The
SingleFileUploadAction class in Listing 12.3 handles this action.
Listing 12.3. The SingleFileUploadAction class
package app12a;
import java.io.File;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
The app12a application also overrides the custom error messages by providing a new
struts-messages.properties file in Listing 12.4.
http://localhost:8080/app12a/File.action
You'll see the upload form like the one in Figure 12.3.
Figure 12.3. Single file upload
When the file upload form is submitted, the File_multipleUpload action is invoked. This
action is handled by the MultipleFileUploadAction class in Listing 12.7. Note that
arrays are used for the uploaded files, file names, and content types.
You can start uploading multiple files by directing your browser here.
http://localhost:8080/app12b/File.action
You can also use Lists instead of arrays. The MultipleFileUploadAction2 class in Listing
12.8 shows how to use Lists. Note that you must instantiate a List implementation for the
List variables.
Listing 12.8. Using Lists
package app12a;
import com.opensymphony.xwork2.ActionSupport;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MultipleFileUploadAction2 extends ActionSupport {
private List<File> attachment =
new ArrayList<File>();
private List<String> attachmentFileName =
new ArrayList<String>();
private List<String> attachmentContentType =
new ArrayList<String>();
Using arrays are better than Lists because with arrays you can iterate over the uploaded
files over by index.
Summary
This chapter discussed file upload. Struts supports file upload through the File Upload
interceptor that incorporates the Jakarta Commons FileUpload library. Two examples that
illustrated single file upload and multiple file upload were presented in this chapter
Chapter 13. File Download
This chapter discusses file download, an important topic that does not often get enough
attention in web programming books, and how Struts supports programmatic file download
by providing the Stream result type. Two examples illustrate the use of the stream result
type.
Downloading files is a day-to-day activity for an Internet surfer. Writing a web application
that allows only authorized users to download certain files is a different story. A solution
would be to use the operating system's or the web container's authentication system. This
authentication mechanism lets you password-protect files so that file downloading is allowed
only after the user has entered the correct user name and password. However, if you have
more than one user, the password must be shared, greatly reducing the effectiveness of the
password. The more people know the password, the less secure it is. Furthermore, when
many users use the same password, it is almost impossible to record who downloads what.
In other applications, you may want to dynamically send a file when the name or location of
the file is not known at design time. For instance, in a product search form, you display the
products found as the result of the search. Each product has a thumbnail image. Since you
do not know at design time which product will be searched for, you do not know which
image files to send to the browser.
In another scenario, you have a large and expensive image that should only be displayed on
your web pages. How do you prevent other web sites from cross referencing it? You can by
checking the referer header of each request for this image before allowing the image to be
downloaded and only allowing access if the referer header contains your domain name.
Programmable file download can help solve all the problems detailed above. In short,
programmable file download lets you select a file to send to the browser.
Note
To protect a file so that someone who knows its URL cannot download it, you must store the
file outside the application directory or under WEB-INF or in external storage such as a
database.
1. Set the response's content type to the file's content type. The Content-Type header
specifies the type of the data in the body of an entity and consists of the media type
and subtype identifiers. Visit http://www.iana.org/assignments/media-types to find
all standard content types. If you do not know what the content type is or want the
browser to always display the File Download dialog, set it to Application/Octet-
stream. This value is case insensitive.
2. Add an HTTP response header named Content-Disposition and give it the value
attachment; filename=theFileName, where theFileName is the default name for the
file that appears in the File Download dialog box. This is normally the same name as
the file, but does not have to be so.
For instance, this code sends a file to the browser.
First, you read the file as a FileInputStream and load the content to a byte array. Then, you
obtain the HttpServletResponse object's OutputStream and call its write method, passing
the byte array.
inputName String inputStream The name of the action class property that
returns the InputStream object to be flushed
to the browser.
bufferSize int 1024 The buffer size used when reading the
InputStream and the OutputStream used for
flushing data to the browser.
Whether the browser will show a file content or display a File Download dialog depends on
the value you set the Content-Type header. Setting it to "text/css" tells the browser that
the file is a CSS file and should be displayed. Assigning "application/octet-stream" tells the
browser that the user should be given the chance to save the file. Listing 13.1 shows the
action declarations in app13a. The Menu action displays the Menu.jsp page from which
the user can select whether to view or download a CSS file.
Note that the main difference between ViewCss and DownloadCss lies in the value of the
contentType parameter. Both use the FileDownloadAction class in Listing 13.2. This
class implements ServletContextAware because it needs access to the ServletContext
object and use its getResourceAsStream method. Using this method is an easy way to
return a resource as a java.io.InputStream.
Listing 13.2. The FileDownloadAction class
package app13a;
import java.io.InputStream;
import javax.servlet.ServletContext;
import org.apache.struts2.util.ServletContextAware;
import com.opensymphony.xwork2.ActionSupport;
The FileDownloadAction class has a filePath property that indicates the file path of the
requested resource. You must set this property from your JSP, the Menu.jsp page shown in
Listing 13.3.
<br/>
<s:url id="url2" action="DownloadCss">
<s:param name="filePath">css/main.css</s:param>
</s:url>
<s:a href="%{url2}">Download CSS</s:a>
</div>
</body>
</html>
The Main.jsp page employs two url tags with different parameters. The URLs are then used
by the a tags on the page.
http://localhost:8080/app13a/Menu.action
You'll see two links as shown in Figure 13.1. If you click the first link, the content of the
main.css file will be displayed. If you click the second link, the File Download dialog of your
browser will open and you can save the file.
The Product.jsp page in Listing 13.7 is used to display the product list.
<h3>Products</h3>
<table>
<tr>
<th>Name</th>
<th>Picture</th>
</tr>
<s:iterator value="products" id="product">
<tr>
<td><s:property value="#product.name"/></td>
<td>
<s:url id="url" action="GetImage">
<s:param name="productId">
<s:property value="#product.id"/>
</s:param>
</s:url>
<img src="<s:property value='#url'/>"
width="100" height="50"/>
</td>
</tr>
</s:iterator>
</table>
</div>
</body>
</html>
A product may have an image stored in the images directory of the application. A product
image is named according to the product identifier in a web-friendly format (one of jpeg,
gif, or png). For product identifier 3, the image name would be 3.gif or 3.jpg or 3.png.
Because the image file name is not stored, you have to find a way to display the image.
The GetImage action flushes an image to the browser. Note that in the Product.jsp page
the iterator tag contains an img element whose source is a URL that references to the
GetImage action and passes a productId parameter.
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Result;
You can test this application by directing your browser to this URL.
http://localhost:8080/app13b/DisplayProducts.action
Summary
In this chapter you have learned how file download work in web applications. You have also
learned how to select a file and sent it to the browser.
Chapter 14. Securing Struts Applications
Security is one of the most critical issues in web application development. As for servlet
applications, there are two ways to secure application resources, by configuring the
application and by writing Java code. The former is more popular because of its flexibility.
By editing your deployment descriptor (web.xml file), you can change your security policy
without rewriting code. For instance, you can restrict access to certain roles and HTTP
methods, determine how users can authenticate themselves, and so forth. Since Struts is
based on the Servlet technology, securing a Struts application will center on this
configuration plus the security feature in Struts itself.
To be good at security configuration, you need to be familiar with the concepts of principal
and roles, therefore this chapter starts with a discussion of both. Afterwards, the chapter
explains how to write a security policy and deals with authentication methods. After a
section on how to hide resources and another on Struts-specific security features, this
chapter concludes with the second way of security servlet applications: by writing code.
Every servlet container provides you with a different mechanism of managing users and
roles. You should consult the documentation that accompanies the servlet container on this.
In Tomcat you manage principal and roles in the tomcat-users.xml file under the conf
directory of the deployment directory. Here is an example of the tomcat-users-xml file.
<tomcat-users>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="vera" password="arev" roles="manager"/>
<user username="chuck" password="chuck" roles="admin"/>
<user username="dave" password="secret" roles="manager,admin"/>
</tomcat-users>
The file says that there are two roles (admin and manager) and three users (vera, chuck,
and dave). You can add as many roles and users as you want to the tomcat-users.xml file.
• Protecting resources
• Determining the login method for user authentication.
These tasks are discussed in the following subsections.
Protecting Resources
You enforce the security policy by using the security-constraint element in the
deployment descriptor. Here is the description of this element.
This means that the security-constraint element can have an optional display-name
subelement, one or many web-resource-collection subelements, an optional auth-
constraint subelement, and an optional user-data-constraint subelement.
You specify the set of web resources that you want to protect in the web-resource-
collection element, and you use the auth-constraint element to define the user roles
allowed to access them. The subelements are described further below.
The security-constraint element will cause the web container to block any request that
match the pattern /manager/*.do that does not come from a user belonging to the
manager role. Because no http-method element is used, the web container will attempt to
block all requests regardless the HTTP method being used to access the resource.
In addition, you should also register all roles used to access the restricted resources by
using the security-role element. Inside a security-role element, you write a role-name
element for each role. For example, the following security-role element defines two roles,
admin and manager.
<security-role>
<role-name>admin</role-name>
<role-name>manager</role-name>
</security-role>
After you specify which resources are restricted and which roles may access them, you must
specify how a user can login to prove that he or she is in the allowed role(s). You specify
the login method by using the login-config element. Here is the description of the login-
config element.
The auth-method element specifies the method for authenticating users. Its possible
values are BASIC, DIGEST, FORM, or CLIENT-CERT. The next section,
"Authentication Methods," explains more about these methods.
The realm-name element specifies a descriptive name that will be displayed in the
standard Login dialog when using the BASIC authentication method.
The form-login-config element is used when the value of <auth-method> is FORM. It
specifies the login page to be used and the error page to be displayed if authentication
failed.
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>User Basic Authentication</realm-name>
</login-config>
Authentication Methods
There are several authentication methods: basic, form-based, digest, Secure Socket Layer
(SSL), and client certificate authentication. With the basic authentication, the web container
asks the browser to display the standard Login dialog box which contains two fields: the
user name and the password. The standard Login dialog box will look different in different
browsers. In Internet Explorer, it looks like the one in Figure 14.1
If the user enters the correct user name and password, the server will display the requested
resource. Otherwise, the Login dialog box will be redisplayed, asking the user to try again.
The server will let the user try to log in three times, after which an error message is sent.
The drawback of this method is that the user name and password are transmitted to the
server using base64 encoding, which is a very weak encryption scheme. However, you can
use SSL to encrypt the user's credential.
Form-based authentication is similar to Basic authentication. However, you specify a login
page yourself. This gives you a chance to customize the look and feel of your login dialog.
This authentication method will also display a custom Error page written by the developer
on a failed attempt to login. Again, you can use SSL to encrypt users' credentials.
Digest authentication works like Basic authentication; however, the login information is not
transmitted. Instead, the hash of the passwords is sent. This protects the information from
malicious sniffers.
Basic and digest authentication methods are specified in RFC 2617, which you can find at
ftp://ftp.isi.edu/in-notes/rfc2617.txt. More information about SSL can be found at
http://home.netscape.com/eng/ssl3/3-SPEC.HTM.
The following subsections provide examples of the basic and form-based authentication
methods.
Note
There are two possible error messages with regard to authentication, 401 and 403. The user
will get a 401 if he or she cannot supply the correct user name and password of any user. A
user is normally given three chances, but this is browser specific. The user will get a 403 if
he or she can enter the correct user name and password of a user but the user is not in the
allowed role list.
The app14a application presents an example of how to use basic authentication. There are
two actions defined, User_input and User, as shown in Listing 14.1.
What is special is the way these resources are protected using configuration in the web.xml
file, which is shown in Listing 14.2.
Listing 14.2. The deployment descriptor (web.xml file)
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>struts2</filter-name>
<filter-
class>org.apache.struts2.dispatcher.FilterDispatcher</filter-
class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/User_input.action</url-pattern>
<url-pattern>/User.action</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>User Basic Authentication</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<error-page>
<error-code>403</error-code>
<location>/403.html</location>
</error-page>
</web-app>
Pay attention to the sections in bold. Practically, the URLs for invoking the two actions are
protected. Using Tomcat with the following tomcat-users.xml file, you know that the
actions can be accessed by Chuck and Dave, but not by Vera.
Only users in the admin role can access it. Use this URL to test it:
http://localhost:8080/app14a/User_input.action
The first time you try to access this resource, you'll see a Basic authentication page that
prompts you to enter the user name and password. If you do not enter the user name and
password of a user in the admin role, you'll get a 403 error. The error-page section in the
web.xml file tells the servlet container to display the 403.html file upon a 403 error
occurring. Without the error-page declaration, you'll get a standard servlet container error
page, as shown in Figure 14.2.
http://localhost:8080/app14a/displayAddOrderForm.do
The app14b application is similar to app14a, except that app14b uses form-based
authentication. Listing 14.3 shows the web.xml file.
<filter>
<filter-name>struts2</filter-name>
<filter-
class>org.apache.struts2.dispatcher.FilterDispatcher</filter-
class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/User_input.action</url-pattern>
<url-pattern>/User.action</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/loginError.html</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<error-page>
<error-code>403</error-code>
<location>/403.html</location>
</error-page>
</web-app>
For the login form, the user name field must be j_usemame, the password field must be
j_password, and the form's action must be j_security_check. Listing 14.4 presents
the login form used in app14b.
Listing 14.4. The login page in app14b
<html>
<title>Authentication Form</title>
</head>
<body>
<form method="post" action="j_security_check">
<table>
<tr>
<td colspan="2">Login:</td>
</tr>
<tr>
<td>User Name:</td>
<td><input type="text" name="j_username"/></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="j_password"/></td>
</tr>
<tr>
<td><input type="submit"/></td>
<td><input type="reset"/></td>
</tr>
</table>
</form>
</body>
</html>
You can test the app14b application using the following URL:
http://localhost:8080/app14b/User_input.action
Like the app14a appHcation, Chuck and Dave can access the restricted resources but Vera
cannot.
The first time you request the action, you'll see the login page in Figure 14.4.
Figure 14.4. The Login page
There are two error pages provided in app14b. The loginError.html, declared in the
web.xml file, is shown if the user cannot enter the correct user name and password. The
403.html file is shown if the user can produce a correct user name and password but the
user is not on the allowed role list
Hiding Resources
An observant reader would notice that all access should go through the Struts action servlet
and JSPs should not be accessible directly. Protecting JSPs from direct access can be easily
achieved in several ways.
1. By placing the resources, i.e. JSPs, under WEB-INF, which makes the JSPs not
accessible by typing their URLs. This way, the JSPs can only be displayed if they are
a forward destination from the action servlet. However, you have also noticed that
throughout this book all JSPs are not in the WEB-INF directory. This is because
some containers (such as WebLogic) will not be able to forward control to a JSP
under WEB-INF. Storing JSPs in WEB-INF may also change how other resources,
such as image and JavaScript files, can be referenced from the JSPs.
2. By using a filter to protect the JSPs outside the WEB-INF directory. It is easy to
implement such a filter. All you need to do is apply the filter so that it will redirect
access to a user page if the URL ends with .jsp. However, this is not as easy as the
trick explained in Step 3.
3. By using the security-constraint element in the web.xml file to protect all JSPs
but without providing a legitimate user role to access them. For example, in both
app14a and app14b, you have two security-constraint elements in the web.xml
files. One to prevent all JSPs from being accessed directly, another to protect
actions.
<security-constraint>
<web-resource-collection>
<web-resource-name>
Direct Access to JSPs
</web-resource-name>
<url-pattern>*.jsp</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>none</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/User_input.action</url-pattern>
<url-pattern>/User.action</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
All URLs ending with .jsp can only be accessed by users in the none role. If you do not have
a user in this role, no one can access the JSPs directly.
• allowedRoles. A list of roles that are allowed to access the corresponding action.
Roles can be comma-delimited.
• disallowedRoles. A list of roles that are not allowed to access the corresponding
action. Roles can be comma-delimited.
The app14c application provides an example of using the roles attribute. To be specific,
you use the deployment descriptor in Listing 14.5, in which you restrict access to all
URLs ending with .action, in effect restricting access to all Struts actions.
Listing 14.5. The deployment descriptor
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>struts2</filter-name>
<filter-
class>org.apache.struts2.dispatcher.FilterDispatcher</filter-
class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>*.action</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>User Basic Authentication</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
<role-name>manager</role-name>
</security-role>
</web-app>
You also specify that two roles may access the application, admin and manager.
Now, you have the following actions in the app14c application: User_input and User. You
want both to be accessible by all managers and admins. The elements shown in Listing
14.6 shows you how to declare the actions and interceptors in both actions.
http://localhost:8080/app14c/User_input.action
Programmatic Security
Even though configuring the deployment descriptor and specifying roles in the tomcat-
users.xml file means that you do not need to write Java code, sometimes coding is
inevitable. For example, you might want to record all the users that logged in. The
javax.servlet.http.HttpServletRequest interface provides several methods that enable
you to have access to portions of the user's login information. These methods are
getAuthType, isUserInRole, getPrincipal, and getRemoteUser. The methods are
explained in the following subsections.
This method returns the name of the authentication scheme used to protect the servlet. The
return value is one of the following values: BASIC_AUTH, FORM_AUTH,
CLIENT_CERT_AUTH, and DIGEST_AUTH. It returns null if the request was not
authenticated.
The isUserInRole Method
This method indicates whether the authenticated user is included in the specified role. If the
user has not been authenticated, the method returns false.
This method returns a java.security.Principal object containing the name of the current
authenticated user. If the user has not been authenticated, the method returns null.
This method returns the name of the user making this request, if the user has been
authenticated. Otherwise, it returns null. Whether the user name is sent with each
subsequent request depends on the browser and type of authentication.
Summary
In this chapter, you have learned how to configure the deployment descriptor to restrict
access to some or all of the resources in your servlet applications. The configuration means
that you need only to modify your deployment descriptor file—no programming is
necessary. In addition, you have also learned how to use the roles attribute in the action
elements in your Struts configuration file.
Writing Java code to secure Web applications is also possible through the following methods
of the javax.servlet.http.HttpServletRequest interface: getRemoteUser, getPrincipal,
getAuthType, and isUserInRole.
Chapter 15. Preventing Double Submits
Double form submits normally happen by accident or by the user's not knowing what to do
when it is taking a long time to process a form. Some double submits have fatal
consequences, some simply unpleasant. For instance, when submitting an online payment in
which a credit card will be charged, the user may click the submit button for the second
time if the server's response time is too slow. This may result in his/her credit card being
charged twice. Other less critical examples include forms that add a new product and doubly
submitting these forms will cause a product to be added twice.
Browsers' behaviors are different with regard to preventing double submits. Mozilla Firefox
will not respond to subsequent clicks on the same button, providing you with some kind of
protection. Other browsers, including Internet Explorer, do not yet implement the feature to
prevent double submits. In addition, in Mozilla and non-Mozilla browsers, if the user presses
the browser Refresh/Reload button after the request is processed, the same request will be
submitted again, effectively causing double submits. As such, you should always take action
if double submits may cause inadvertent consequences in your business logic.
Struts has built-in support for preventing double submits. It employs a technique that you
can also find in other web application development technologies. This technique involves
storing a unique token in the server and inserting a copy of the token into a form. When the
form is submitted, this token is also sent to the server. The server application will compare
the token with its own copy for the current user. If they match, form submission is
considered valid and the token is reset. Subsequent (accidental) submits of the same form
will fail because the token on the server have been reset.
This chapter explains how to use Struts' built-in feature for preventing double submits.
Managing Tokens
Struts provides the token tag that generates a unique token. This tag, which must be
enclosed in a form tag, inserts a hidden field into the form and stores the token in the
HttpSession object. If you use the debug tag on the same page as the form, you'll see a
session attribute session.token with a 32 character value.
The use of token must be accompanied by one of two interceptors, Token and Token
Session, that are capable of handling tokens. The Token interceptor, upon a double submit,
returns the result "invalid.token" and adds an action error. The default message for this
error is
The form has already been processed or no token was supplied, please
try again.
This is confusing for most users. Should they try again by resubmitting the form? Hasn't the
form been processed?
To override the message, you can create a validation file and add a value for the key
struts.messages.invalid.token. The supporting class for the Token interceptor is
org.apache.struts2.interceptor.TokenInterceptor. Therefore, to override the message, you
must place your key/value pair in a TokenInterceptor.properties file and place it under this
directory:
/WEB-INF/classes/org/apache/struts2/interceptor
The Token Session interceptor extends the Token interceptor and provides a more
sophisticated service. Instead of returning a special result and adding an action error, it
simply blocks subsequent submits. As a result, the user will see the same response as if
there were only one submit.
There are two actions in the application, Pay_input and Pay. The declarations for these
actions are shown in Listing 15.1. Pay_input displays the Payment.jsp page, which
contains a form to take payment details. Submitting the form invokes the Pay action. The
Pay action is protected by the Token interceptor.
Listing 15.1. The action declarations
<package name="app15a" extends="struts-default">
<action name="Pay_input">
<result>/jsp/Payment.jsp</result>
</action>
<action name="Pay" class="app15a.Payment">
<interceptor-ref name="token"/>
<interceptor-ref name="basicStack"/>
<result name="invalid.token">/jsp/Error.jsp</result>
<result name="input">/jsp/Payment.jsp</result>
<result>/jsp/Thanks.jsp</result>
</action>
</package>
The Pay action provides three results. The invalid.token result, executed if a token is
invalid, forwards to the Error.jsp page. The input result, which will be executed if input
validation failed, forwards to the Payment.jsp page. Finally, the success result forwards to
the Thanks.jsp page.
import com.opensymphony.xwork2.ActionSupport;
public class Payment extends ActionSupport {
private double amount;
private int creditCardType;
private String nameOnCard;
private String number;
private String expiryDate;
The Pay action uses the Payment class in Listing 15.2 as its action class. The class
simulates a long processing task that will take four seconds, giving you a chance to double
submit the form.
http://localhost:8080/app15a/Pay_input.action
Listing 15.7 shows the action declarations. Instead of the Token interceptor for the Pay
action, we use the Token Session interceptor. The JSPs are the same as those in app15a
and will not be reprinted here.
Summary
Double form submits normally happen by accident or by the user's not knowing what to do
when it is taking a long time to process a form. The technique to prevent a form from being
submitted twice is by employing a token which is reset at the first submit of a form. Struts
has built-in support for handling this token, through the token tag and the Token and
Token Session interceptors.
Chapter 16. Debugging and Profiling
This chapter discusses two related topics that can help you debug your application,
debugging and profiling. Debugging is made easy by the introduction of the debug tag in
the Struts tag library and the Debugging interceptor. Profiling lets you profile your
application courtesy of the Profiling interceptor.
This chapter starts with the debug tag and proceeds with the Debugging interceptor. It
then concludes with profiling.
<s:debug/>
This tag has one attribute, id, but you hardly need to use it.
You can direct your browser to this URL to test the debug tag.
http://localhost:8080/app16a/Debug.action
The page in Figure 16.1 shows how the tag is initially rendered.
Figure 16.1. The Debug tag
If you click the [Debug] link, you'll see the stack objects and the objects in the context
map, as shown in Figure 16.2.
Figure 16.2. Useful information for debugging
You can use the debug tag to see the values of action properties and the contents of
objects such as the session and application maps. This will help you pinpoint any error in
your application quickly.
Appending debug=xml will result in an XML that contains the values of the Value Stack and
other objects, such as the following:
<debug>
<parameters/>
<context>
<attr/>
<report.conversion.errors>false</report.conversion.errors>
<struts.actionMapping>
<class>class
org.apache.struts2.dispatcher.mapper.ActionMapping</class>
<name>DebuggingTest</name>
<namespace>/</namespace>
</struts.actionMapping>
</context>
<request/>
<session/>
<valueStack>
<value>
<actionErrors/>
<actionMessages/>
<amount>0.0</amount>
<class>class app16a.Profiling</class>
<errorMessages/>
<errors/>
<fieldErrors/>
<locale>
<ISO3Country>USA</ISO3Country>
<ISO3Language>eng</ISO3Language>
<class>class java.util.Locale</class>
<country>US</country>
<displayCountry>United States</displayCountry>
<displayLanguage>English</displayLanguage>
<displayName>English (United States)</displayName>
<displayVariant></displayVariant>
<language>en</language>
<variant></variant>
</locale>
<transactionType>0</transactionType>
</value>
<value>
<class>class
com.opensymphony.xwork2.DefaultTextProvider</class>
</value>
</valueStack>
</debug>
Using debug=console displays a console like the one shown in Figure 16.3. You can
enter an OGNL expression to the bottom of the page and the value will be displayed.
Figure 16.3. The OGNL console
Note
When I tested this feature, it did not work with Internet Explorer but worked perfectly with
Mozilla Firefox.
Profiling
Struts supports profiling that can potentially identify any bottleneck in your program. Struts
keeps track the time taken by its filter dispatcher, each interceptor, action execution, and
result execution with the help of a class called UtilTimerStack (a member of the
com.opensymphony.xwork2.util.profiling package). By default, however, the profiling result
is not shown. The Profiling interceptor, which is part of the default stack, can help activate
profiling. When profiling is activated for a particular action, the profiling result is printed by
an internal logger in UtilTimerStack on the container console or to a log file, depending on
the setting of your container. If you're using Tomcat, this will be the console (on Windows)
or the catalina.out file (on Unix and Linux).
Here is an example of a profiling result for an action that uploads a file.
Each line represents an activity. On the left of each line is the accumulated time taken to
invoke the activity. For example, the bottommost line says that executing the result took
10ms, whereas invoking the Upload2 action took Oms. Of course it does not mean that
there was no time at all to execute the action, it's just that it took less than what the timer
can measure.
The Conversion Error interceptor's accumulated time is also 10ms, which means the
invocation of this interceptor took Oms because the activities invoked after it consumed
10ms. The File Upload interceptor took 20ms to execute (40ms – 20ms), and so on.
There are a few ways to activate profiling. Once it is active, it will stay active until it's
turned off or until the application is restarted.
http://localhost:8080/app16a/Test.action?profiling=true
Note that "profiling" is the default profiling key defined in the Profiling interceptor.
You can override this if you have to, for example because you have a form input with
the same name, by using the param element. For instance, this changes the profiling
key to pf so that you can turn on and off profiling by adding the request parameter
pf=true or pf=false.
2. By setting the active property of the UtilTimerStack object through code in a servlet
listener or your action method.
// do something
return SUCCESS;
}
System.setProperty(UtilTimerStack.ACTIVATE_PROPERTY, "true");
You can also monitor a certain activity in your action code. To do this, you need to call the
push and pop methods on UtilTimerStack:
Summary
This chapter discusses two important topics that can help you make more robust
applications, debugging and profiling. For debugging you use the debug tag and the
Debugging interceptor. Profiling is a bundled feature in Struts that just needs activation.
The Profiling interceptor can be used to activate profiling. Alternatively, you can use code to
activate it.
Chapter 17. Progress Meters
What do you do if one of your actions takes five minutes to complete and you don't want
your user worried or sleepy? Show a progress meter! In a web application writing a
progress meter is not an easy task, you would spend at least days on it. Happily, Struts has
an easy to use interceptor, Execute and Wait, that is good at emulating a progress meter
for heavy tasks.
Time consuming tasks, ones that take minutes, should be handled differently in web
applications than they are in desktop programs. They pose more risks in the former because
HTTP connections may time out, something not possibly occurring in the latter.
The Execute and Wait interceptor was designed to handle such situations. Since it's not part
of the default stack, actions that need this interceptor must declare it and it must come last
after in the interceptor stack.
This interceptor runs on a per-session basis, which means the same user may not cause two
instances of this interceptor (recall that each action has its own instance of any declared
interceptor) to run in parallel. An action backed by this interceptor will execute normally.
However, Execute and Wait will assign a background thread to handle the action and
forward the user to a wait result before the execution finishes and schedule the result to hit
the same action again. On subsequent requests, if the first action has not finished
executing, the wait result is sent again. If it has finished, the user will get a final result for
that action.
A wait result acts like a dispatcher result. However, the view it forwards to has this meta
tag that reloads the same URL after n seconds:
By default n is 5 and url is the same URL used to invoke the current action.
You can create your own wait view if you don't like the default. If no wait result is found
under the action declaration, the default will be used.
The Execute and Wait interceptor can take these parameters, all optional.
The execute method of the action class takes twelve seconds to complete, enough to show
off the progress meter. The complete field and its getter are only used by the second
example.
The action declaration for the first example is given in Listing 17.2.
http://localhost:8080/app17a/HeavyDuty1.action
The wait page is shown in Figure 17.1. Pretty standard and uninspiring.
If you're interested enough to check, you'll see the source of the wait page as follows.
<html>
<head>
<meta http-equiv="refresh"
content="5;url=/app17a/HeavyDuty1.action"/>
</head>
<body>
Please wait while we process your request...
<p/>
This page will reload automatically and display your request
when it is completed.
</body>
</html>
Notice the meta tag? That's the one that forces the page to refresh every five seconds.
Using A Custom Wait Page
The second example is similar to the first one and uses the action class in Listing 17.1. It
also uses the complete property of the action class to show the progress to the user. The
second example also differs from the first in that it employs a custom wait page, as shown
in the action declaration in Listing 17.3.
Note that a wait result is present that forwards to a Wait.jsp page (See Listing 17.4). It
is an ordinary JSP that has a meta tag that refreshes the page every two seconds. Since the
URL part is not present in the meta tag, the same page will be reloaded.
Another thing to note is that it displays the value of complete. Its getter increments its
value by 10 every time it is called.
private int complete = 0;
public int getComplete() {
complete += 10;
return complete;
}
http://localhost:8080/app17a/HeavyDuty2.action
The wait page is shown in Figure 17.2. Notice that it looks more like a progress meter
that indicates how much progress is being made?
Summary
This chapter discusses how you can use the Execute and Wait interceptor to handle time-
consuming tasks. The trick is to create a background thread that executes the action and
forward the user to a temporary wait page that keeps hitting the same action until the
background thread finishes its task.
Chapter 18. Custom Interceptors
There are more than a dozen default interceptors that come with Struts. Input validation,
for instance, is handled by the Validation interceptor. Unplug this interceptor and validation
will stop working. File upload is so smooth thanks to another interceptor, the File Upload
interceptor. Some of the interceptors may prevent the action from being executed if certain
conditions are not met. For example, the Validation interceptor keeps an action from firing if
an error occurs during the validation of that action.
For most applications, the default interceptors are sufficient. However, there are times when
you need to create your own interceptor. This chapter explains how.
• init. This method is called once right after the interceptor is created. An interceptor
author overrides this method to perform resource initialization.
• intercept. This method is called every time the request for an action is invoked,
giving the interceptor a chance to do something before and after the action is
executed.
• destroy. The method is called before the interceptor is destroyed. Code to release
resources should be written here.
Struts calls the intercept method of each interceptor registered for an action. Each time
this method is called, Struts passes an instance of the
com.opensymphony.xwork2.ActionInvocation interface. An ActionInvocation
represents the execution state of an action, from which an interceptor can obtain the
Action object as well as the Result object associated with the action. To let the execution
chain proceed to the next level, the interceptor calls the invoke method on
ActionInvocation.
void beforeResult(com.opensymphony.xwork2.ActionInvocation
invocation, java.lang.String resultCode)
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
Every time an action backed by this interceptor is invoked, the interceptor injects the
DataSource object. Not all actions will get this object, only those whose classes implement
the DataSourceAware interface will. This interface is given in Listing 18.4.
The Product_list action lists products from a database. The database can be accessed by
using the DataSource injected by the custom interceptor. The ListProductAction class in
Listing 18.6 handles the action.
Listing 18.6. The ListProductAction class
package app18a;
import interceptor.DataSourceAware;
import java.util.List;
import javax.sql.DataSource;
import com.opensymphony.xwork2.ActionSupport;
public class ListProductAction extends ActionSupport implements
DataSourceAware {
private DataSource dataSource;
private List<Product> products;
There are two things to note. A product is represented by the Product class in Listing
18.7. A Product is a transfer object that encapsulates four properties, productId, name,
description, and price. The ListProductAction class implements DataSourceAware so
an instance of ListProductAction can be injected a DataSource.
The ListProductAction class uses the ProductDAO class (shown in Listing 18.8) to
retrieve data from the Products table in the database. You must of course first create this
table and populates it with data. The action injects the ProductDAO the DataSource by
calling the ProductDAO's setDataSource method.
Listing 18.8. The ProductDAO class
package app18a;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
http://localhost:8080/app18a/Product_list.action
You will see the results shown in your browser, like those in Figure 18.1. What you see
depends on the content of the Products table in your database.
Summary
You can write custom interceptors by implementing the Interceptor interface or extending
the AbstractInterceptor class. In this chapter you learned how to write a custom
interceptor and how to register it in an application.
Chapter 19. Custom Result Types
Struts ships with standard result types such as Dispatcher and Stream. This chapter
explains how you can write a custom result type. An example, a CAPTCHA image producing
result type, is also discussed.
Overview
This method gets called when the result is executed. A result type author can write the code
that will be run when an instance of the result type executes.
Note
CAPTCHA is a slightly contrived acronym for "Completely Automated Public Turing test to
tell Computers and Humans Apart." CAPTCHA images are often used in web forms. For
example, a login form, such as the one in Figure 19.1, can use a CAPTCHA image in
addition to the usual user name and password fields to make it more secure. A user who
wishes to log in is asked to type in his/her user name and password plus the word displayed
by the CAPTCHA image. Login is successful if the user entered the correct username and
password as well as typed in the correct image word. A login form equipped with a
CAPTCHA image is more secure because brute force, attempts to log in by using
automatically generated pairs of user names and passwords until one successfully logs the
offending computer in, will be less likely to be successful.
Figure 19.1. The CAPTCHA-facilitated login page
Another common use of CAPTCHA is to prevent spammers from sending messages to form
owners. CAPTCHA forms may be used to frustrate automatic programs that submit forms
because submission will only be successful if the correct word is also supplied.
The idea behind using CAPTCHA in forms is that computers are good with characters and
numbers but not so with images. Therefore, if you ask the computer what the word in the
image in Figure 19.1 reads, chances are the computer will not have a clue. Unless of
course you use a program designed to recognize images, which are already in existence but
are not so commonplace. In other words, CAPTCHA makes your login form more secure but
there's no 100% guarantee that it will protect you from the most determined people.
In a web form, CAPTCHA works by producing a pair of words. The first word is converted
into an image and the second word is produced using an algorithm in such a way that
different instances of the same word always produce the same second word. However,
knowing the second word is not good enough to find out what the first word is. Many
implementations of CAPTCHA use a hash algorithm to produce the second word.
There are several ways of producing CAPTCHA-facilitated forms. One way would be to
generate hundreds or thousands of word pairs and store them in a database. When you
send the form to the browser, you also send the image version of the first word and the
second word in a hidden field. When the form is submitted, the server matches the hidden
field value and the word typed in by the user. If the two match, the user passed the
CAPTCHA test.
Another way, one that does not require a database, is by using cookies. A Struts action
specializes in generating a word and its hash and converts the word to an image. At the
same time, the second word or the hash is sent to the browser as a cookie. When the form
is submitted, the server will match the value entered by the user and the cookie. The server
will do this by using the same algorithm that produces the word pair in the first place.
It sounds complicated, but I have written a Java library, free for download from
brainysoftware.com and free to use commercially or non-commercially, that can
generate random words and produce CAPTCHA images. The library is included in the ZIP
that accompanies this book.
Returns an image representation of the specified word. The width and height arguments
specify the image size in pixel. The last argument is currently reserved for future use.
Returns true if the specified hash is the hash of the specified word. Otherwise, returns false.
Now, let's see how we can create a result type that returns a CAPTCHA image with the help
of this library.
The CaptchaResult class in Listing 19.1 is the brain of the new result type. It extends
the StrutsResultSupport class and overrides its doExecute method.
Listing 19.1. The CaptchaResult class
package com.brainysoftware.captcha;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import com.opensymphony.xwork2.ActionInvocation;
The doExecute method generates a random word and a corresponding hash and creates a
Cookie that contains the hash. It then appends the cookie to the HttpServletResponse
object, generates a BufferedImage of the random word, and sends the image to the
browser.
<struts>
<package name="app19a" extends="struts-default">
<result-types>
<result-type name="captcha"
class="com.brainysoftware.captcha.CaptchaResult"
/>
</result-types>
<action name="Login_input">
<result>/jsp/Login.jsp</result>
</action>
<action name="Login" class="app19a.Login">
<param name="hashCookieName">hashCookie</param>
<result name="success">/jsp/Thanks.jsp</result>
<result name="input">/jsp/Login.jsp</result>
</action>
<action name="GetCaptchaImage">
<result type="captcha">
<param name="hashCookieName">hashCookie</param>
<param name="wordLength">6</param>
<param name="imageWidth">90</param>
<param name="imageHeight">25</param>
</result>
</action>
</package>
</struts>
The captcha result type is declared under <result-types> and there are three action
elements. The Login_input action shows the login form and the Login action verifies the
user name and password and the CAPTCHA word. The GetCaptchaImage action returns a
CAPTCHA image.
The execute method verifies the user name and password and validates the word and the
hash. The hash is obtained from a cookie and the word is what the user types in the third
text field in the Login form.
The Login.jsp page is given in Listing 19.4 and the Thanks.jsp page in Listing 19.5.
Listing 19.4. The Login.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Login with CAPTCHA</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global">
<h3>Enter your user name, password, and the image word</h3>
<s:actionerror/>
<s:form action="Login">
<s:textfield name="userName" label="User Name"/>
<s:password name="password" label="Password"/>
<tr>
<td><img src="GetCaptchaImage.action"/></td>
<td>
<s:textfield theme="simple" name="word"
value=""/>
</td>
</tr>
<s:submit value="Login"/>
</s:form>
</div>
</body>
</html>
Note that the img element's src attribute in the Login.jsp page points to the
GetCaptchaImage action.
Note
You must copy both the brainycaptcha.jar and brainycaptchaplugin.jar files in your
WEB-INF/lib directory. Both JAR files are included in the zip file that bundles the sample
applications that accompany this book.
To test the application, direct your browser to this URL:
http://localhost:8080/app19a/Login_input.action
Summary
This chapter explained how you could write a custom result type. It also presented an
example of result type that streamed a CAPTCHA image to the browser.
Chapter 20. Velocity
The Apache Velocity Engine is an open source templating engine that supports a simple and
powerful template language to reference Java objects. The Apache Velocity Project is an
Apache project responsible for creating and maintaining the Apache Velocity Engine. The
software is available for free download from http://velocity.apache.org. Struts includes the
latest version of Velocity so there's no need to download Velocity separately.
Overview
Most Struts applications use JSP as the view technology. However, JSP is not the only view
technology Struts supports. Velocity and FreeMarker (discussed in Chapter 21,
"FreeMarker") can also be used to display data.
Velocity is a template language. A template is text that provides a basis for documents and
allows for words to be dynamically inserted into certain parts of it. For example, JSP can
serve as a template because it lets you insert values through the use of the Expression
Language. Since you already know JSP then it should not be hard to learn Velocity as both
are similar.
Unlike JSP, however, Velocity does not permit Java code to be used and only allows
rudimentary access to data. As such, developers are forced to separate presentation from
the business logic. In the past this "feature," the inability to use Java code, was often cited
by Velocity proponents as a reason to leave JSP and embrace Velocity. However, starting
from Servlet 2.0 you can now configure your servlet applications to disallow Java code in
JSPs and hence promote separation of presentation and logic.
Another point to note is that Velocity templates can be placed within the application or in
the class path. Contrast this with JSPs that can only be found if placed within the
application. Velocity will first search the application, if the template could not be found, it
will search the class path. In addition, Velocity templates can be loaded from a JAR while
JSPs cannot. Therefore, if you are deploying a component as a Struts plug-in, Velocity is a
great choice because you can include the templates in the same JAR as the other part of the
component.
Velocity supports simple control structures such as loops and if-else statements, though.
The dollar sign ($) has a special meaning in Velocity. It is used to indicate what follows is a
variable name that needs to be replaced at run-time.
The struts-default.xml file already defines the velocity result type, you can use Velocity in
Struts without writing additional configurations.
<result-type name="velocity"
class="org.apache.struts2.dispatcher.VelocityResult"/>
You just need to make sure that the following JAR files are copied to your WEB-INF/lib
directory: velocity-VERSION.jar, velocity-dep-VERSION.jar, and velocity-tools-VERSION.jar.
In addition, Velocity relies on the Digester project, so the commons-digester-VERSION.jar
file, included with Struts deployment, is also needed.
The default.properties file specifies the following entry that indicates that Velocity
configuration file must be named velocity.properties.
struts.velocity.configfile = velocity.properties
Just like JSP, Velocity allows access to important objects such as the ServletContext and
HttpServletRequest. Table 20.1 lists the implicit objects in Velocity.
Velocity in Struts extends the tags in the Struts tag library. Velocity tags are similar to the
Struts tags but the syntax for using them is slightly different. To start, you don't need this
taglib directive that you need when using JSP:
In JSP, a start tag is enclosed with < and > and an end tag with </ and >. In Velocity a
start tag starts with #s followed by the tag name. Most tags are inline and do not need an
end tag. For example:
#stextfield
#sform ...
#stextfield ...
#ssubmit ...
#end
Velocity tag attributes are enclosed in brackets. Each attribute name/value are enclosed in
double quotes and separated by an equal sign.
For example:
Velocity Example
The app20a application illustrates the use of Velocity in Struts. It features two actions,
Product_input and Product_save, as declared using the action elements in Listing
20.1.
Listing 20.1. Action declarations
<package name="app20a" extends="struts-default">
<action name="Product_input">
<result type="velocity">/template/Product.vm</result>
</action>
<action name="Product_save" class="app20a.Product">
<result name="input" type="velocity">
/template/Product.vm
</result>
<result type="velocity">/template/Details.vm</result>
</action>
</package>
The Product_input action forwards to the Product.vm template in Listing 20.2. This
template contains a form for inputting product information.
<h3>Add Product</h3>
#sform ("action=Product_save")
#stextfield ("name=name" "label=Product Name")
#stextfield ("name=description" "label=Description")
#stextfield ("name=price" "label=Price")
#ssubmit ("value=Add Product")
#end
</div>
</body>
The Product_save action invokes the Product action class in Listing 20.3 and forwards
to the Details.vml template in Listing 20.4.
Listing 20.3. The Product class
package app20a;
import com.opensymphony.xwork2.ActionSupport;
public class Product extends ActionSupport {
private String productId;
private String name;
private String description;
private double price;
http://localhost:8080/app20a/Product_input.action
If you click the Add Product button, you will see the content of the Details.vm template.
Figure 20.1. The content of the Details.vm template
Summary
JSP is not the only view technology that can be used in Struts. Velocity and FreeMarker can
too, and so can XSLT. This chapter explained how you can use Velocity as a view
technology.
Chapter 21. FreeMarker
FreeMarker is a template engine written in Java that can be used with Struts. In fact, the
Struts tag library uses FreeMarker as the default template language. FreeMarker supports
more features than Velocity. For detailed comparison between FreeMarker and Velocity,
read this:
http://freemarker.org/fmVsVel.html
Overview
To use FreeMarker in Struts, you don't need to install additional software. The JAR file that
contains the FreeMarker engine, the freemarker-VERSION.jar file, is already included in
Struts deployment. In fact, without this file your Struts application won't work because
FreeMarker is the default template for the Struts tag library.
FreeMarker templates can be placed within the application directory or the class path. The
application directory will be searched first. The fact that the FreeMarker engine also
searches the class path makes this technology perfect for Struts because it enables
FreeMarker templates to be packaged in JAR files. As you'll learn in Chapter 23, "Plug-
ins", plug-ins are distributed as JAR files. You cannot package JSPs in a JAR and hope the
web container will translate and compile them.
1. Built-in variables
2. The Value Stack
3. The action context
4. Request scope
5. Session scope
6. Application scope
Just like JSP, FreeMarker allows access to important objects such as the ServletContext
and HttpServletRequest. Table 21.1 lists the implicit objects in FreeMarker.
FreeMarker Tags
Struts provides FreeMarker tags that are extensions to the tags in the Struts tag library.
The syntax is very similar to that in JSP. You use <@s.tag as the start tag and </@s.tag>
as the end tag, where tag is the tag name. For example, here is the form tag:
<@s.form action="...">
</@s.form>
<s:form action="Product_save">
<s:textfield name="name" label="Product Name"/>
<s:textfield name="description" label="Description"/>
<s:textfield name="price" label="Price"/>
<s:submit value="Add Product"/>
</s:form>
FreeMarker supports dynamic attributes, a feature missing in JSP. In JSP, you can use the
param tag to pass values to the containing tag. For instance:
<s:url value="myResource">
<s:param name="userId" value="%{userId}"/>
</s:url>
In FreeMarker you don't need to pass the parameter using the param tag. Instead, you can
treat the parameter as a dynamic attribute. The FreeMarker equivalent of the url tag above
will be:
Example
As an example, consider the app21a application that has two actions, Product_input and
Product_save. The application uses FreeMarker templates instead of JSPs.
The Product_save action uses the Product action class given in Listing 21.2. This is
exactly the same action class you would have for a dispatcher result.
Listing 21.2. The Product class
package app21a;
import com.opensymphony.xwork2.ActionSupport;
public class Product extends ActionSupport {
private String productId;
private String name;
private String description;
private double price;
Listings 21.3 and 21.4 shows two templates that sport FreeMarker tags.
<h3>Add Product</h3>
<@s.form action="Product_save">
<@s.textfield name="name" label="Product Name"/>
<@s.textfield name="description" label="Description"/>
<@s.textfield name="price" label="Price"/>
<@s.submit value="Add Product"/>
</@s.form>
</div>
</body>
<h3>Product Details</h3>
<table>
<tr>
<td>Name:</td>
<td><@s.property value="name"/></td>
</tr>
<tr>
<td>Description:</td>
<td>${description}</td>
</tr>
<tr>
<td>Price:</td>
<td>${price}</td>
</tr>
</table>
</div>
</body>
Note that to access an action property, you can use the property tag or the notation ${ ...
}.
http://localhost:8080/app21a/Product_input.action
You'll see the Product form like the one in Figure 21.1.
Summary
FreeMarker is the template language used to render tags in the Struts tag library. It is also
a good alternative to JSP and allows templates to reside in the class path, in addition to a
directory under the application directory. Because of this feature, FreeMarker templates can
be deployed in a JAR file, which makes FreeMarker suitable for plug-ins.
Chapter 22. XSLT Results
Extensible Stylesheet Language (XSL) is a World Wide Web Consortium specification that
deals with XML formatting. XSL defines how an XML document should be displayed. XSL to
XML is what CSS to HTML. There are two technologies defined in the XSL specification: XSL
Formatting Objects and XSL Transformations (XSLT). The latter is the main focus of this
chapter as the Struts XSLT result type is intended to support this technology.
http://www.w3.org/TR/xslt
Overview
XML documents are used for easy data exchange. Unlike proprietary databases where data
is stored in proprietary formats that make exchanging data difficult, XML documents are
plain text and can be understood by just reading the documents. For example, this XML
document is self-explanatory, it contains information about an employee.
<employee>
<employeeId>34</employeeId>
<firstName>Jen</firstName>
<lastName>Goodhope</lastName>
<birthDate>2/25/1980</birthDate>
<hiredDate>3/22/2006</hiredDate>
</employee>
If you send this XML document, the receiving party can easily understand it and probably
manipulate it with their own tools. However, it's probably not as straightforward as you may
think. The other party may have XML documents containing details on employees, but the
format is slightly different. Instead of employeeId they might use id and instead of
employee they might call it worker.
<worker>
<id>50</employeeId>
<firstName>Max</firstName>
<lastName>Ocean</lastName>
<birthDate>12/13/1977</birthDate>
<hiredDate>10/5/2005</hiredDate>
</worker>
If the data from the first XML document is to be merged into the second XML document, for
example, there must be some kind of transformation that converts <employee> to
<worker> and <employeeId> to <id>. This is where XSLT plays a role.
Figure 22.1 shows how XSLT works. At the core is an XSLT processor that reads the
source XML and uses a stylesheet to transform an XML document into something else.
Figure 22.1. How XSLT works
An XSL stylesheet is an XML file with an xsl or xslt extension. The root element of an XSL
stylesheet is either <xsl:stylesheet> or <xsl:transform>. Here is the skeleton of an XSL
stylesheet:
...
</xsl:stylesheet>
The xsl:stylesheet element has two attributes in this case. The first attribute declares the
version, which currently is 1.0. The second attribute declares the XML namespace. It points
to the official W3C XSLT namespace. The prefix xsl is preferred for an XSL stylesheet but
could be anything you like.
The list of elements can be found in the specification. Here are some of the more important
ones:
• xsl:template. Defines a template. Its match attribute associates the template with
an element in the source XML. For example, this xsl:template element matches the
root of the source XML:
<xsl:template match="/">
• xsl:value-of. Reads the value of an XML element and appends it to the output
stream of the transformation. You select an XML element by using the select
attribute. For instance, the following prints the value of the name element under
<result>.
<xsl:value-of select="/result/name"/>
• xsl:for-each. Iterates over a node set. Again, use the select attribute to specify an
XML element. For example, this xsl:for-each element iterates over the
result/supplier elements and prints the details of each supplier and formats them
in an HTML table.
<table>
<xsl:for-each select="/result/supplier">
<tr>
<td><xsl:value-of select="supplierName"/></td>
<td><xsl:value-of select="address"/></td>
</tr>
</xsl:for-each>
</table>
The Struts XSLT result type inspects the Value Stack and produces a raw XML with a result root element. Nested within this element are
all the action properties and other information, such as the locale. The XSLT result will then use the supplied XSLT stylesheet to convert
the raw XML to another XML or XHTML.
Note there is also a deprecated location parameter that does the same thing as stylesheetLocation.
Note
By default XSLT stylesheets are cached. In development mode it's easier if they are not. You can change this behavior by setting
struts.xslt.nocache to true in the struts.properties file.
Consider the Product action class in Listing 22.1. The supplier property of Product is of type Supplier, shown in Listing 22.2.
Listing 22.1. The Product action class
package app22a;
import com.opensymphony.xwork2.ActionSupport;
public class Product extends ActionSupport {
private String productId;
private String name;
private String description;
private double price;
private Supplier supplier;
Note that the execute method populates the properties. However, in a real world application, the data could come from anywhere.
The XSLT result would produce the following raw XML out of a Product action.
<result>
<actionErrors></actionErrors>
<actionMessages></actionMessages>
<description>
<#text>Super printer</#text>
</description>
<errorMessages></errorMessages>
<errors></errors>
<fieldErrors></fieldErrors>
<locale>
<ISO3Country>
<#text>USA</#text>
</ISO3Country>
<ISO3Language>
<#text>eng</#text>
</ISO3Language>
<country>
<#text>US</#text>
</country>
<displayCountry>
<#text>United States</#text>
</displayCountry>
<displayLanguage>
<#text>English</#text>
</displayLanguage>
<displayName>
<#text>English (United States)</#text>
</displayName>
<displayVariant>
<#text></#text>
</displayVariant>
<language>
<#text>en</#text>
</language>
<variant>
<#text></#text>
</variant>
</locale>
<name>
<#text>Epson</#text>
</name>
<price>
<#text>12.34</#text>
</price>
<productId>
<#text>345</#text>
</productId>
<supplier>
<address>
<#text>Oakville, Ontario</#text>
</address>
<name>
<#text>Online Business Ltd. </#text>
</name>
<supplierId>
<#text>20a</#text>
</supplierId>
</supplier>
<texts>
<#text>null</#text>
</texts>
</result>
Example
As an example, the app22a application features an action that uses an XSLT result. The
action, XSLT, converts the Product action to XHTML. The Product class is the same class
shown in Listing 22.1. The action declaration is shown in Listing 22.3.
The XSL action uses an XSLT result that employs the Product.xsl template in Listing
22.4.
http://localhost:8080/app22a/XSL.action
Note
Summary
The XSLT result type transforms action objects to XML. This result type is not as common as
Dispatcher but may be used in applications that require XML outputs, such as web services.
In this chapter you learned how it works and how to use it in your Struts applications.
Chapter 23. Plug-ins
The Struts plug-in provides an elegant mechanism to promote code reuse. A plug-in is
essentially a JAR. It may contain Java classes, FreeMarker or Velocity templates, and a
struts-plugin.xml file. The latter, if present, can be used to configure applications that use
the plug-in.
Overview
Struts has been designed to be extensible through plug-ins. Using a plug-in is as easy as
copying the plug-in JAR file to the WEB-INF/lib directory. Unlike an ordinary JAR file, a
plug-in may contain a struts-plugin.xml file that complies with the same rules as a
struts.xml file. It is possible to include configuration settings in a plug-in because Struts
loads configuration files in this order:
This means, you can override values defined in the struts-default.xml file in your struts-
plugin.xml, even though the application will have the final say since anything defined in
the struts.xml file overrides similar settings in other configuration files.
You can distribute any type of Struts component in your plug-in, including new packages,
new result types, custom interceptors, actions, new tag libraries, and others.
Struts comes bundled with several plug-ins, including the Tiles plug-in, the JFreeChart plug-
in, and the SiteMesh plug-in. However, the Struts community is buzzing with third-party
plug-ins, most of which are free. This site maintains a registry of Struts 2 plug-ins:
http://cwiki.apache.org/S2PLUGINS/home.html
At my last visit there were close to forty plug-ins available. I suspect there are others that
are not listed here.
<struts>
<package name="captcha-default" extends="struts-default">
<result-types>
<result-type name="captcha"
class="com.brainysoftware.captcha.CaptchaResult"
/>
</result-types>
</package>
</struts>
The directory structure of our plug-in application is shown in Figure 23.1. There are one
class and one XML file.
Now, create a JAR. The standard way, albeit not the easiest, is to use the jar program that
comes with your JDK by following these steps. This assumes that your JDK has been added
to the path directory so that you can invoke the jar program from anywhere in your
computer.
There are three actions defined in app23b, Login_input, Login, and GetCaptchaImage.
These action declarations are shown in Listing 23.2.
The first thing that should catch your attention is the extends attribute of the package
element. Its value is captcha-default, which represents a package in Captcha plug-in.
Since captcha-default extends struts-default, you inherit all the settings from the latter
in the package. In addition, you can use the new result type captcha. Note that the action
GetCaptchaImage has a captcha result type.
There is only one action class, the Login class, which is shown in Listing 23.3.
Listing 23.3. The Login class
package app23b;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.interceptor.ServletRequestAware;
import com.brainysoftware.captcha.CaptchaUtil;
import com.opensymphony.xwork2.ActionSupport;
Pay special attention to the execute method. How it works was explained in Chapter 19.
All I'll say here is the user can log in by using don and secret as the user name and
password and entering the word in the CAPTCHA image.
The Login.jsp page displays the Login form. This page is given in Listing 23.4 and the
Thanks.jsp, the page you'll see after a successful login, in Listing 23.5
Listing 23.4. The Login.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Login with CAPTCHA</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global">
<h3>Enter your user name, password, and the image word</h3>
<s:actionerror/>
<s:form action="Login">
<s:textfield name="userName" label="User Name"/>
<s:password name="password" label="Password"/>
<tr>
<td><img src="GetCaptchaImage.action"/></td>
<td><s:textfield theme="simple" name="word"
value=""/></td>
</tr>
<s:submit value="Login"/>
</s:form>
</div>
</body>
</html>
Note
You must copy both the brainycaptcha.jar and captchaplugin.jar files in your WEB-
INF/lib directory
http://localhost:8080/app23b/Login_input.action
You'll see the captcha image on the Login page as shown in Figure 23.2.
Figure 23.2. The CAPTCHA-facilitated login page
Summary
Struts provides an elegant way to distribute code: through plug-ins. This chapter showed
how easy it is to write and use one.
Chapter 24. The Tiles Plug-in
Web applications need a consistent look, which you can achieve by using the same layout
for all the pages. A typical layout has a header, a footer, a menu, an ad section, and a body
content. Normally, many parts—such as the header, the footer, and the menu—look the
same in all pages. To support component reuse, a common part can be implemented as an
external resource. You then have the choice of using a frameset, a layout table, or div
elements to include these external resources. With a frameset, you reference each common
external resource using a frame. If layout tables or div elements are used, each JSP in your
application will employ several include files: one for the header, one for the footer, one for
the menu, one for the body content, and so on. The JSP technology provides the include
directive (<%@ include %> to include static files and the include tag (<jsp:include>)
to include dynamic resources. However, as will be discussed in the first section of this
chapter, both JSP includes are not without shortcomings. If the layout needs changing, you
will have to change all your JSPs.
Tiles overcomes these failings and adds more features to enable you to lay out your pages
more easily and flexibly. First and foremost, Tiles provides a tag library that allows you to
create a layout JSP that defines the layout for all JSPs in an application. Changes to a layout
JSP will be reflected in all the JSPs referencing it. This means, only one page needs to be
edited should the layout change.
In addition to layout JSPs, Tiles allows you to write definition pages, which are more
powerful than the former. A definition page can have one of the two formats, JSP and XML.
This chapter teaches you how to make full use of Tiles by presenting a sample application
that uses Tiles.
Note
The Tiles framework provides its services through a series of tags in the Tiles Tag Library.
Tiles used to be a component of Struts 1. After it gained popularity, Tiles was extracted
from Struts as Tiles 2 and is now an independent Apache project. Its website is
http://tiles.apache.org/. The classes that make up Tiles are deployed in three JAR
files, tiles-core- VERSION, tiles-api- VERSION.jar, and tiles-jsp- VERSION.jar. In
addition, to use Tiles with Struts, you need the struts2-tiles-plugin- VERSION..jar. All
these JARs are deployed with Struts 2. You must copy these JARs to your WEB-INF/lib
directory
Figure 24.1 shows a page layout with a header, a footer, a menu, an ad section, and a
body content. All parts, with the exception of the body content, are common to all the JSPs.
The header comes from the header.jsp page, the footer from the footer.jsp page, the
menu from the menu.jsp page, and the ad section from the ad.jsp page.
Figure 24.1. A typical layout of a web page
To achieve a consistent look, each of your JSPs must contain a layout table such as this.
<html>
<head><title>Page title</title></head>
<body>
<table>
<tr>
<td colspan="3"><%@include file="header.jsp"%></td>
</tr>
<tr>
<td width="120"> <%@include file="menu.jsp"%></td>
<td>
body content
</td>
<td width="120"> <%@include file="ad.jsp"%></td>
</tr>
<tr>
<td colspan="3"><%@include file="footer.jsp"%></td>
</tr>
</table>
</body>
</html>
Note
A layout table is used just for illustration. You should always use CSS instead.
With this approach, what differentiates one JSP from another is the body content.
Now, what if you want to change the layout? For example, what if you want to make the
menu wider by 30 pixels? Or, what if you want the ad to appear on top of the menu? This
would require changing all your JSPs, which of course is a tedious and error-prone chore.
Tiles can help solve this problem.
A layout page is a template JSP that defines a layout. You can have as many layout JSPs as
you deem necessary. Each JSP that needs to use a layout will only need to reference the
layout JSP indirectly. If you need to change the layout of the whole application, you need
only change one file, the layout JSP.
Note
JSPs that need to use a layout do not directly reference the layout page. Instead, they refer
to a definition that references the layout page. You'll learn more about Tiles definitions in
the next subsection.
An example of a layout JSP is given in Listing 24.1. The JSP is named MyLayout.jsp.
There are two tags from the Tiles Tag Library used here, insertAttribute and getAsString.
The insertAttribute tag defines an insert point into which an attribute will be inserted. The
name attribute specifies the logical name of the resource that will be inserted.
name String The name of the attribute to insert. It will be ignored if the value
attribute is present.
flush boolean A value of true causes the current page output stream to be flushed
before insertion.
ignore boolean A value of true indicates that no exception will be thrown if the
attribute specified by the name attribute cannot be found. The
default value for this attribute is false.
role String Specifies the role that the current user must belong to in order for
this tag to be executed.
The getAsString tag specifies a variable whose String value will be passed by objects
referencing the layout JSP. You would imagine that the getAsString tag in Listing 24.1
would be passed a different page title by each JSP using this layout.
name String A required attribute that specifies the name of the attribute.
ignore boolean A value of true indicates that no exception will be thrown if the
attribute specified by the name attribute cannot be found. The
default value for this attribute is false.
Table 24.2. getAsString tag's attributes
Attribute Type Description
role String Specifies the role that the current user must belong to in order for
this tag to be executed.
Tiles Definitions
The second thing you need to grasp before you can use Tiles is definitions. A definition is a
layer between a layout page and a JSP using the layout. In Struts a Tiles definition
corresponds to a view. The view is normally a JSP, but Velocity or FreeMarker can also be
used.
By analogy, a layout page is like a Java interface and a definition page is a base class that
provides default method implementations of the interface. Any Java class that needs to
implement the interface can extend the base class, so that the class does not need to
implement a method unless it needs to override the default. By the same token, a JSP
references a definition page instead of a layout JSP. The diagram in Figure 12.2 provides
comparison between Java inheritance and Tiles' layout and definition pages.
Figure 24.2. Comparing Java inheritance and Tiles' layout and definition
Tiles definitions are defined in a tiles.xml file located in the WEB-INF directory of your
Struts application. A tiles.xml file must comply with the DTD file defined in the following
DOCTYPE declaration that must precede the root element.
The root element for a tiles definition file is tiles-definition. Under it you write one or more
definition element, each of which defines a definition.
The name attribute specifies a name that will be used by a view to refer to this definition.
The template attribute specifies the template or layout page. In the example above, the
definition name is MyDefinition and the layout page is MyLayout.jsp.
A definition element is only useful if it contains one or several put-attribute elements. A
put-attribute element is used to pass a value to the layout page referenced by the
definition. For example, the definition elements below use the MyLayout.jsp page and
pass four values:
The Product definition passes "Product Info" to the getAsString tag in the MyLayout.jsp
page and inserts the Header.jsp, Footer.jsp, and Product.jsp to the header, footer, body
insertAttribute tags, respectively. The Thanks definition passes "Thanks You" to the
getAsString tag and inserts the Header.jsp, Footer.jsp, and Thanks.jsp to the header,
footer, body insertAttribute tags, respectively.
A Struts result that needs to forward to a definition can refer to it by its name like this.
<action name="Product_input">
<result name="success" type="tiles">Product</result>
</action>
<action name="Product_add">
<result name="success" type="tiles">Thanks</result>
</action>
Contrast these tiles results with dispatcher results that forward to a JSP.
<listener>
<listener-class>
org.apache.struts2.tiles.StrutsTilesListener
</listener-class>
</listener>
3. Extend the tiles-default package in your package or define the following in your
package:
<result-types>
<result-type name="tiles"
class="org.apache.struts2.views.tiles.TilesResult"/>
</result-types>
The app24a application has two actions, Product_input and Product_add. Figure
24.3 shows the directory structure of this application.
The action declarations for this application are shown in Listing 24.2.
<tiles-definitions>
<definition name="Product" template="/jsp/MyLayout.jsp">
<put-attribute name="pageTitle" value="Product Input"/>
<put-attribute name="header" value="/jsp/Header.jsp"/>
<put-attribute name="footer" value="/jsp/Footer.jsp"/>
<put-attribute name="body" value="/jsp/Product.jsp"/>
</definition>
Both definitions use the MyLayout.jsp page as their template. It's clear that the result
associated with the Product_input action will be forwarded to the MyLayout.jsp page
using the attributes specified in the Product definition. The Product_add action, on the
other hand, will be forwarded to the same template using the attributes specified in the
Thanks definition.
The MyLayout.jsp page is the same as that in Listing 24.1 but reprinted in Listing
24.4 for your reading convenience.
http://localhost:8080/app24a/Product_input.action
The same consistent layout is used for the Product_add action, as shown in Figure
24.5.
Figure 24.5. The Thank You page
Summary
Tiles helps Struts developers create a consistent look throughout an application. Tiles, which
is vastly superior to JSP includes, allows you to write layout and definition pages. This
chapter is meant to be a brief introduction to Tiles 2. For more details on Tiles, consult the
documentation at its website http://tiles.apache.org/.
Chapter 25. JFreeChart Plug-ins
JFreeChart is a Java open source library for creating charts. Thanks to the two plug-ins
discussed in this chapter, you can too tap the power of this popular library. This chapter is
focused on how to use the plug-ins and not a tutorial on JFreeChart itself, even though a
brief introduction is given.
JFreeChart must be downloaded separately as its LGPL license does not permit it to be
distributed with Struts. Information on how to download it is available from its website:
http://www.jfree.org/jfreechart,
This chapter explains the standard JFreeChart plug-in that comes with Struts and a more
flexible plug-in from BrainySoftware that I wrote.
http://www.jfree.org/jfreechart/api/javadoc/index.html
For example, you can create an instance of JFreeChart, hence a web chart, just by having
an instance of Plot, which will be discussed in the next subsection. Here are the
constructors of JFreeChart.
This abstract class is the main member of the org.jfree.chart.plot package. An instance of
Plot represents a plot that draws a chart. There are many subclasses of Plot that you can
use, one of which you'll see in the app25a application.
1. Download the JFreeChart component and copy the jfreechart- VERSION.jar and
jcommon- VERSION.jar files to your application's WEB-INF/lib directory.
4. Use chart as the result type and pass the width and height parameters to the result.
5. Your action class must have a chart property that returns the JFreeChart object to be
displayed.
The plug-in sends the chart as a PNG image. You may want to use an img element to
request the chart so that you can include the chart in an HTML page.
The plug-in accepts two parameters, width and height, to give you a chance to change the
chart size, which by default is 200px X 150px.
As an example, the app25a application shows an action that uses JFreeChart. The action
declarations for the application are given in Listing 25.1.
Listing 25.1. The action declarations
<package name="chart" extends="jfreechart-default">
<action name="chart" class="app20a.GetChartAction">
<result name="success" type="chart">
<param name="width">400</param>
<param name="height">300</param>
</result>
</action>
</package>
<package name="app25a" extends="struts-default">
<action name="main">
<result name="success">/jsp/Main.jsp</result>
</action>
</package>
There are two actions here. The chart action is part of the chart package that extends
jfreechart-default. This is the action that retrieves the chart. You can invoke this action by
itself to quickly view the resulting chart.
The second action, main, displays a JSP that contains an img element whose source
references the chart action. Note that both actions are contained in different packages. This
has to be so because jfreechart-default does not extend struts-default, so only chart
results are allowed under jfreechart-default.
The GetChartAction class is shown in Listing 25.2 and the Main.jsp page in Listing
25.3.
Listing 25.2. The GetChartAction class
package app25a;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import com.opensymphony.xwork2.ActionSupport;
XYSeriesCollection xyDataset =
new XYSeriesCollection(xySeries);
// create XYPlot
XYPlot xyPlot = new XYPlot(xyDataset, xAxis, yAxis,
new StandardXYItemRenderer(
StandardXYItemRenderer.SHAPES_AND_LINES));
chart = new JFreeChart(xyPlot);
return SUCCESS;
}
...
</p>
</body>
</html>
http://localhost:8080/app25a/main.action
aThere are two things in the JFreeChart plug-in that I did not really like and prompted me to
write my own plug-in, the BrainySoftware JFreeChart plug-in. The first is the fact that
jfreechart-default does not extend struts-default. The second is the fact that changing a
chart size requires updating the Struts configuration file. The exact size is often in the
graphic designer's hand and if he or she could resize the image without having to bother the
application administrator, it would be a much coveted feature.
Using it is not harder than the standard plug-in either, you just need to follow these steps.
1. Download the JFreeChart component and copy the jfreechart- VERSION.jar and
jcommon-VERSION.jar files to the WEB-INF/lib directory.
2. Copy the brainyjfreechartplugin.jar file to the WEB-INF/lib directory.
5. Your action class must have a chart property that returns the JFreeChart object to be
displayed. Optionally, you can have chartWidth and chartHeight properties to
determine the chart size.
Application app25b shows an action that uses Brainy Software's JFreeChart plug-in. The
action declarations are shown in Listing 25.4.
The action class is given in Listing 25.5. This is similar to the one in Listing 25.2,
however it has two additional properties, chartWidth and chartHeight.
Listing 25.5. The GetBrainyChartAction class
package app25b;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import com.opensymphony.xwork2.ActionSupport;
xySeries.add(0, 200);
xySeries.add(1, 300);
xySeries.add(2, 500);
xySeries.add(3, 700);
xySeries.add(4, 700);
xySeries.add(5, 900);
XYSeriesCollection xyDataset =
new XYSeriesCollection(xySeries);
// create XYPlot
XYPlot xyPlot = new XYPlot(xyDataset, xAxis, yAxis,
new StandardXYItemRenderer(
StandardXYItemRenderer.LINES));
chart = new JFreeChart(xyPlot);
return SUCCESS;
}
http://localhost:8080/app25b/main.action
Figure 25.2 shows the result.
Summary
JFreeChart is a powerful open-source library for generating charts. To use it in Struts, you
need a plug-in. At least two free JFreeChart plug-ins are available, the standard one that
comes with Struts and the one downloadable from brainysoftware.com. This chapter
showed how to use both.
Chapter 26. Zero Configuration
Struts configuration is easy, but it is possible not to have to configure at all. In other words,
zero configuration. Instead of mapping actions and results in the struts.xml file, you
annotate the action class. And if you're tired of annotating, you can use the CodeBehind
plug-in to handle that for you.
Note
Conventions
Since you will not have a configuration file if you decide to go the zero configuration way,
you will need to tell Struts how to find your action classes. You do this by telling Struts the
Java packages of the action classes used in your application by including, in your web.xml
file, an actionPackages initial parameter to the Struts filter dispatcher. Like this.
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>app26a,com.example</param-value>
</init-param>
</filter>
Now, since without a struts.xml file you cannot give an action a name, you rely on Struts to
do that. What name does Struts give your action? The action name will be the same as the
name of the action class after the first letter of the class name is converted to lower case
and its Action suffix, if any, is removed. Therefore, the action name for an action class
named EmployeeAction will be employee, and you can invoke it using the URI
employee.action.
Of course you must also take into account the namespace. If an action class is not a
member of a package passed to the actionPackages parameter, but rather a member of its
sub-package, the part of the subpackage name is not in the actionPackages parameter will
be the namespace. For instance, if com.example is passed to the actionPackages parameter,
the action class com.example.action.CustomerAction will be accessible through this URI:
/action/customer.action
Annotations
By following the conventions explained in the previous section, you can invoke action
classes in your zero configuration application. But hold on, Struts does not know yet what
results are associated with those action classes. This time you need to annotate, using the
annotation types discussed in this section.
@Result
• name. The name of the result that corresponds to the return value of the action
method.
• params. An array of Strings used to pass parameters to the result.
• type. The class of the result type whose instance will handle the result.
• value. The value passed to the result.
@Result(name="success", value="/jsp/Customer.jsp",
type=ServletDispatcherResult.class)
public class Customer extends ActionSupport {
public String execute() {
System.out.println("Help I'm being executed...");
return SUCCESS;
}
}
The annotation in Listing 26.1 indicates to Struts that if the action method returns
"success," Struts must create an instance of ServletDispatcherResult and pass the
instance "/jsp/Customer.jsp." The ServletDispatcherResult class is the underlying class
for the Dispatcher result type. Practically this means the same as this.
http://localhost:8080/app26a/customer.action
Note
When going zero configuration, you need to get familiar with the underlying classes for the
bundled result types, not only their short names. You can look up the class names in
Appendix A.
@Results
If an action method may return one of two values, say "success" or "input," you cannot use
two Result annotations. Instead, use @Results. The syntax for this annotation type is as
follows.
For example, the Supplier action class in Listing 26.2 may return "success" or "error." It
is annotated @Results.
Listing 26.2. The Supplier action class
package app26a;
import org.apache.struts2.config.Result;
import org.apache.struts2.config.Results;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import com.opensymphony.xwork2.ActionSupport;
@Results({
@Result(name="success", value="/jsp/Customer.jsp",
type=ServletDispatcherResult.class),
@Result(name="error", value="/jsp/Error.jsp",
type=ServletDispatcherResult.class)
})
http://localhost:8080/app26a/supplier.action
http://localhost:8080/app26a/supplier.action?name=whatever
@Namespace
Use this annotation type to override the namespace convention. It has one element, value,
which specifies the namespace for the annotated class.
@Result(name="success", value="/jsp/Customer.jsp",
type=ServletDispatcherResult.class)
@Namespace(value="/")
public class EditCustomer extends ActionSupport {
}
http://localhost:8080/app26a/editCustomer.action
Consequently, you can no longer use this URL to invoke the editCustomer action.
http://localhost:8080/app26a/admin/action/editCustomer.action
@ParentPackage
Use this annotation type to inherit an XWork package other than struts-default. For
example, this annotation indicates that the action belongs to the captcha-default package:
@ParentPackage(value="struts-default")
To use this plug-in, you must first copy the struts-codebehind-plugin-VERSION.jar file
to your WEB-INF/lib directory.
You still need to pass an actionPackages initial parameter in your web.xml file so that
Struts can find default action classes.
For example, the app26b application shows how to use the CodeBehind plug-in. To the
filter dispatcher, we pass an actionPackages initial parameter, as shown in Listing
26.4.
Listing 26.4. The filter declaration
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>app26b</param-value>
</init-param>
</filter>
The Login class in Listing 26.5 is an action class in app26b. By using the CodeBehind
plug-in, the Login action will be able to forward to the correct JSP after the action is
executed.
&& userName.equals("don")
&& password.equals("secret")) {
return SUCCESS;
} else {
return INPUT;
}
}
The action method (execute) returns either "input" or "success." As such, the forward JSP
will have to be either login-input.jsp or login-success.jsp. These JSPs are shows in
Listings 26.6 and 26.7. Note that in Listing 26.6, because there's no explicit action
declaration, you need to pass a URI and not an action name to the form's action attribute.
Listing 26.6. The login-input.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Login</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global" style="width:400px">
<h3>Enter your user name and password</h3>
<s:form action="login.action">
<s:textfield name="userName" label="User Name"/>
<s:password name="password" label="Password"/>
<s:submit value="Login"/>
</s:form>
</div>
</body>
</html>
<body>
You're logged in.
</body>
</html>
http://localhost:8080/app26b/login-input.action
The CodeBehind plug-in will kick in, invoke the Login action, and forward to the login-
input.jsp page. The result is shown in Figure 26.1.
Figure 26.1. The login-input.jsp page
When you submit the form, the field values are sent to this URL:
http://localhost:8080/app26b/login.action
Summary
This chapter discussed the zero configuration feature in Struts that can match a URL with an
action class. This feature does not match actions and results, however, and for the latter
you need the CodeBehind plug-in.
Chapter 27. AJAX
The Struts Dojo plug-in bundles the Dojo Toolkit, an open source JavaScript framework, and
provides custom tags to build AJAX components effortlessly. Thanks to this plug-in you can
even use AJAX even if you know nothing about JavaScript. However, a solid command of
JavaScript will help you tap the power of AJAX.
This chapter discusses the tags in the plug-in. To test the examples in this chapter, you
have to be using Struts 2.1.0 or later. At the time of writing, version 2.1 has not been
released and can be downloaded from here.
http://people.apache.org/builds/struts/2.1.0/
The Struts Dojo plug-in itself is not included in the lib directory of the Struts distribution and
must be extracted from the Showcase application that comes with Struts. Unfortunately, the
version of Dojo in this plug-in is 0.4, which is a much older version than what is available at
the time of writing (version 1.01). Version 0.4 is very slow compared with its successors.
The next release of the Struts Dojo plug-in is expected to bring Dojo 1.01 or later to the
table.
Another unfortunate fact is that Dojo 1.0 or later is not backward compatible with version
0.4, which means any code you write that uses this plug-in may not work with a future
version of the plug-in. Having said that, the plug-in is still great software that can help you
write AJAX applications easily.
Note
AJAX Overview
AJAX is a name coined by Jesse James Garrett of Adaptive Path for two old technologies,
JavaScript and XML. AJAX applications asynchronously connect to the server to collect more
data that can be displayed in the current web page. As a result, new information can be
shown without page refresh. Google was the first to popularize this strategy with their Gmail
and Google Maps applications. However, Google was not the first to make full use of the
XMLHttpRequest object, the engine that makes asynchronous connections possible.
Microsoft added it to Internet Explorer 5 and seasoned developers discovered ways to reap
its benefits. Soon afterwards Mozilla browsers also had their own version of this object. Prior
to XMLHttpRequest, people used DHTML and HTML frames and iframes to update pages
without refresh.
This is where a JavaScript framework like Dojo comes to rescue. With Dojo you only need to
write and test once and let it worry about browser compatibility. Needless to say, using the
Struts Dojo plug-in as your AJAX platform saves an awful lot of time.
Dojo allows you to connect a JavaScript function with an event. As such, you can create an
event handler that will get called when an event is triggered. The Dojo connect method links
an event with a function. The disconnect method severs a connection. Dojo's event object is
the normalized version of the JavaScript event object. Unlike the latter, which behaves
slightly differently in different browsers and hence making developing cross browser
applications very difficult, the former provide a uniform interface that works the same in all
supported browsers. Using Dojo saves you time because you don't need to test and tweak
your code to cater for a specific browser.
In addition to the normalized event object, Dojo supports a topic-based messaging system
that enables anonymous event communication. Anonymous in the sense that you can
connect elements in a web page that have no previous knowledge about each other. A topic
is logical channel similar to an Internet mailing list. Anyone interested in a mailing list can
subscribe to it to get notification every time a subscriber broadcasts a message. With a
topic-based messaging system such as that in Dojo, a web object (a button, a link, a form,
a div element) may subscribe to a topic and publish a topic. This means, an AJAX
component can be programmed to do something upon the publication of a topic as well as
publish a topic that may trigger other subscribers to do something.
To publish a topic, you use the publish method. Bear in mind that this is how you do it in
Dojo 0.4, which may not work in newer versions of Dojo.
dojo.event.topic.publish(topicName, arguments)
The topic name can be anything. As long as the other parties know a topic name, they can
subscribe to the topic.
In AJAX programming, you normally subscribe to a topic because you want something to be
done upon a message publication to that topic. As such, when you subscribe to a topic, you
also define what you need to do or what function to call. Here is the method to subscribe to
a topic in Dojo. Again, this is Dojo 0.4 we're talking here.
Dojo.event.topic.subscribe(topicName, functionName)
The tags in the Struts Dojo plug-in make it even easier to work with topics. Most tags can
subscribe and publish a topic without JavaScript code. For instance, the a tag has an
errorNotifyTopics attribute you can use to list the topics to publish when the tag raises an
error. The div tag has a startTimerListenTopics attribute to accept a list of topics that will
cause the rendered div element to start its internal timer.
Topic-based messaging system will become clearer after you learn about the tags.
To use the tags in the plug-in, you must follow these steps.
2. Copy the Struts Dojo plug-in to your WEB-INF/lib directory. This plug-in is included
in the lib directory of this book.
The compressed attribute, which is true by default, indicates whether or not the
compressed version of Dojo files should be used. Using the compressed version saves
loading time, but it is hard to read. In development mode you may want to set this attribute
to false so that you can easily read the code rendered by the tags discussed in this chapter.
In development mode you should also set the debug attribute to true and the cache
attribute to false. Turning on the debug attribute makes Dojo display warnings and error
messages at the bottom of the page.
Here is how your head tag may look like in development mode.
<sx:head/>
The div Tag
This tag renders an HTML div element that can load content dynamically. The rendered div
element will also have an internal timer to reload its content at regular intervals. An ad
rotator can be implemented using the div tag without programming.
The div tag also inherits the common attributes specified in Chapter 5, "Form Tags."
Example 1
The Div1.jsp page in Listing 27.1 uses a div tag that updates itself every three
seconds. The href attribute is used to specify the server location that will return the content
and the updateFreq attribute specifies the update frequency in milliseconds. The internal
timer starts automatically because by default the value of the autoStart attribute is true.
An interesting feature of this tag is the automatic highlight color that will highlight the div
element and then fade. You can specify the highlight color using the highlightColor
attribute.
The Div2.jsp page in Listing 27.2 showcases a div tag whose startTimerListenTopics
attribute is set to subscribe to a startTimer topic. Upon publication of this topic, the div's
internal timer will start. A submit button is used to publish a startTimer topic.
http://localhost:8080/app27a/Div2.action
Example 3
This div tag in the Div3.jsp page in Listing 27.3 shows how you can use a div tag to
publish a topic.
Listing 27.3. The Div3.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<html>
<head>
<title>Div</title>
<sx:head/>
<script type="text/javascript">
var counter = 1;
dojo.event.topic.subscribe("updateCounter", function(event, widget){
dojo.byId("counter").innerHTML =
"The server has been hit " + counter++ + " times";
});
</script>
</head>
<body>
<sx:div
cssStyle="border:1px solid black;height:75px;width:100px"
href="ServerTime.action"
updateFreq="2000"
afterNotifyTopics="updateCounter"
highlightColor="#ddaaba">
Server time will be displayed here
</sx:div>
<div id="counter">
</div>
</body>
</html>
The div tag has its internal timer set to set off every two seconds. Every time it does, it
publishes an updateCounter topic, which is assigned to its afterNotifyTopics attribute.
The Dojo subscribe method is used to subscribe to the topic and run the specified function
every time the div tag publishes the topic.
The function associated with the updateCounter topic increments a counter and changes
the content of a second div tag.
http://localhost:8080/app27a/Div3.action
The a Tag
The a tag renders an HTML anchor that, when clicked, makes an AJAX request. The targets
attribute of the tag is used to specify elements, normally div elements, that will display the
AJAX response. If nested within a form, this tag will submit the form when clicked. Table
27.3 lists the attributes of the a tag.
listenTopics String The topics that will trigger the remote call
rendered HTML
The a tag also inherits the common attributes specified in Chapter 5, "Form Tags."
For instance, the A.jsp page in Listing 27.4 uses an a tag to populate the div elements
div1 and div2.
Listing 27.4. The A.jsp page
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<html>
<head>
<title>A</title>
<sx:head/>
</head>
<body>
<sx:div id="div1"
cssStyle="height:50px;width:200px;border:1px solid brown"/>
<sx:div id="div2"
cssStyle="height:50px;width:200px;border:1px solid brown"/>
<sx:a href="ServerTime.action" targets="div1,div2">
Update Time
</sx:a>
</body>
</html>
http://localhost:8080/app27a/A.action
Like the a tag, submit has a targets attribute to specify elements that will display the
result of the form submit.
The submit tag attributes are listed inTable 27.4. In addition, the submit tag inherits
the common attributes specified in Chapter 5, "Form Tags."
Table 27.4. submit tag attributes
Name Data Default Description
Type Value
The submit tag can be nested within the form it submits or stand independently. This
submit tag is nested within a form.
<s:div id="div1">
<s:form action="ServerTime.action">
<s:submit targets="div1"/>
</s:form>
</s:div>
And this is a submit tag outside the form it submits. In this case, you use the formId
attribute to specify the form to submit.
<s:form id="loginForm" action="...">
<s:textfield name="userName" label="User Name"/>
<s:password name="password" label="Password"/>
</s:form>
<sx:submit formId="loginForm"/>
The attributes that can appear inside a bind tag are presented in Table 27.5
The bind tag also inherits the common attributes specified in Chapter 5, "Form Tags."
As an example, the following bind tag attaches the b1 submit button's onclick event with
an AJAX call to MyAction.action and the response to the div element div1.
<sx:bind id="binder"
href="MyAction.action"
sources="b1"
events="onclick"
targets="div1" />
The following bind tag causes the onclick event of the b2 button to publish the myTopic
topic.
The datetimepicker tag renders either a date picker or a time picker. Figure 27.1
shows a date picker (on the left) and a time picker (on the right).
dayWidth String narrow Determines the day names in the header. Possible
values are narrow, abbr, and wide.
formatLength String short The formatting type for the display. Possible
values are short, medium, long, and full.
staticDisplay boolean false Whether or not only the dates in the current
month can be viewed and selected
toggleType String plain The toggle type for the dropdown. Possible values
Table 27.6. datetimepicker tag attributes
Name Data Default Description
Type Value
The datetimepicker tag inherits the common specified in Chapter 5, "Form Tags."
The acceptable date and time patterns for the displayFormat attribute can be found here:
http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns
The adjustWeeks attribute plays an important role in the display. If the value of
adjustWeeks is false, there are always six rows for each month. For example, in Figure
27.2 the picker on the left is displaying January 2008 and has its adjustWeeks attribute
set to false. The one on the right, on the other hand, has its adjustWeeks attribute set to
true and, as a result, the second week of February 2008 is not shown.
Figure 27.2. Different values of adjustWeeks
<sx:datetimepicker
adjustWeeks="true"
displayFormat="MM/dd/yyyy"
toggleType="explode" />
You can view the example by directing your browser to this URL.
http://localhost:8080/app27a/DateTimePicker.action
The tabbedpanel tag renders a tabbed panel like the one in Figure 27.3. It can contain
as many panels as you want and each panel may be closable.
Figure 27.3. A tabbed panel
For example, the following tabbedpanel tag contains two div elements as its panels.
<sx:tabbedpanel id="test">
<sx:div label="Server Time" cssStyle="height:200px"
href="ServerTime.action">
Server Time
</sx:div>
<sx:div label="Closable" closable="true">
This pane can be closed.
</sx:div>
</sx:tabbedpanel>
http://localhost:8080/app27a/TabbedPanel.action
The textarea tag renders a sophisticated text editor. Figure 27.4 shows the textarea
tag used in a blog application.
http://localhost:8080/app27a/TextArea.action
autoComplete
dataFieldName String value in the The name of the field in the returned
name attribute JSON object that contains the data array
dropdownWidth integer the same as the The width of the dropdown in pixels
textbox
headerKey String The key for the first item in the list
headerValue String The value for the first item in the list
Note
Like other form tags, the autocompleter tag should be nested within a form. When the
user submits the form, two key/value pairs associated with the autocompleter will be sent
as request parameters. The key for the first request parameter is the value of the
autocompleter tag's name attribute. The key for the second request parameter is by
default the value of the name attribute plus the suffix Key. That is, if the value of the
name attribute is searchWord, the key of the second request parameter will be
searchWordKey. You can override the second key name using the keyName attribute.
The keyName attribute is the one that should be mapped with an action property. Its value
will be the value of the selected option.
The autocompleter tag also inherits the common specified in Chapter 5, "Form Tags."
Three examples illustrate the use of autocompleter. All examples use the
AutoCompleterSupport class in Listing 27.5.
Example 1
This example shows how you can populate an autocompleter by assigning a List to its list
attribute. The JSP in Listing 27.6 shows the autocompleter tag.
Listing 27.6. The AutoCompleter1.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<html>
<head>
<title>Auto Completer</title>
<sx:head/>
</head>
<body>
<s:form action="ShowSelection" theme="simple">
<sx:autocompleter name="carMake" list="carMakes"/>
<s:submit/>
</s:form>
</body>
</html>
You can test this example by directing your browser to this URL:
http://localhost:8080/app27a/AutoCompleter1.action
When the containing form is submitted, the selected option will be sent as the request
parameter carMakeKey.
Example 2
This example shows how to populate an autocompleter by assigning a JSON object. The
location of the server that returns the object must be assigned to its href attribute and, for
security reasons, it must be the same location as the origin of the page.
...
['key-n','value-n']
]
http://localhost:8080/app27a/AutoCompleter2.action
Example 3
This example is similar to Example 2 and the JSP is shown in Listing 27.9.
The difference between this one and Example 2 is the format of the JSON object. For this
example, the JSON object contains a property (make) that contains the list of options to
display. The format of the JSON object is as follows.
{
"make" : {
'key-1':'value-1',
'key-2':'value-2',
...
'key-n':'value-n'
}
}
You use the dataFieldName attribute to tell the autocompleter the name of the JSON
object's property that contains the options.
Listing 27.10 shows the JSP that formats the options as a JSON object.
Listing 27.10. CarMakesAsJSON2.jsp page
<%@ taglib prefix="s" uri="/struts-tags" %>
{
"make" : {
<s:iterator value="carMakes" status="status">
'<s:property/>':'<s:property/>'
<s:if test="!#status.last">,</s:if>
</s:iterator>
}
}
http://localhost:8080/app27a/AutoCompleter3.action
gridIconSrcC String Image source for under child item child icons
gridIconSrcX String Image source for grid for sole root item
gridIconSrcY String Image source for grid for last root item
toggle String fade The toggle property. Possible values are fade
or explode.
The tree tag also inherits the common attributes specified in Chapter 5, "Form Tags."
Example 1
This example shows how to build a tree statically, by adding all nodes to the page. This is a
simple example that is pretty much self-explanatory. The Tree1.jsp page in Listing
27.11 shows the tree and treenode tags used for the tree.
http://localhost:8080/app27a/Tree1.action
Example 2
This example shows how you can construct a tree dynamically. At minimum, the tree tag
must have the following attributes: rootNode, nodeTitleProperty, nodeIdProperty,
childCollectionProperty. In addition, you must also create a model object to back up your
view.
The Tree2 action, the action for this example, is associated with the TreeSupport action
class in Listing 27.12. The class provides the rootNode property that maps to the
rootNode attribute of the tree tag.
In this example, a tree node is represented by a Node object. The Node class is shown in
Listing 27.13. It is a simple JavaBean class with three properties, id, title, and
children. The children property returns the children for the tree node. A static counter is
used so that it does not loop indefinitely.
The Tree2.jsp in Listing 27.14 shows the JSP with a tree tag used to construct a tree
dynamically. The tree tag also has its selectedNotifyTopics assigned a nodeSelected
topic to indicate to Dojo that selecting a node must publish the topic. A JavaScript function
subscribes to the topic.
The JavaScript function in Tree2.jsp will be executed every time a node is selected. It will
receive a JavaScript object that has a node property. In the example, the function simply
prints the node title.
http://localhost:8080/app27a/Tree2.action
The constructed tree is shown in Figure 27.7. Click a node and you'll see an alert box
displaying the node title.
The root element of a struts.xml file is struts. This section explains elements that may
appear under the struts element, either directly or indirectly. The following elements can be
direct sub-elements of <struts>.
• package
• include
• bean
• constant
An action element is nested within a package element and represents an action. Its
attributes are listed in Table A.1. Note that the name attribute is required.
<action name="MyAction">
An action that does not specify an action class will be given an instance of the default action
class.
If an action has a non-default action class, however, you must specify the fully class name
using the class attribute. In addition, you must also specify the name of the action method,
which is the method in the action class that will be executed when the action is invoked.
Here is an example.
If the class attribute is present but the method attribute is not, execute is assumed for
the method name. In other words, the following action elements mean the same thing.
Use this element to instruct Struts either to create a bean or have a bean's static methods
available for use by the application. The attributes that may appear in this element are
listed in Table A.2. Only class, indicated by an asterisk, is required.
scope The bean scope. Allowable values are default, singleton, request, session, and
thread.
Table A.2. bean element attributes
Attribute Description
The constant element is used to override a value in the default.properties file. By using a
constant element, you may not need to create a struts.properties file. The attributes for
this element are given in Table A.3. Both the name and value attributes are required.
For example, the struts.devMode setting determines whether or not the Struts application
is in development mode. By default, the value is false, meaning the application is not in
development mode. The following constant element sets struts.devMode to true.
This element must appear under a package element and specifies the default action that
will be invoked if no matching for a URI is found for that package. It has a name attribute
that specifies the default action. For example, this default-action-ref element indicates
that the Main action should be invoked for any URI with no matching action.
<default-action-ref name="Main"/>
This element must appear under a package element and specifies the default interceptor or
interceptor stack to be used for an action in that package that does not specify any
interceptor. The name attribute is used to specify an interceptor or interceptor stack. For
example, the struts-default package in the struts-default.xml file defines the following
default-interceptor-ref element.
<default-interceptor-ref name="defaultStack"/>
result* Specifies a result that will be executed if an exception is caught. The result
may be in the same action or in the global-results element.
You can nest one or more exception-mapping elements under your action declaration. For
example, the following exception-mapping element catches all exceptions thrown by the
User_save action and executes the error result.
<global-results>
<result name="error">/jsp/Error.jsp</result>
<result name="sqlError">/jsp/SQLError.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.sql.SQLException"
result="sqlError"/>
<exception-mapping exception="java.lang.Exception"
result="error"/>
</global-exception-mappings>
The Exception interceptor handles all exceptions caught. For each exception caught, the
interceptor adds these two objects to the Value Stack.
See Chapter 3, "Actions and Results" to learn how to handle these objects.
A global-results element must appear under a package element and specifies global
results that will be executed if an action cannot find a result locally. For example, the
following global-results element specifies two result elements.
<global-results>
<result name="error">/jsp/Error.jsp</result>
<result name="sqlError">/jsp/SQLError.jsp</result>
</global-results>
A large application may have many packages. In order to make the struts.xml file easier to
manage for a large application, you can divide it into smaller files and use include elements
to reference the files. Each file would ideally include a package or related packages and is
referred to by using the include element's file attribute. An include element must appear
directly under <struts>.
Each module.xml file would have the same DOCTYPE element and a struts root element.
Here is an example:
For instance, the following interceptor element registers the File Upload interceptor.
<interceptor name="fileUpload"
class="org.apache.struts.interceptor.FileUploadInterceptor"/>
This element is used to reference a registered interceptor and can appear either under an
interceptor-stack element or an action element. If it appears under an interceptor-
stack element, the interceptor-ref element specifies an interceptor that will become part
of the interceptor stack. If it appears under an action element, it specifies an interceptor
that will be used to process the action.
You use its name attribute to refer to a registered interceptor. For instance, the following
configuration registers four interceptors and applies them to the Product_save action.
With most Struts application having multiple action elements, repeating the list of
interceptors for each action can be a daunting task. In order to alleviate this problem,
Struts allows you to create interceptor stacks that group interceptors. Instead of referencing
interceptors from within each action element, you can reference the interceptor stack
instead.
For instance, six interceptors are often used in the following orders: exception,
servletConfig, prepare, checkbox, params, and conversionError. Rather than
referencing them again and again in your action declarations, you can create an interceptor
stack like this:
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
An interceptors element must appear directly under a package element and registers
interceptors for that package. For example, the following interceptors element registers
two interceptors, validation and logger.
For the sake of modularity, Struts actions are grouped into packages, which can be thought
of as modules. A struts.xml file can have one or many package elements. The attributes
for this element are given in Table A.6.
name* The package name that must be unique throughout the struts.xml file.
A package element must specify a name attribute and its value must be unique
throughout the struts.xml file. It may also specify a namespace attribute. If namespace
is not present, the default value "/" will be assumed. If the namespace attribute has a non-
default value, the namespace must be added to the URI that invokes the actions in the
package. For example, the URI for invoking an action in a package with a default
namespace is this:
/context/actionName.action
To invoke an action in a package with a non-default namespace, you need this URI:
/context/namespace/actionName.action
A package element almost always extends the struts-default package defined in struts-
default.xml. The latter is the default configuration file included in the Struts core JAR and
defines the standard interceptors and result types. A package that extends struts-default
can use the interceptors and result types without re-registering them. The content of the
struts-default.xml file is given in the next section.
The param element can be nested within another element such as action, result-type,
and interceptor to pass a value to the enclosing object. The param element has a name
attribute that specifies the name of the parameter. The format is as follows:
<param name="property">value</param>
Used within an action element, param can be used to set an action property. For example,
the following param element sets the siteId property of the action.
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
A result element corresponds to the return value of an action method. Because an action
method may return different values for different situations, an action element may have
several result elements, each of which corresponds to a possible return value of the action
method. This is to say, if a method may return "success" and "input," you must have two
result elements. The attributes for this element are listed in Table A.7.
name The result name, associated with the action method's return value.
For instance, the following action element contains two result elements.
This element registers a result type for a package and must appear directly under a result-
types element. The attributes for this element are given in Table A.8.
Table A.8. result-type element attributes
Attribute Description
default Specifies whether or not this is the default result type for the package.
For instance, these two result-type elements register the Dispatcher and FreeMarket result
types in the struts-default package. Note that the default attribute of the first result-
type element is set to true.
This element groups result-type elements and must appear directly under a package
element. For example, this result-types element groups three result types.
<result-types>
<result-type name="chain" class="..."/>
<result-type name="dispatcher" class="..." default="true"/>
<result-type name="freemarker" class="..."/>
</result-types>
The struts-default.xml file is the default configuration file included in the Struts core JAR
and defines the standard interceptors and result types. A package that extends struts-
default can use the interceptors and result types without re-registering them. This file is
shown in Listing A.1.
Listing A.1. The struts-default.xml file
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="struts-default">
<result-types>
<result-type name="chain"
class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher"
class="org.apache.struts2.dispatcher.ServletDispatcherResult"
default="true"/>
<result-type name="freemarker"
class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader"
class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect"
class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirect-action"
class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream"
class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity"
class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt"
class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plaintext"
class="org.apache.struts2.dispatcher.PlainTextResult"/>
</result-types>
<interceptors>
<interceptor name="alias"
class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="autowiring"
class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor
"/>
<interceptor name="chain"
class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<interceptor name="conversionError"
class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
<interceptor name="createSession"
class="org.apache.struts2.interceptor.CreateSessionInterceptor"/>
<interceptor name="debugging"
class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor"/>
<interceptor name="external-ref"
class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
<interceptor name="execAndWait"
class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception"
class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<interceptor name="fileUpload"
class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<interceptor name="i18n"
class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
<interceptor name="logger"
class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
<interceptor name="model-driven"
class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="scoped-model-driven"
class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<interceptor name="params"
class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<interceptor name="prepare"
class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
<interceptor name="static-params"
class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="scope"
class="org.apache.struts2.interceptor.ScopeInterceptor"/>
<interceptor name="servlet-config"
class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="sessionAutowiring"
class="org.apache.struts2.spring.interceptor.SessionContextAutowiringIntercep
tor"/>
<interceptor name="timer"
class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
<interceptor name="token"
class="org.apache.struts2.interceptor.TokenInterceptor"/>
<interceptor name="token-session"
class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
<interceptor name="validation"
class="com.opensymphony.xwork2.validator.ValidationInterceptor"/>
<interceptor name="workflow"
class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
<interceptor name="store"
class="org.apache.struts2.interceptor.MessageStoreInterceptor"/>
<interceptor name="checkbox"
class="org.apache.struts2.interceptor.CheckboxInterceptor"/>
<interceptor name="profiling"
class="org.apache.struts2.interceptor.ProfilingActivationInterceptor"/>
<default-interceptor-ref name="defaultStack"/>
</package>
</struts>
You may have a struts.properties file in the WEB-INF/classes file to override configuration
settings defined in the default.properties file.
struts.i18n.encoding = UTF-8
struts.objectFactory
struts.objectFactory.spring.autoWire = name
The auto-wiring logic when using the SpringObjectFactory. Valid values are name (the
default), type, auto, and constructor.
struts.objectFactory.spring.useClassCache = true
struts.objectTypeDeterminer
Specifies the object type determiner. The value must be an implementation of
com.opensymphony.xwork2.util.ObjectTypeDeterminer. Shorthand notations such as tiger
or notiger are supported.
struts.multipart.parser=Jakarta
struts.multipart.saveDir
The default save directory for file upload. The default value is the directory indicated by
javax.servlet.context.tempdir.
struts.multipart.maxSize = 2097152
struts.custom.properties
struts.mapper.class
The action mapper to handle how request URLs are mapped to and from actions. The
default value is org.apache.struts2.dispatcher.mapper.DefaultActionMapper.
struts.action.extension = action
struts.serve.static = true
Indicates whether or not Struts should serve static content from inside its JAR. A value of
false indicates that the static content must be available at <contextPath>/struts.
struts.serve.static.browserCache = true
Indicates if the filter dispatcher should write out headers for static contents that will be
cached by web browsers. A value of true is suitable for development mode. This key will be
ignored if struts.serve.static is false.
struts.enable.DynamicMethodInvocation = true
Indicates if dynamic method invocation is enabled. The default value is true, but for security
reasons its value should be false. Dynamic method invocation is discussed in Chapter 2.
struts.enable.SlashesInActionNames = false
struts.tag.altSyntax = true
Indicates if the alternative expression evaluation syntax that requires %{ ... } is allowed.
struts.devMode = false
Indicates if development mode should be enabled. When the value is true, Struts will reload
the application struts.xml file, validation files, and resource bundles on every request, which
means you do not need to reload the application if any of these files changes. In addition, a
value of true will raise the level of debug or ignorable problems to errors. For example, in
development mode a form field with no matching action property will throw an exception. In
production mode, it will be ignored.
struts.ui.theme = xhtml
struts.ui.templateDir = template
struts.ui.templateSuffix = ftl
The default template type. Other values in addition to ftl (FreeMarker) are vm (Velocity) and
jsp (JSP).
struts.configuration.xml.reload=false
struts.velocity.configfile = velocity.properties
struts.velocity.contexts
A comma separated list of VelocityContext class names to chain to the
StrutsVelocityContext.
struts.velocity.toolboxlocation
struts.url.http.port = 80
struts.url.https.port = 443
struts.custom.i18n.resources
struts.dispatcher.parametersWorkaround = false
struts.freemarker.manager.classname
struts.xslt.nocache = false
struts.configuration.files = struts-default.xml,struts-plugin.xml,struts.xml
struts.mapper.alwaysSelectFullNamespace=false
Indicates if Struts should select the namespace to be everything before the last slash.
Appendix B. The JSP Expression Language
OGNL is the expression language used with the Struts custom tags. However, there are
cases whereby the JSP Expression Language (EL) can help. For example, the JSP EL
provides shorter syntax for printing a model object than what the property tag and OGNL
offer. With the JSP EL, instead of this
<s:property value="serverValue"/>
${serverValue}
In addition, there's no easy way to use Struts custom tags to print a request header. With
EL, it's easy. For instance, the following EL expression prints the value of the host header:
${header.host}
The EL that was adopted into JSP 2.0 first appeared in the JSP Standard Tag Library (JSTL)
1.0 specification. JSP 1.2 programmers could use the language by importing the standard
libraries into their applications. JSP 2.0 developers can use the EL without JSTL. However,
JSTL also provides other libraries useful for JSP page authoring.
${expression}
For example, to write the expression x+y, you use the following construct:
${x+y}
It is also common to concatenate two expressions. A sequence of expressions will be
evaluated from left to right, coerced to Strings, and concatenated. If a+b equals 8 and
c+d equals 10, the following two expressions produce 810:
${a+b}${c+d}
<my:tag someAttribute="${expression}"/>
Reserved Words
The following words are reserved and must not be used as identifiers:
or ne le false empty
The return type of an EL expression can be any type. If an EL expression results in an object
that has a property, you can use the [] or . operators to access the property. The [] and .
operators function similarly; [] is a more generalized form, but. provides a nice shortcut.
To access a scoped object's property, you use one of the following forms:
${object["propertyName"]}
${object.propertyName}
However, you can only use the first form (using the [] operator] if propertyName is not a
valid Java variable name.
For instance, the following two EL expressions can be used to access the HTTP header host
in the implicit object header.
${header["host"]}
${header.host}
However, to access the accept-language header, you can only use the [] operator
because accept-language is not a legal Java variable name. Using the . operator to access
it will throw an exception.
If an object's property happens to return another object that in turn has a property, you can
use either [] or . to access the property of the second object. For example, the
pageContext implicit object represents the PageContext object of the current JSP. It has
the request property, which represents the HttpServletRequest object. The
HttpServletRequest object has the servletPath property. The following expressions are
equivalent and result in the value of the servletPath property of the HttpServletRequest
object in pageContext:
${pageContext["request"]["servletPath"]}
${pageContext.request["servletPath"]}
${pageContext.request.servletPath}
${pageContext["request"].servletPath}
An EL expression is evaluated from left to right. For an expression of the form expr-
a[expr-b], here is how the EL expression is evaluated:
You can use either the . operator or the [] operator to access a bean's property. Here are
the constructs:
${beanName["propertyName"]}
${beanName.propertyName}
For example, to access the property called secret on a bean named myBean, you use the
following expression:
${myBean.secret}
If the property is an object that in turn has a property, you can access the property of the
second object too, again using the . or [] operator. Or, if the property is a Map, a List, or an
array, you can use the same rule explained in the preceding section to access the Map's
values or the members of the List or the element of the array.
EL Implicit Objects
From a JSP, you can use JSP scripts to access JSP implicit objects. However, from a script-
free JSP page, it is impossible to access these implicit objects. The EL allows you to access
various objects by providing a set of its own implicit objects. The EL implicit objects are
listed in Table B.1.
param A Map containing all request parameters with the parameters names
as the keys. The value for each key is the first parameter value of the
specified name. Therefore, if there are two request parameters with
the same name, only the first can be retrieved using the param object.
For accessing all parameter values that share the same name, use the
params object instead.
Table B.1. The EL Implicit Objects
Object Description
paramValues A Map containing all request parameters with the parameter names
as the keys. The value for each key is an array of strings containing all
the values for the specified parameter name. If the parameter has
only one value, it still returns an array having one element.
header A Map containing the request headers with the header names as the
keys. The value for each key is the first header of the specified header
name. In other words, if a header has more than one value, only the
first value is returned. To obtain multi-value headers, use the
headerValues object instead.
headerValues A Map containing all request headers with the header names as the
keys. The value for each key is an array of strings containing all the
values for the specified header name. If the header has only one
value, it returns a one-element array.
cookie A Map containing all Cookie objects in the current request object. The
cookies' names are the Map's keys, and each key is mapped to a
Cookie object.
applicationScope A Map that contains all attributes in the ServletContext object with
the attribute names as the keys.
sessionScope A Map that contains all the attributes in the HttpSession object in
which the attribute names are the keys.
pageScope A Map that contains all attributes with the page scope. The attributes'
names are the keys of the Map.
request javax.servlet.http.HttpServletRequest
response javax.servlet.http.HttpServletResponse
out javax.servlet.jsp.JspWriter
session javax.servlet.http.HttpSession
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
pageContext javax.servlet.jsp.PageContext
page javax.servlet.jsp.HttpJspPage
exception java.lang.Throwable
For example, you can obtain the current ServletRequest object using one of the following
expressions:
${pageContext.request}
${pageContext["request"]
And, the request method can be obtained using one of the following expressions:
${pageContext["request"]["method"]}
${pageContext["request"].method}
${pageContext.request["method"]}
${pageContext.request.method}
Request parameters are accessed more frequently than other implicit objects; therefore,
two implicit objects, param and paramValues, are provided. The param and paramValues
implicit objects are discussed in the sections "param" and "paramValues."
initParam
The initParam implicit object is used to retrieve the value of a context parameter. For
example, to access the context parameter named password, you use the following
expression:
${initParam.password}
or
${initParam["password"]
param
The param implicit object is used to retrieve a request parameter. This object represents a
Map containing all the request parameters. For example, to retrieve the parameter called
userName, use one of the following:
${param.userName}
${param["userName"]}
paramValues
You use the paramValues implicit object to retrieve the values of a request parameter.
This object represents a Map containing all request parameters with the parameters' names
as the keys. The value for each key is an array of strings containing all the values for the
specified parameter name. If the parameter has only one value, it still returns an array
having one element. For example, to obtain the first and second values of the
selectedOptions parameter, you use the following expressions:
${paramValues.selectedOptions[0]}
${paramValues.selectedOptions[1]}
header
The header implicit object represents a Map that contains all request headers. To retrieve
a header value, you use the header name as the key. For example, to retrieve the value of
the accept-language header, use the following expression:
${header["accept-language"]}
If the header name is a valid Java variable name, such as connection, you can also use the
. operator:
${header.connection}
headerValues
The headerValues implicit object represents a Map containing all request headers with the
header names as the keys. Unlike header, however, the Map returned by the
headerValues implicit object returns an array of strings. For example, to obtain the first
value of the accept-language header, use this expression:
${headerValues["accept-language"][0]}
cookie
You use the cookie implicit object to retrieve a cookie. This object represents a Map
containing all cookies in the current HttpServletRequest object. For example, to retrieve
the value of a cookie called jsessionid, use the following expression:
${cookie.jsessionid.value}
${cookie.jsessionid.path}
You use the applicationScope implicit object to obtain the value of an application-scoped
variable. For example, if you have an application-scoped variable called myVar, you use
this expression to access the attribute:
${applicationScope.myVar}
Arithmetic Operators
• Addition (+)
• Subtraction (-)
• Multiplication (*)
• Division (/ and div)
• Remainder/modulo (% and mod)
The division and remainder operators have two forms, to be consistent with XPath and
ECMAScript.
Note that an EL expression is evaluated from the highest to the lowest precedence, and
then from left to right. The following are the arithmetic operators in the decreasing lower
precedence:
* / div % mod
+-
This means that *, /, div, %, and mod operators have the same level of precedence, and +
has the same precedence as - , but lower than the first group. Therefore, the expression
${1+2*3}
Relational Operators
For instance, the expression ${3==4} returns false, and ${"b"<"d"} returns true.
Logical Operators
${statement? A:B}
If statement evaluates to true, the output of the expression is A. Otherwise, the output is
B.
For example, you can use the following EL expression to test whether the HttpSession
object contains the attribute called loggedIn. If the attribute is found, the string "You have
logged in" is displayed. Otherwise, "You have not logged in" is displayed.
The empty operator is used to examine whether a value is null or empty. The following is
an example of the use of the empty operator:
${empty X}
If X is null or if X is a zero-length string, the expression returns true. It also returns true if
X is an empty Map, an empty array, or an empty collection. Otherwise, it returns false.
On the other hand, in some circumstances you'll probably want to disable the EL in your
applications. For example, you'll want to do so if you are using a JSP 2.0-compliant
container but are not ready yet to upgrade to JSP 2.0. In this case, you can disable the
evaluation of EL expressions.
This section discusses how to enforce script-free JSPs and how to disable the EL in JSP 2.0.
To disable scripting elements in JSPs, you use the jsp-property-group element with two
subelements: url-pattern and scripting-invalid. The url-pattem element defines the
URL pattern to which scripting disablement will apply. Here is how you disable scripting in
all JSPs in an application:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>
Note
There can be only one jsp-config element in the deployment descriptor. If you have
specified a jsp-property-group for deactivating the EL, you must write your jsp-
property-group for disabling scripting under the same jsp-config element.
In some circumstances, such as when you need to deploy JSP 1.2 applications in a JSP 2.0
container, you may want to deactivate EL evaluation in a JSP. When you do so, an
occurrence of the EL construct will not be evaluated as an EL expression. There are two
ways to deactivate EL evaluation in a JSP.
First, you can set the isELIgnored attribute of the page directive to true, such as in the
following:
The default value of the isELIgnored attribute is false. Using the isELIgnored attribute is
recommended if you want to deactivate EL evaluation in one or a few JSPs.
Second, you can use the jsp-property-group element in the deployment descriptor. The
jsp-property-group element is a subelement of the jsp-config element. You use jsp-
property-group to apply certain settings to a set of JSPs in the application.
To use the jsp-property-group element to deactivate the EL evaluation, you must have
two subelements: url-pattern and el-ignored. The url-pattern element specifies the URL
pattern to which the EL deactivation will apply. The el-ignored element must be set to
true.
As an example, here is how you deactivate the EL evaluation in a JSP named noEl.jsp.
<jsp-config>
<jsp-property-group>
<url-pattern>/noEl.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>
You can also deactivate the EL evaluation in all the JSPs in an application by assigning *.jsp
to the url-pattern element, as in the following:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>
The EL evaluation in a JSP will be deactivated if either the isELIgnored attribute of its
page directive is set to true or its URL matches the pattern in the jsp-property-group
element whose el-ignored subelement is set to true. For example, if you set the page
directive's isELIgnored attribute of a JSP to false but its URL matches the pattern of JSPs
whose EL evaluation must be deactivated in the deployment descriptor, the EL evaluation of
that page will be deactivated.
In addition, if you use a deployment descriptor that is compliant to Servlet 2.3 or earlier,
the EL evaluation is already disabled by default, even though you are using a JSP 2.0
container.
Summary
The EL is one of the most important features in JSP 2.0. It can help you write shorter and
more effective JSPs, as well as helping you author script-free pages. In this chapter you
have seen how to use the EL to access JavaBeans and implicit objects. Additionally, you
have seen how to use EL operators. In the last section of this chapter, you learned how to
use the application settings related to the EL in JSP 2.0 and later versions.
Appendix C. Annotations
A new feature in Java 5, annotations are notes in Java programs to instruct the Java
compiler to do something. You can annotate any program elements, including Java
packages, classes, constructors, fields, methods, parameters, and local variables. Java
annotations are defined in JSR 175 (http://www.jcp.org/en/jsr/detail?id=175).
Java 5 provided three standard annotations and four standard meta-annotations. Java 6
added dozens of others.
This appendix is for you if you are new to annotations. It tells you everything you need to
know about annotations and annotation types. It starts with an overview of annotations,
and then teaches you how to use the standard annotations in Java 5 and Java 6. It
concludes with a discussion of custom annotations.
An Overview of Annotations
Annotations are notes for the Java compiler. When you annotate a program element in a
source file, you add notes to the Java program elements in that source file. You can
annotate Java packages, types (classes, interfaces, enumerated types), constructors,
methods, fields, parameters, and local variables. For example, you can annotate a Java
class so that any warnings that the javac program would otherwise issue be suppressed.
Or, you can annotate a method that you want to override to get the compiler to verify that
you are really overriding the method, not overloading it. Additionally, you can annotate a
Java class with the name of the developer. In a large project, annotating every Java class
can be useful for the project manager or architect to measure the productivity of the
developers. For example, if all classes are annotated this way, it is easy to find out who is
the most or the least productive programmer.
The Java compiler can be instructed to interpret annotations and discard them (so those
annotations only live in source files) or include them in resulting Java classes. Those that
are included in Java classes may be ignored by the Java virtual machine, or they may be
loaded into the virtual machine. The latter type is called runtime-visible and you can use
reflection to inquire about them.
When studying annotations, you will come across these two terms very often: annotations
and annotation types. To understand their meanings, it is useful to first bear in mind that an
annotation type is a special interface type. An annotation is an instance of an annotation
type. Just like an interface, an annotation type has a name and members. The information
contained in an annotation takes the form of key/value pairs. There can be zero or multiple
pairs and each key has a specific type. It can be a String, int, or other Java types.
Annotation types with no key/value pairs are called marker annotation types. Those with
one key/value pair are often referred to single-value annotation types.
Annotation Syntax
In your code, you use an annotation differently from using an ordinary interface. You
declare an annotation type by using this syntax.
@AnnotationType
or
@AnnotationType(elementValuePairs)
The first syntax is for marker annotation types and the second for single-value and multi-
value types. It is legal to put white spaces between the at sign (@) and annotation type, but
this is not recommended.
For example, here is how you use the marker annotation type Deprecated:
@Deprecated
And, this is how you use the second element for multi-value annotation type Author:
@Author(firstName="Ted",lastName="Diong")
There is an exception to this rule. If an annotation type has a single key/value pair and the
name of the key is value, then you can omit the key from the bracket. Therefore, if the
fictitious annotation type Stage has a single key named value, you can write
@Stage(value=1)
or
@Stage(1)
The Annotation Interface
Know that an annotation type is a Java interface. All annotation types are subinterfaces of
the java.lang.annotation.Annotation interface. It has one method, annotationType,
that returns an java.lang.Class object.
In addition, any implementation of Annotation will override the equals, hashCode, and
toString methods from the java.lang.Object class. Here are their default
implementations.
Returns true if object is an instance of the same annotation type as this one and all
members of object are equal to the corresponding members of this annotation.
Returns the hash code of this annotation, which is the sum of the hash codes of its
members
Returns a string representation of this annotation, which typically lists all the key/value
pairs of this annotation.
You will use this class when learning custom annotation types later in this chapter.
Standard Annotations
Java 5 comes with three built-in annotations, all of which are in the java.lang package:
Override, Deprecated, and SuppressWarnings. They are discussed in this section.
Override
Override is a marker annotation type that can be applied to a method to indicate to the
compiler that the method overrides a method in a superclass. This annotation type guards
the programmer against making a mistake when overriding a method.
Suppose, you want to extend Parent and override its calculate method. Here is a subclass
of Parent:
The Child class compiles. However, the calculate method in Child does not override the
method in Parent because it has a different signature, namely it returns and accepts ints
instead of floats. In this example, a programming mistake like this is easy to spot because
you can see both the Parent and Child classes. However, you are not always this lucky.
Sometimes the parent class is buried somewhere in another package. This seemingly trivial
error could be fatal because when a client class calls the calculate method on an Child
object and passes two floats, the method in the Parent class will be invoked and a wrong
result will be returned.
Using the Override annotation type will prevent this kind of mistake. Whenever you want
to override a method, declare the Override annotation type before the method:
This time, the compiler will generate a compile error and you'll be notified that the
calculate method in Child is not overriding the method in the parent class.
It is clear that @Override is useful to make sure programmers override a method when
they intend to override it, and not overload it.
Deprecated
If you use or override a deprecated method, you will get a warning at compile time. For
example, Listing C.2 shows the DeprecatedTest2 class that uses the serve method in
DeprecatedTest.
On top of that, you can use @Deprecated to mark a class or an interface, as shown in
Listing C.3.
SuppressWarnings
You use it by passing a String array that contains warnings that need to be suppressed. Its
syntax is as follows.
@SuppressWarnings(value={string-1, ..., string-n})
where string-1 to string-n indicate the set of warnings to be suppressed. Duplicate and
unrecognized warnings will be ignored.
• unchecked. Give more detail for unchecked conversion warnings that are mandated
by the Java Language Specification.
• path. Warn about nonexistent path (classpath, sourcepath, etc) directories.
• serial. Warn about missing serialVersionUID definitions on serializable classes.
• finally. Warn about finally clauses that cannot complete normally.
• fallthrough. Check switch blocks for fall-through cases, namely cases, other than
the last case in the block, whose code does not include a break statement, allowing
code execution to "fall through" from that case to the next case. As an example, the
code following the case 2 label in this switch block does not contain a break
statement:
switch (i) {
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
// falling through
case 3:
System.out.println("3");
}
@SuppressWarnings(value={"unchecked","serial"})
public class SuppressWarningsTest implements Serializable {
public void openFile() {
ArrayList a = new ArrayList();
File file = new File("X:/java/doc.txt");
}
}
Standard Meta-Annotations
Meta annotations are annotations that are applied to annotations. There are four meta-
annotation types that come standard with Java 5 that are used to annotate annotations;
they are Documented, Inherited, Retention, and Target. All the four are part of the
java.lang.annotation package. This section discusses these annotation types.
Documented
For example, the Override annotation type is not annotated using Documented. As a
result, if you use Javadoc to generate a class whose method is annotated @Override, you
will not see any trace of @Override in the resulting document.
For instance, Listing C.5 shows the OverrideTest2 class that uses @Override to
annotate the toString method.
On the other hand, the Deprecated annotation type is annotated @Documented. Recall
that the serve method in the DeprecatedTest class in Listing C.2 is annotated
@Deprecated. Now, if you use Javadoc to generate the documentation for
OverrideTest2, the details of the serve method in the documentation will also include
@Deprecated, like this:
serve
@Deprecated
public void serve()
Inherited
You use Inherited to annotate an annotation type so that any instance of the annotation
type will be inherited. If you annotate a class using an inherited annotation type, the
annotation will be inherited by any subclass of the annotated class. If the user queries the
annotation type on a class declaration, and the class declaration has no annotation of this
type, then the class's parent class will automatically be queried for the annotation type. This
process will be repeated until an annotation of this type is found or the root class is
reached.
Check out the section "Custom Annotation Types" on how to query an annotation
type.
Retention
@Retention indicates how long annotations whose annotated types are annotated
@Retention are to be retained. The value of @Retention can be one of the members of the
java.lang.annotation.RetentionPolicy enum:
@Retention(value=SOURCE)
public @interface SuppressWarnings
Target
Target indicates which program element(s) can be annotated using instances of the
annotated annotation type. The value of Target is one of the members of the
java.lang.annotation.ElementType enum:
As an example, the Override annotation type declaration is annotated the following Target
annotation, making Override can only be applied to method declarations.
@Target(value=METHOD)
You can have multiple values in the Target annotation. For example, this is from the
declaration of SuppressWarnings:
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String firstName();
String lastName();
boolean internalEmployee();
}
@Author(firstName="firstName", lastName="lastName",
internalEmployee=true|false)
For example, the Test1 class in Listing C.7 is annotated Author.
The next subsection "Using Reflection to Query Annotations" shows how the
Author annotations can be of good use.
Returns this element's annotation for the specified annotation type, if present. Otherwise,
returns null.
Indicates whether an annotation for the specified type is present on this class
When run, you will see the following message in your console: