Anda di halaman 1dari 16

Copyright 2006 Ted Husted. All Rights Reserved.

Portions based on material


provided by the Apache Software Foundation under the Apache License <
http://apache.org/licenses/LICENSE-2.0.txt >.

Migrating to Apache Struts 2: A tutorial for Struts 1 Developers.


by Ted Husted

This tutorial helps Struts 1 developers become knowledgeable Struts 2


developers as quickly as possible by leveraging our hard-earned experience.

Over the course of the tutorial, we will migrate a Struts 1 application to


Struts 2, pointing out the similarities and differences as we go.
The tutorial is intended for readers who have already developed a web
application with Struts 1, or a similar framework, like Spring MVC. A working
knowledge about how Java web applications are built is needed to follow the
material presented in this tutorial.
Before we dig into the code, let's set the scene by answering a few baseline
questions:
* What is Struts 2?
* How are Struts 1 and Struts 2 alike?
* What's changed in Struts 2?
* Is Struts 1 obsolete?
* Is it difficult to migrate from Struts 1 to Struts 2?
* Why are "plain old Java objects" important?

(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.

How are Struts 1 and Struts 2 alike?


Both versions of Struts provide three key components.
(1) A "request" handler that maps Java classes to web application URIs.
(2) A "response" handler that maps logical names to server pages, or other web

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.

What's changed in Struts 2?


Struts 2 is designed to be simpler to use and closer to how Struts was always
meant to be. Some key changes are:

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.

First-class AJAX support - The AJAX theme gives interactive applications a


significant boost.
Stateful Checkboxes - Struts 2 checkboxes do not require special handling for
false values.
QuickStart - Many changes can be made on the fly without restarting a web
container.

Easier!
Easy-to-test Actions - Struts 2 Actions are HTTP independent and can be tested
without resorting to mock objects.

Easy-to-customize controller - Struts 1 lets us customize the request


processor per module, Struts 2 lets us customize the request handling per
action, if desired.

Easy-to-tweak tags - Struts 2 tag markup can be altered by changing an


underlying stylesheet. Individual tag markup can be changed by editing a
FreeMarker template. No need to grok the taglib API! Both JSP and FreeMarker
tags are fully supported.
Easy cancel handling - The Struts 2 Cancel button can go directly to a
different action.
Easy Spring integration - Struts 2 Actions are Spring-aware. Just add Spring
beans!

Easy plugins - Struts 2 extensions can be added by dropping in a JAR. No


manual configuration required!

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.

Is it difficult to migrate from Struts 1 to Struts 2?


Somewhat. The work itself is not difficult, but the amount of effort is non-
trivial. As this tutorial will show, migrating Actions and pages does take
time and effort. For existing applications that will not change, or change
much, migration may not be worth the effort. But, for applications that will
continue to change and grow, the time invested will be well spent. Struts 2 is
smarter, easier, and best of all, POJO-ier!

Why are POJOs important?


Being able to use Plain Old Java Objects with Struts 2 means that we don't
have to use "extra" objects just to placate the framework. One good example is
testing Struts Action classes outside of a container. In Struts 1, if an
Action class uses servlet state, we have to use a special mock object to
simulate the servlet. In Struts 2, we can simulate servlet state with a plain
old HashMap.

Another good example is transferring form input to an object. In Struts 1, the


Actions and tags expect us to use ActionForms with all String properties. In
Struts 2, we can use whatever object we want to capture input, including the
Acton class itself. Best of all, non-String properties are not a problem.

Where should a migration begin?


When we have an existing Struts 1 application to migrate, the simplest
approach is to add the Struts 2 JARs and migrate the application a page at a
time. We can use both versions in the same web application together. The
configuration and the tags have been overhauled for Struts 2, but web
application architectures can remain the same. Many changes are just a matter
of removing Struts 1 red tape that is no longer needed. Other changes are just
a matter of swapping one tag for another.
To get started, let's migrate a "Hello World" application from Struts 1 to
Struts 2. Initially, the application simply displays a welcome message. As
part of the migration, we will internationalize the application so that it can
display a message in two languages. Then, we will extend it again to add a
data entry form so we can input our own message and validate the data entry.
(Remember, the complete source code for the tutorial is available from the
Struts University site.)

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

Listing: A web.xml with both Struts 1 and Struts 2 enabled.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd ">
<web-app>
<!-- Struts 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>
<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>

<!-- Either version -->


<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<web-app>

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.

Listing: Application Context File Required by the Spring filter.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC
"-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd ">

<beans>
</beans>

Tip: If the Struts 1 application already uses Spring, then another


configuration file does not need to be added.

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"!

Is the Struts 2 configuration file different?


Yes. Compared to Struts 1, the Struts 2 configuration file is streamlined.
Fewer elements are configured, and the remaining elements need fewer
attributes. Even the element names are shorter. The factory default name for
Struts 1 is "struts-config.xml". The default for Struts 2 is just
"struts.xml".

Let's compare the configurations for our simple Hello World application.

Listing: Struts 1 Configuration for our "Hello World" application.


<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://struts.apache.org/dtds/struts-config_1_3.dtd ">
<struts-config>
<form-beans>

<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>

Listing: Struts 2 Configuration for our "Hello World" application.


<?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>
<include file="struts-default.xml"/>

<package name="hello-default" extends="struts-default">


<action name="Hello" class="actions.Hello">
<result>/Hello.jsp</result>
</action>
</package>
</struts>

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.

To convert a copy of our Hello World configuration from Struts 1 to Struts 2,


first we

(1) Replace the DTD

(2) Change <struts-config> to <struts>


(3) Add <include file="struts-default.xml"/>

(4) Remove the <form-beans> element

(5) Change <action-mappings> to <package name="hello-default" extends="struts-


default">
(6) Update each <action> element
To update each <action> element, we

(1) Remove from <action> the "name" attribute


(2) Change the <action> "path" attribute to "name", and "type" to "class"
(3) Change the <forward> element into a <result> element.

Why are there so many changes to the Struts configuration?


Three reasons: Obsolescence, consistency, and comprehension.

Obsolescence. Some elements, like <form-beans>, are obsolete in Struts 2. We


just plain don't need them anymore.
Consistency. Attribute names are applied consistently across the framework.
For example, the attribute "class" is used to indicate a true Java class, as
it does in the Spring and iBATIS frameworks. The attribute "type" identifies a

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.

Listing: A Struts 1 <forward> element and a corresponding Struts 2 <result>


element
<forward name="success" path="/Hello.jsp"/>
<result>/Hello.jsp</result>

In the case of <result>, there are three "Intelligent Defaults" being set.

(1) The <result> name defaults to "success".


(2) The <result> content defaults to "location" (e.g. path).

(3) The <result> type defaults to "dispatch".


In this one example, Intelligent Defaults save sixteen characters out of
forty-three (a 37% reduction). But, the real bonus is readability. At a
glance, the <result> element is much easier to read and understand.
Of course, we can override the defaults as needed. If the Action returned
"cancel", and we wanted to redirect instead of forward, we can specify an
alternate name and result type.

Listing: Selecting a different result type.


<result name="cancel" type="redirect">/Hello.jsp</result>

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.

Definition: "The Doctrine of Intelligent Defaults." When an essential value is


omitted, the system automatically provides a predefined value, eliminating the
need to explicitly qualify each and every aspect of a declaration. (Adapted
from the CULE Knowledgebase <
http://www.softwareperspectives.com/culeplace/Default.aspx?tabid=55&articleid=
4>)

Do the Action classes change too?

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;
}

