(Some of the introductory material is adapted from the Apache Struts website.)
With introductions out of the way, we will dig in and start migrating Struts 1
code to Struts 2. In this session, we'll keep it simple with a "Hello World"
example, In later sessions, we move up to a realistic, data-driven
application.
Almost all of the source code is presented as we go. The complete source code
for the tutorial is available from the Struts University site.
What is Struts 2?
Apache Struts is a free open-source framework for creating Java web
applications. Today, the Apache Struts Project offers two major versions of
the Struts framework.
Struts 1 is recognized as the most popular web application framework for Java.
The 1.x framework is mature, well-documented, and widely supported. More teams
now use Struts 1 than all other Java web frameworks combined. Struts 1 is the
best choice for teams who value proven solutions to common problems.
Struts 2 was originally known as WebWork 2. After working independently for
several years, the WebWork and Struts communities joined forces to create
Struts 2. The new framework is the best choice for teams who value elegant
solutions to difficult problems.
1
resources.
(3) A tag library to help us create rich, responsive, form-based applications.
In Struts 2, all three components have been redesigned and enhanced, but the
same architectural hallmarks remain.
Smarter!
Improved Design - All Struts 2 classes are based on interfaces. Core
interfaces are HTTP independent.
Intelligent Defaults - Most configuration elements have a default value that
we can set and forget.
Enhanced Results - Unlike ActionForwards, Struts 2 Results can actually help
prepare the response.
Enhanced Tags - Struts 2 tags don't just output data, but provide stylesheet-
driven markup, so that we can create consistent pages with less code.
Easier!
Easy-to-test Actions - Struts 2 Actions are HTTP independent and can be tested
without resorting to mock objects.
POJO-ier!
POJO forms - No more ActionForms! We can use any JavaBean we like or put
properties directly on our Action classes. No need to use all String
properties!
2
POJO Actions - Any class can be used as an Action class. We don't even have to
implement an interface!
Is Struts 1 obsolete?
No. There is a robust and vibrant community of developers using Struts 1 in
production, and we expect that thousands of teams will continue to base new
projects on Struts 1, and continue to support existing projects, for many,
many years to come.
Of course, if you are starting a new project, and you have your choice of
versions, this would be an excellent time to consider whether you would like
to continue to use Struts 1 or whether it's time to try Struts 2.
First, we should add the Struts 2 JARs to our Struts 1 application's WEB-
INF/lib folder, and the corresponding elements to the application's web.xml
file.
The elements we need to add are
3
(1) The Struts 2 Filter
(2) A filter mapping
(3) The Spring Listener
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Struts 1 -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/classes/struts-config.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Tip: With filters, it's better for us to use "/*" as the URI pattern. The
default extension for Struts 2 actions can be set in the struts.properties
file (new to Struts 2). Prefix mapping is not supported in the default
distribution.
After we add the elements shown in the listing, there is one last bit of red
tape. By default, Struts 2 uses the Spring Framework to instantiate its
objects. The Spring listener expects there to be a Spring configuration file
4
loaded, even if it's empty. To satisfy Spring, we need to add an
"applicationContext.xml" file to our WEB-INF folder, like the one shown in the
listing.
<beans>
</beans>
Once the Struts 2 elements are added to a Struts 1 application, we can use
both versions together. The Struts 1 actions can handle the *.do URIs, and the
Struts 2 actions can handle the *.action URIs. So we can migrate one to the
other, until there's nothing left to "do"!
Let's compare the configurations for our simple Hello World application.
<form-bean
name="HelloForm"
type="forms.HelloForm">
</form-bean>
</form-beans>
<action-mappings>
<action path="/Hello"
name="HelloForm"
type="actions.HelloAction"
validate="false">
<forward name="success" path="/HelloPage.jsp"/>
</action>
</action-mappings>
<message-resources parameter="resources"/>
5
</struts-config>
As a Struts 1 application goes, the first listing seems concise. But, even so,
it's still longer than the Struts 2 listing.
In the Struts 1 listing, we map both an Action and ActionForm. In the Struts 2
listing, we map only the Action class. In Struts 2, the Action and ActionForm
are combined. Instead of declaring an ActionForm, we can place the message
property directly on the Struts 2 Action class.
6
"nickname" for a class. Some confusing attribute names, like "path", are
avoided altogether.
Comprehension. Other elements are streamlined to be more concise and easier to
understand. Verbose elements with redundant attributes take more effort to
create now, and they will be harder to understand later.
Under the heading "comprehension", a very big change in Struts 2 is the notion
of "Intelligent Defaults". Take for example the Struts 1 <forward> element and
the corresponding Struts 2 <result> element shown in the listing.
In the case of <result>, there are three "Intelligent Defaults" being set.
As shown by the listing, with the added information, the result element
becomes more verbose. But that's OK. The important thing is that the most
common <result> is the easiest to specify.
Yes. The Action classes become simpler in most ways. Struts 2 Action classes
can combine the utility of a Struts 1 ActionForm, so sometimes we will add
properties to capture form input.
Let's look at the ActionForm and Actions for our simple Hello World
application.
7
Listing: Struts 1 Hello ActionForm and Action class
package forms;
import org.apache.struts.action.ActionForm;
public class HelloForm extends ValidatorForm {
private String message;
public String getMessage() {
return message;
}
8
As seen in the listings, migrating an Action from Struts 1 to Struts 2 is
mainly a matter of simplifying the old code. Instead of storing the Message
property on a separate object, we can just add the property to the Action
class.
Tip: Unlike Struts 1 Actions, Struts 2 Actions are not singletons. Struts 2
Actions do not need to be thread-safe, and we can use member variables, if
desired.
To convert our Hello World Action class from Struts 1 to Struts 2, first we
(1) Update or remove imports. (A well-factored Struts 2 Action should not need
to refer to anything from the servlet.http package.)
(2) Move the Message property from the ActionForm to the Action, and remove
the ActionForm object completely. It is obsolete.
(3) Change the Struts 2 Action to extend ActionSupport. Extending
ActionSupport is optional, but it is usually helpful. In this case, we are
just using the predefined SUCCESS token.
(4) Remove the ActionForm cast and reference the Message property directly,
since it is now a property on the Action class.
(5) Remove the SUCCESS static, since it is already defined on ActionSupport.
To convert from Struts 1 to Struts 2 the Hello World server pages shown in the
listings, first we
9
(1) Replace the <%@ taglib @%> directive
(2) Change the <bean:write ... /> tag to a <s:property ... /> tag
As with the configuration and Action class, most of the changes help
streamline the server page. The <bean:write> tag needs to know the name of the
object. The <s:property> tag finds the property automatically. The name of the
ActionForm is no longer coupled to the page.
See the appendix for a table listing Struts 1 tags and Struts 2 equivalents.
In most cases, the Struts 2 tags are not just simple equivalents. The new tags
provide significant improvements over the Struts 1 libraries.
The listing indicates that we will have a resource bundle with the root name
"resources.properties" and alternate files named in the style
"resource_es.properties", where "es" is a standard locale code. Other valid
codes might include "ja" or "ru", or even "fr_CA", for Canadian French.
Both the resource bundle and the struts.properties file can be placed in the
classes directory of a web application, so that they can be read from the
classpath. (Other configurations are possible.)
For Hello World, the resource bundles, shown by the listing, are simple
enough.
resources.properties
message = Hello World
resources_es.properties
message = ¡Hola Mundo!
Since we are setting the "Hello" message in our Action, we can lookup the
appropriate message by key, and set the localized message to our form
property.
10
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
HelloForm input = (HelloForm) form;
- input.setMessage(MESSAGE);
+ input.setMessage(getResources(request).getMessage(MESSAGE));
return mapping.findForward(SUCCESS);
}
- public static final String MESSAGE = "Hello World!";
+ public static final String MESSAGE = "message";
public static final String SUCCESS = "success";
}
In the listing, the lines with "-" in the margin are being removed. The lines
marked "+" are being added.
For Struts 2, as the listing shows, the i18n syntax is a tad cleaner.
Tip: If Struts 1 can't find a message, it returns null. If Struts 2 can't find
a message, it returns the message key.
As with Struts 1, some of the Struts 2 tags will output localized messages,
based on a key passed to the tag. Right now, our page has "Hello World"
embedded in the title. Let's change the title so that it looks up the message,
just like the Action does.
11
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
- <title>Hello World!</title>
+ <title><s:text name="message"/></title>
</head>
<body>
<h2><s:property value="message"/></h2>
</body>
</html>
setLocale(request, locale);
return mapping.findForward(SUCCESS);
}
To kick off changing the locale, we need to add a link to our Struts 1 Locale
action. The listing shows a typical locale link.
In Struts 2, we just add the appropriate "magic" links to a page, and the
framework will change the locale automatically. An additional Action class is
12
not needed.
The key takeaway shown in the listing is that we need to pass "request_locale=
es" as a request parameter, where "es" can be any standard locale code. The
framework sees the "magic" parameter and updates the locale for the instant
session.
13
<!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="message">
<field-validator type="requiredstring">
<message key="requiredstring"/>
</field-validator>
</field>
</validators>
Right now, our Hello Actions are providing a default message. Let's add a data
entry form and validate the input to be sure there is message, then we
wouldn't need to set the message in Action class.
In Struts 1, the usual approach to adding an input form is to add another
mapping.
<action path="/HelloInput"
name="HelloForm"
forward="/HelloInput.jsp"
validate="false" />
<action path="/Hello"
name="HelloForm"
forward="/HelloPage.jsp"
validate="true"
input="/HelloInput.do" />
</action-mappings>
</struts-config>
Since we don't need the Action class anymore, we also changed the "Hello"
mapping in the listing to forward directly to our display page. Following the
new mappings, we can add a HelloInput.jsp to carry our form. The listing shows
our form markup for a HelloInput page.
Tip: We are using the Struts 1 HTML taglib on this page, so we need to import
the taglib into the JSP.
14
In Struts 2, its easy to reuse the same action mapping for the input form.
Built into Struts 2 is the capability to call an alternate method. In Struts
1, we can do this by using some type of "dispatch" Action. In Struts 2,
calling an alternate method is a first-class feature of the framework. The
listing shows how we can call alternate methods using wildcards.
As with the Struts 1 rendition, we can provide an input page to go with the
Struts 2 mapping. Since the Struts 2 tags provide their own markup, the input
form is concise, as shown by the listing.
In Struts 2, the base ActionSupport class has a special input method that does
nothing but return the token "input". If we call an "input" method, by design,
the framework will bypass validation. Usually, the only thing that happens on
input is that the framework prepares and displays an "input" page.
Most often, the input page will post back to the same action, but to the
default "execute" method this time. Some developers might use a "save" or
"submit" method instead of "execute", but the notion is the same. We use one
Action class method for input and another for submit.
Finally, we should update the resource bundle with localized error messages.
15
# Struts 1
errors.required={0} se requiere.
# Struts 2
requiredstring = ${getText(fieldName)} se requiere.
As shown in the listing, Struts 1 and Struts 2 take a different tact to using
replacement tokens in messages. In Struts 1, a numeric parameter is used (up
to 4). In Struts 2, we have access to the same expression language and
symbolic parameters that we use with the tags.
Not by a long shot, but in this brief tutorial, we did cover the basics:
mappings, Actions, tags, localization, and validation.
We converted a simple Hello World application from Struts 1 to Struts 2 by
moving Actions, pages, and configuration elements over, one at a time. We were
also able to share essential resources, like message bundles, between the
versions.
About the author. Ted Husted's speciality is building agile web applications
with open source products like Struts and iBATIS and helping others do the
same. His books include JUnit in Action, Struts in Action, and Professional
JSP Site Design. Ted provides onsite training to software development teams
through the United States. Visit www.StrutsMentor.com for details.
####
16