public void setMessage(String message) {


this.message = message;
}
}
package actions;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public class HelloAction extends Action {


public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

HelloForm input = (HelloForm) form;


input.setMessage(MESSAGE);
return mapping.findForward(SUCCESS);
}

public static final String MESSAGE = "Hello World!";


public static final String SUCCESS = "success";
}

Listing: Struts 2 Hello Action class


package actions;
import com.opensymphony.xwork2.ActionSupport;
public class Hello extends ActionSupport {

public String execute() throws Exception {


setMessage(MESSAGE);
return SUCCESS;
}
public static final String MESSAGE = "Hello World!";
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = 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.

What about the tags?


Let's have a look.

Listing: Struts 1 "Hello" server page


<%@ taglib prefix="bean" uri="http://struts.apache.org/tags-bean " %>
<html>
<head>
<title>Hello World!</title>
</head>
<body>
<h2><bean:write name="HelloForm" property="message" /></h2>
</body>
</html>

Listing: Struts 2 "Hello" server page


<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Hello World!</title>
</head>
<body>
<h2><s:property value="message"/></h2>
</body>
</html>

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.

Can we still localize messages?


Both Struts 1 and Struts 2 use standard message resource bundles. If we have
an existing bundle, it can be registered in the struts.properties file.

Listing: Registering a Struts 2 message resource bundle via struts.properties


struts.custom.i18n.resources = resources

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.

Listing: Hello World Resource bundles

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.

Listing: Changes to Struts 1 Hello Action to enable internationalism


package actions;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import forms.HelloForm;
public class HelloAction extends Action {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,

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.

Listing: Changes to Hello Action to enable internationalism

public class Hello extends ActionSupport {


public String execute() throws Exception {
- setMessage(MESSAGE);
+ setMessage(getText(MESSAGE));
return SUCCESS;
}

- public static final String MESSAGE = "Hello World!";


+ public static final String MESSAGE = "message";

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.

Listing: Struts 1 "Hello" server page


<%@ taglib prefix="bean" uri="http://struts.apache.org/tags-bean " %>
<html>
<head>
- <title>Hello World!</title>
+ <title><bean:message key="message"/></title>
</head>
<body>
<h2><bean:write name="HelloForm" property="message" /></h2>
</body>
</html>

Listing: Struts 2 "Hello" server page

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>

How do we change Locales in Struts 2?


The Locale is stored in the user's session with the Java container. To change
locales in Struts 1, most often, we cobble up some kind of LocaleAction class.
The listing shows a typical implementation.

Listing: Changing the locale in Struts 1 (LocaleAction)


public final class LocaleAction extends Action {

public ActionForward execute(ActionMapping mapping,


ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
String language = request.getParameter(LANGUAGE);
String country = request.getParameter(COUNTRY);
Locale locale = getLocale(request);

if ((language != null && language.length() > 0) &&


(country != null && country.length() > 0)) {
locale = new java.util.Locale(language, country);
} else if (language != null && language.length() > 0) {
locale = new java.util.Locale(language, "");
}

setLocale(request, locale);
return mapping.findForward(SUCCESS);
}

private static final String LANGUAGE = "language";


private static final String COUNTRY = "country";
private static final String SUCCESS = "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.

Listing: Changing the locale in Struts 1 (JSP)


<li>
<html:link action="/Locale?language=es">Español</html:link>
</li>

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.

Listing: Changing the locale in Struts 2 (JSP)


<li>
<s:url id="es" action="Hello">
<s:param name="request_locale">es</s:param>
</s:url>
<s:a href="%{es}">Español</s:a>
</li>

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.

Does Struts 2 use the Commons Validator?


No, we use a similar but different validation framework. The Struts 2
validation framework is part of Open Symphony XWork. (Open Symphony was the
original host of WebWork 2, which became Struts 2.)
Validation comes into play when we submit an input form for processing. Before
we add an input form, let's look at how both versions of Struts configures its
respective validation framework.
In Struts 1, we can add a validations.xml file to the application with a
stanza that describes how to validate our "HelloForm", as shown by the
listing.

Listing: The Struts 1 validation.xml configuration file for HelloForm


(validations.xml)

<?xml version="1.0" encoding="ISO-8859-1" ?>


<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules
Configuration 1.3.0//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd ">
<form-validation>
<formset>
<form name="HelloForm">
<field property="message" depends="required">
<arg key="message"/>
</field>
</form>
</formset>
</form-validation>

In a larger application, we would continue to add <form> elements for each


ActionForm to be validated.

In Struts 2, the configuration files are finely-grained. We create a


validation.xml file for each object being validated. In this case, since we
want to validate the actions.Hello class, we create a Hello-validation.xml
file in the actions package, like the one shown by the listing.

Listing: The Struts 2 validation file for Hello (actions/Hello-validation.xml)


<?xml version="1.0" encoding="ISO-8859-1" ?>

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.

Listing: Adding an input form to Hello World for Struts 1

<!-- ... -->


<action-mappings>

<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.

Listing: A Struts 1 input form (HelloInput.jsp)


<html:errors/>
<html:form action="/Hello" focus="message"
onsubmit="return validateRegistrationForm(this);">
<table>
<tr><td>
Message:
</td><td>
<html:text property="message" />
</td></tr>
</table>
</html:form>

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.

Listing: Adding an input form to Hello World for Struts 2


<action name="Hello!*" method="{1}" class="actions.Hello">
<result>/Hello.jsp</result>
<result name="input">/Hello!input.jsp</result>
</action>

In the Struts 2 rendition, when we call "Hello!input.action", the mapping will


not invoke the usual "execute" method. Instead, it will call the "input"
method by replacing the "{1}" with the characters matching the wildcard "*" in
the action name. We called "Hello!input", so the framework invokes the "input"
method.

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.

Listing: A Struts 2 input form (Hello!input.jsp)


<s:actionerror/>
<s:form action="Hello">
<s:textfield label="Message" name="message"/>
</s:form>

In the Struts 2 action mapping, we make use of wildcards to call an alternate


method. The same sort of thing can be done with Struts 1, but it's more
complicated. One complication is that we need to turn validation off for an
input action, but we need validation turned on for a submit action.

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.

Listing: Resource bundle with localized error messages


resources.properties
prompt = Enter Message
message = Hello World!
# Struts 1
errors.required={0} is required.
# Struts 2
requiredstring = ${getText(fieldName)} is required.
resources_es.properties
prompt = Entre el mensaje
message = ¡Hola Mundo!

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.

Can we register custom type converters with Struts 2?

How do we access servlet context properties in Struts 2?

Does Struts 2 support modules?

How does Struts 2 handle plugins?

Is that all there is?

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.

Struts 2 is a giant leap forward, but, for Struts 1 developers, it's a


learning curve we can walk, one step at a time.

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

Anda mungkin juga menyukai