struts
DESCRIPTION
Struts Web FrameworkTRANSCRIPT
The Struts Web Application Framework
Architecture and Best Practices
By
Brian Ong
Master’s Project Department of Management Information Systems
May 9, 2004
Table of Contents Introduction to Struts .......................................................................................................... 1 The Struts Framework Architecture.................................................................................... 6
Struts and the Model-View-Controller Design Pattern................................................... 6 Example Struts Application 1 ....................................................................................... 13
Struts Best Practices.......................................................................................................... 40 Action Classes must be Thread Safe......................................................................... 40 The Action is not the Model ..................................................................................... 40 Global Forwards and Global Exceptions .................................................................. 44 Specialty Action Classes ........................................................................................... 50
Example Struts Application 2 ....................................................................................... 52 Struts Roadmap and Alternatives...................................................................................... 60 Conclusion ........................................................................................................................ 64 Appendix A: Source Code and Example Applications .................................................... 67 Bibliography...................................................................................................................... 73
Page 1
Introduction to Struts In the past fifteen years the World Wide Web has undergone tremendous change.
From its humble beginnings in the CERN laboratory in 1989 to the present day, the Web
has gone from being a forum for a small group of physicists to share research using
hypertext documents to being a medium that has become part of our everyday lives.
Today, the Web has countless uses for people all over the globe. The Web has given us
the ability to trade stocks with the click of a mouse; it allow skiers to check snow
conditions and see live pictures from the top of the mountain from the comfort of their
own home; and it has given us search engines that allows us to find all of this information
with relative ease.
As the Web has changed and grown, so to has the technology used to create and
serve its content. Initially, the Web was made up of static documents, but this made for a
rather dull arena. In order to overcome this limitation, the Common Gateway Interface
(CGI) was created that allowed documents to be created dynamically. CGI gave web
servers the ability to communicate with programs, written in any number of languages,
allowing for content to not only be generated dynamically, but also to interact with other
systems such as databases. Information could be retrieved from a database and inserted
into pages for viewing by the client. Likewise, CGI allowed for data to be entered on the
client-side and retrieved for usage by the server. With this technology in place, the
possible applications for the Web exploded, and things such as ecommerce were born.
In 1997, the Java platform joined the fray with the introduction of its Java Servlet
technology 1 . This opened whole new avenues for web developers and had several
advantages over many of the scripting languages commonly used in CGI applications.
Page 2
By using Servlet technology, developers had access to the entire suite of Java application
programming interfaces (API’s) and libraries. Additionally, developers could also take
advantage of Java’s Object Oriented paradigm, which facilitates code reuse, extensibility,
and ease of maintenance, as well as Java’s platform independence.
However, servlets alone had some significant limitations. Essentially, servlets
generate HTML dynamically through print statements, which means that the website’s
HTML is embedded within Java code. This leads to a blurring of responsibilities
between the web designers and application developers, as a change in the web site’s
design not only requires the web developers to modify the static HTML of the site, but
also requires the application developers to modify and recompile the servlets that are
responsible for generating the site’s HTML.
In response to this limitation, the Java platform introduced Java Server Pages (JSP)
technology2. JSPs are text documents that contain HTML, XML-like tags, and scriptlets
(scripts embedded within the document written in Java) with a .jsp extension. Where
servlets can be described as HTML embedded within Java code, JSPs can be described as
HTML with embedded Java code. With these two technologies in place, servlets and
JSPs, the Java platform provided an appealing solution for dynamic web programming.
As the Java 2 Enterprise Edition (J2EE) platform (a set of Java technologies that
includes servlets and JSPs) continued to evolve, two main design patterns for the
structuring of web applications emerged. The first pattern, which was given the
imaginative name “Model 1,” can be called a page-centric approach. Essentially, a
request is made to a specific JSP, which services the request, handling all of the
processing involved in that request, and then generates the output to be returned to the
Page 3
client. This approach is simple and fast to implement, but as the web application grows,
this approach can quickly become unwieldy. For example, if the web application
contains 100 pages and a new page is added, in order for those 100 pages to be able to
make use of the new page, all 100 pages must be updated to be made aware of the new
page. This approach is obviously a maintenance nightmare, not to mention the tendency
for this approach to create a muddle of spaghetti code.
The second pattern described in the J2EE specifications was the Model 2 design.
Unlike the page-centric approach, the Model 2 design is based around a “Controller
Servlet” that handles the initial processing of the request and then decides which JSP to
display next. The main advantage of this approach is that the application has a single
point of entry, the controller servlet. This single point of entry allows for many aspects
of the application, such as authentication and logging, to be handled in a centralized
location, the controller servlet.
Figure 1: Model 2 design pattern from J2EE Design Patterns, Sun Microsystems, Inc.
In contrast to the page-centric approach, if a new page or function is added to the
application, only the controller servlet, rather than each individual page, needs to be made
Page 4
aware of the new page. This means the Model 2 approach is highly extensible and much
easier to maintain than the Model 1 approach.
Furthermore, the J2EE specification has specific guidelines for design enterprise
application as a whole. The specification describes a layered approach, consisting of five
layers (Figure 2).
Figure 2: Designing Enterprise Applications with the J2EE Platform, Sun Microsystems, Inc.3
However, take note of the second layer from the top, the “Application Framework” layer.
This layer leaves a tremendous void in the implementation of the J2EE platform. For
example, a company intending to implement an enterprise application using the J2EE
platform can choose any number of vendors to supply the operating system. Additionally,
several major implementations of the J2SE platform, or the core Java library, exist –
Sun’s own implementation, JRocket from BEA Systems, and Websphere from IBM, to
name a few. The next layer up, J2EE or Web Container layer, also has no shortage of
implementations, with major commercial offerings, including WebLogic from BEA and
Websphere, as well as the popular open-source servlet container Tomcat and application
server Jboss. Obviously, the application developer must create the application-specific
Page 5
code. However, this leaves a void in the layer specified as Application Framework. Left
to his own devices, the application developer would be left to create his own framework.
And left to his own devices, Craig R. McClanahan created his own web
application framework. He donated this framework to the Apache Software Foundation
in 2000, where it became part of the Jakarta project and is now commonly known as
Struts. Currently in version 1.1, the Struts framework, based on the Model 2 J2EE design
pattern, has quickly become the most popular web application framework for the J2EE
platform. It has been incorporated into several major web applications, such as
DirecTV.com, verizonwireless.com, selected areas of Hewlett-Packard’s web
applications, as well as Tomcat’s management console.
The remainder of this discussion takes an in-depth look at the Struts framework.
First, the architecture of the framework will be discussed, including the theory behind its
design, as well as a description of each of the framework’s major components. Strategies
for design with and use of Struts, a best practices section, will follow. The discussion
will close with a look at Struts in relation to other technologies, including the future of
Struts as well as its alternatives. Additionally, sample applications as well as the
application source code accompany the discussions below. Finally, it is the aim of this
author to demonstrate that the Struts framework provides an excellent means for creating
robust, extensible and scalable web applications.
Page 6
The Struts Framework Architecture
As previous noted, the Struts framework is a controller servlet or Model 2 based
architecture. In more abstract terms, the Model 2 type architecture is based on the
Model-View-Controller (MVC) design pattern. The key aspect of the MVC architecture,
which was created by Professor Trygve Reenskaug, Professor Emeritus at the University
of Oslo in Norway, and popularized in Smalltalk-80, is its clear separation of
responsibilities.4 The MVC architecture, which as the name suggests, is comprised of
three components, the Model, the View and the Controller. An MVC application is a set
of models, views and one or more controllers. The Model contains the application state.
The application state is data that can be stored in any number of different technologies,
including databases, files, JavaBeans, etc. The View reads the information contained in
the Model, and generates a response based on that data. The Controller, perhaps the most
important aspect of the architecture, is responsible for coordinating these activities. The
Controller initially receives a user request, processes the user input, updates the Model,
and then selects the appropriate View. 5 Separating the application into these three
components means that each component is self-contained, with well-defined interfaces.
This allows for each component to change independently, leading to extensibility and
reuse.
Struts and the Model-View-Controller Design Pattern
With JavaBeans (model), JSP (view), and Servlets (controller), the J2EE platform
naturally lends itself to the MVC architecture, and Struts leverages this natural fit in its
design. The Struts Framework is primarily an implementation of the controller aspect of
Page 7
the MVC design, allowing for applications to choose among various view and model
technologies. While virtually any display technology is possible, Struts (not to mention
the J2EE platform) provides significant support for and is tightly coupled with JSP
technology for the view components. This means that while any view technology can be
used, many of the advantages of the Struts Framework cannot be realized unless using
JSPs for the view components. For model components, Struts relies on and provides
seamless integration with other technologies, such as JDBC, Enterprise JavaBeans (EJB),
and object-to-relational mapping tools such as Torque or Hibernate.6 This design, where
Struts acts as the controller and allows for various view and model technologies, allows
for a great deal of flexibility within an application.
The Struts Framework consists of four major components: the ActionServlet,
which is the controller servlet, Action classes, which are developer added extensions to
the controller, ActionForm classes, which are the means by which Struts passes model
information between the model and the view, and the Struts configuration file, which is
an XML file that links all of the pieces together.
At the heart of the Struts architecture is its implementation of the controller, the
Action Servlet. The Action Servlet, which can be found under
org.apache.struts.action.ActionServlet, is the initial point of contact for all user
requests (For source code and compiled binaries of the Struts Framework, visit the
official Struts website at http://struts.apache.org/). The Action Servlet provides an easily
extensible and highly flexible controller that processes user input, selects and updates the
appropriate model, and then forwards the request to the corresponding view.
Page 8
Interestingly, the Struts Framework relies on a single controller, the Action
Servlet, for the entire application. This means that this single controller must be aware of
all types of request that a user can perform. For small applications, this may not present
much of a problem, and the types of request could perhaps be hard-coded into the servlet
itself. However for complex web applications, the application may need to handle tens to
perhaps hundreds of different kinds of requests. In this case, hard-coding the types of
requests and the actions necessary to process those request directly into the Action
Servlet would lead to an enormous and unwieldy servlet. Clearly, a better solution is
called for.
Fortunately, the designers of the Struts Framework were aware of this limitation
in a simple MVC implementation, and instead used a variation of the MVC pattern called
the Command Pattern. The Command Pattern is one of the many design patterns to be
described in the famed book, Design Patterns: Elements of Reusable Object Oriented
Software, by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, who are
commonly referred to as the “Gang of Four.”7 In this pattern, the controller first
performs tasks common to all requests, then forwards control to a request specific
command, which then handles the tasks that are unique to that request. Each command is
a single object that encapsulates some sort of request, and according to the design pattern
should share a simple common interface. These commands are also referred to as
“actions.”8
Clearly, the designers of the Struts Framework took this notion of an “action” to
heart, as the implementation of the command objects in Struts must extend the Action
class (org.apache.struts.action.Action). The Action class also has a very simple
Page 9
interface that each child must implement (in the Struts Framework, the Action class is
actually a concrete class). Each child of the Action class should override a single method
that has the following signature:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception; The return type, ActionForward, and two of the parameters, ActionMapping and
ActionForm, are Struts specific classes, which will be explained below. After the Action
Servlet has performed the tasks common to all requests*, the Action Servlet delegates the
request to a specific Action class through a process of URL mapping (described below),
and calls the execute method for that specific Action. It is here that the request specific
logic occurs. Furthermore, it is the use of the Command Pattern that allows developers to
add request specific logic, in the Action classes, to the controller without having to add
code to the controller, the Action Servlet, itself, leading to a highly extensible design.
Another important aspect of any framework is to remove the need to perform
common and highly repetitive tasks from the developer. In web programming, one such
tedious task is form processing. Nice looking pages full of flash animations are all well
and good, but web applications are not particularly useful unless they can accept
information from the user; for example, all ecommerce applications require the ability to
obtain information from the customer such as her name and billing information.
However, this does not change the fact the processing information submitted via an
HTML form is an extremely tedious and repetitive task. For example, processing a form
containing user billing information would look something like the following:
* More correctly, the ActionServlet delegates the common tasks to the RequestProcessor, but for the purposes of this discussion, the RequestProcessor will be considered part of the ActionServlet.
Page 10
String firstName = request.getParameter(“firstName”); String lastName = request.getParameter(“lastName”); String address = request.getParameter(“address”); So on and so forth. Consider a situation in which an HTML form has 50 or more data
elements and one obviously realizes how dreary a task this would be. For this situation,
Struts clearly does not disappoint, as one of its greatest strengths as a web application
framework is its automatic form processing. Struts has a powerful mechanism that not
only extracts the user-submitted information from the request object, but can also
perform validation of the user-submitted information to verify its correctness.
Struts accomplishes this automatic form processing through the use of the
ActionForm class (org.apache.struts.action.ActionForm). The ActionForm class
is merely a JavaBean that specifies a few additional methods that are Struts specific. In
order to make use of the automatic form processing, the developer simply extends the
ActionForm class, adding getter and setter methods for each form property that should be
processed. The Struts Framework then uses reflection (more accurately, Struts delegates
to another Apache Software Foundation project, the Commons Project which uses
reflection), to populate the ActionFrom with the relevant request parameters.
Another important method which the developer should override when extending
the ActionForm class is the reset method, which has the following signature:
public void reset(ActionMapping map, HttpServletRequest request); This method is called prior to the form bean being populated and allows the developer to
set default values within the form bean. Most importantly, in order for the developer to
take advantage of Struts’ powerful validation mechanism, the developer should also
override the validate method, which has the following signature:
public ActionErrors validate(ActionMapping map, HttpServletRequest request);
Page 11
By overriding the validate method, the developer provides the necessary logic for the
Struts validation mechanism to not only ensure the correctness of the user input, but, in
the event of invalid data entered by the user, to also redirect the user back to the
corresponding HTML form and repopulate the form automatically. This form processing
function of Struts – the extraction of form data, validation of the data, and repopulation of
the HTML form in the event of invalid data – is one of its most powerful and useful
aspects.
In order to better illustrate the process, perhaps it would be useful to examine a
user request and the process by which Struts handles that request. Figure 3 is a simplified
representation of a user request processed by the Struts Framework. The request begins
with the user submitting an HTTP request, which is then referred to the Action Servlet.
After receiving the request, the Action Servlet first performs the common tasks to all
request, which includes locating the appropriate ActionForm, populating the ActionForm,
and calling the validate method of the ActionForm. If the form passes validation, the
Action Servlet then locates and delegates the request to the request specific Action class
by calling the execute method, which takes the ActionForm as one of the parameters.
While in the execute method, the request specific logic to update the model is executed,
most likely using information contained in the ActionForm. Upon completion of the
execute method, the Action directs the Action Servlet to forward the request to the next
view, which generally is a JSP. The view then uses the updated model in generating the
response, which is then returned to the client for viewing.
Page 12
Figure 3. Sequence Diagram of User Request
While Figure 3 may illustrate the main aspects of the sequence of events that
occurs when processing a user request, it is certainly not the complete picture. After
examining Figure 3, there are some the natural questions that come to mind. For instance,
how does the ActionServlet know which ActionForm to use for the request? How does
the ActionServlet delegate the request to the request specific Action? How does the
Action forward the request to the correct view? The answer to these questions is the
Struts configuration file. The Struts, like many Java technologies, uses an XML file for
configuration. For Struts, the configuration file is the glue that links all of the different
aspects of the Struts Framework together, and is the means by which the Struts controller
can be modified dynamically at runtime. Although the file can be named practically
anything, by default Struts will use a file named struts-config.xml in the WEB-INF
directory of the web application.
Page 13
Example Struts Application 1 In order to put all of the pieces together, examining an example Struts application
would perhaps be useful. Consider a situation at a university with a new incoming class.
More specifically, the MIS department at the University of Arizona has an incoming
group of graduate students, and would like to have all students enter their school related
information into a student database. Furthermore, the system should enforce certain rules
for the information they enter, such as that they must register using an Eller College
issued email address. The system should also allow students to view and edit their
information. An application with such requirements is a perfect fit for the Struts
Framework (Note: all source code and prepackaged web applications can be found at
http://www.u.arizona.edu/~bong/masters_project/examples.zip. Please see Appendix A
for instructions on installing and running the sample applications).
The first step is to make the web application aware of the Struts Framework,
which is done through the deployment descriptor of the web application. The
deployment descriptor should be named web.xml, must be placed in the WEB-INF
directory of the application. In this file, the web application must be made aware of the
Struts ActionServlet and which requests it will handle. Example 1-1 demonstrates how
this is done.
Example 1-1 web.xml
<?xml version="1.0"?> <!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> <display-name>Brian Ong's Sample Struts Application</display-name> <!-- ***************** Servlets ************************************* --> <servlet>
Page 14
<servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- ***************** Servlet Mappings **************************** --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app> The web application is made aware of the ActionServlet with the use of the <servlet> tag.
The application is then instructed that all requests that end with the .do extension (a
technique called extension mapping) should be delegated to the ActionServlet. This is
the means used to enforce the single point of entry for the web application, as all requests
with the .do extension will be referred to the ActionServlet. Struts also supports a path
mapping technique, for example:
<url-pattern>/execute/*</url-pattern>
However, beginning with Struts 1.1, certain features of Struts will not function properly
using path mapping, so extension mapping is the recommend method.
At this point, we should also create the Struts configuration file, which should be
called struts-config.xml. This file should also be placed in the WEB-INF directory of the
web application. At this point, the file should only contain the following text:
<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> </struts-config>
The next step in developing the application is to determine what information
needs to be stored, that is to say, to establish the model. In this scenario, we will want to
record the student’s first and last name, email address, degree program, semester and year
Page 15
of projected graduation. Accordingly, we will create a StudentDTO class (Data Transfer
Object – a DTO is an effective means to package related data, which then can be passed
to any number of data persistence technologies, such as Enterprise JavaBeans, Object-to-
Relational Mapping tools, etc. For more information on DTO’s refer to J2EE Design
Patterns, William Crawford, O’Reilly). Example 1-2 shows the StudentDTO source.
Example 1-2 StudentDTO.java
package ong.brian.masters_project.example1; import java.io.Serializable; /** * This source code is offered as a companion to Brian Ong's Master's Project. * It has no other use beyond a demonstration of certain aspects of the Apache * Software Foundation's Struts Web Application Framework. * @author Brian Ong * */ public class StudentDTO implements Serializable { private String firstName; private String lastName; private String email; private String degreeProgram; private String graduationSemester; private int graduationYear; public StudentDTO() { firstName = ""; lastName = ""; email = ""; degreeProgram = ""; graduationSemester = ""; } public StudentDTO(String firstName,String lastName,String
email,String degreeProgram, String graduationSemester,int graduationYear) {
this.firstName = firstName; this.lastName = lastName; this.email = email; this.degreeProgram = degreeProgram; this.graduationSemester = graduationSemester; this.graduationYear = graduationYear; } public String getDegreeProgram() { return degreeProgram; } public String getEmail() { return email; }
Page 16
public String getFirstName() { return firstName; } public String getGraduationSemester() { return graduationSemester; } public int getGraduationYear() { return graduationYear; } public String getLastName() { return lastName; } public void setDegreeProgram(String string) { degreeProgram = string; } public void setEmail(String string) { email = string; } public void setFirstName(String string) { firstName = string; } public void setGraduationSemester(String string) { graduationSemester = string; } public void setGraduationYear(int i) { graduationYear = i; } public void setLastName(String string) { lastName = string; } } The StudentDTO class is merely a JavaBean that has variables for each piece of the
Student’s information that we would like to keep in the system.
With the model set, we move on to the next requirement of the application, which
is to allow Students the ability to register themselves in the system, as well as modify
their information. In order to accomplish this, the application must have a means for the
model and the view to interact. This is done through the use of an ActionForm, which in
this case is called RegistrationForm. Note that RegistrationForm extends
ActionForm. Example 1-3 shows the RegistrationForm source.
Page 17
Example 1-3 RegistrationForm.java
package ong.brian.masters_project.example1; import java.util.Calendar; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; /** * This source code is offered as a companion to Brian Ong's Master's Project. * It has no other use beyond a demonstration of certain aspects of the Apache * Software Foundation's Struts Web Application Framework. * @author Brian Ong * */ public class RegistrationForm extends ActionForm { private StudentDTO student; public void reset(ActionMapping map,HttpServletRequest req) { if(student == null) student = new StudentDTO(); student.setFirstName(""); student.setLastName(""); student.setEmail(""); student.setDegreeProgram(""); student.setGraduationSemester("Spring"); student.setGraduationYear(Calendar.getInstance().get(Calendar.YEAR)+2); } public ActionErrors validate(ActionMapping map,HttpServletRequest req) { ActionErrors errors = new ActionErrors(); if(student.getFirstName().length() == 0) { ActionError fName = new ActionError("errors.required","Your First Name"); errors.add(ActionErrors.GLOBAL_MESSAGE,fName); } if(student.getLastName().length() == 0) { errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.required","Your Last Name")); } boolean validEmail = false; if(System.getProperty("java.version").startsWith("1.4")) { if(student.getEmail().matches("^\\S+@(bpa|eller)\\.arizona\\.edu$")) validEmail = true; } else { if(student.getEmail().endsWith("@eller\\.arizona\\.edu")) validEmail = true; if(student.getEmail().endsWith("@bpa\\.arizona\\.edu")) validEmail = true; } if(!validEmail) { errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.eller-email"));
Page 18
} if(student.getDegreeProgram().length() == 0) { errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.select","Degree Program")); } if(student.getGraduationSemester().length() == 0) { errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.required","Graduation Semester")); } if(student.getGraduationYear() == 0) { errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.required","Graduation Year")); } return errors; } public StudentDTO getStudent() { return student; } public void setStudent(StudentDTO user) { this.student = user; } public String getDegreeProgram() { return student.getDegreeProgram(); } public String getEmail() { return student.getEmail(); } public String getFirstName() { return student.getFirstName(); } public String getGraduationSemester() { return student.getGraduationSemester(); } public int getGraduationYear() { return student.getGraduationYear(); } public String getLastName() { return student.getLastName(); } public void setDegreeProgram(String string) { student.setDegreeProgram(string); } public void setEmail(String string) { student.setEmail(string); } public void setFirstName(String string) { student.setFirstName(string); }
Page 19
public void setGraduationSemester(String string) { student.setGraduationSemester(string); } public void setGraduationYear(int i) { student.setGraduationYear(i); } public void setLastName(String string) { student.setLastName(string); } }
There are several aspects of the RegistrationForm that should be noted. The
first aspect is that the RegistrationForm contains only a single instance variable,
student, which is of type StudentDTO. Also note that the RegistrationForm has getters
and setter for each piece of information that we would like to store for each student, but
that these getters and setters then delegate to the corresponding getters and setters in the
StudentDTO class. Encapsulating a DTO in this manner allows for a very convenient
means for Struts to automatically populate a DTO, or for Struts to extract information
from a DTO (through the use of an ActionForm), without the developer having to
explicitly transfer the information, i.e.:
RegistrationForm.setFirstName(studentDTO.getFirstName()); RegistrationForm.setLastName(studentDTO.getLastName()); With the getter and setter method for the StudentDTO, the developer can simply extract
all of the information from the RegistrationForm by simply calling getStudent(), and
likewise can populate the RegistrationForm with the setter setStudent(StudentDTO
student).
The next aspect of RegistrationForm that should be noted is the reset() method.
Here the developer can set default values or reset values that may contain previous
information. In this example, the reset method sets most of the values to the null string,
Page 20
“”, except for the graduation year, which is set to the current year plus two – which
should be appropriate, since this is a two-year program.
The third important aspect of RegistrationForm is the validate() method. By
overriding this method, the developer can take the steps necessary to validate the
information entered by the user, and make use of Struts’ validation mechanism. Most
importantly, this occurs prior to the information being stored, which will maintain the
integrity of the data stored by the system. In this example, except for the email field, the
validate method simply ensures that the user has entered a value. For the email field, the
system ensures that the user enters only an Eller College issued email address.
The most important part of the validate method is to note what occurs when a
field does not pass validation. For example, if the user does not enter a value for
firstName, meaning the length of that field is zero, a new ActionError is created. Notice
that the ActionError constructor takes two strings. The first string, “errors.required” is a
reference to a key in a Java properties file (a properties file is a text file that contains key-
values pairs). The key “errors.required” corresponds to a value which is a message that
should be displayed to the user for this particular error. In this case, the Java properties
file contains a key-value pair as follows:
errors.required={0} is required
The second parameter passed to the ActionError constructor, which in this case is “Your
First Name,” is the value that should be substituted for the {0} in the above message.
The resulting message that is displayed to the user is “Your First Name is required.” This
is a useful mechanism that allows for the reuse of messages, but with the flexibility to be
tailored to specific situations. For example, notice the next value that is checked in the
validate method, which is the Student’s last name. In this case, the same message key,
Page 21
“errors.required,” can be used again, except with the second parameter being “Your Last
Name.” Furthermore, these messages can be passed zero or more parameters, and the
ActionError constructor is overloaded to allow for this possibility. For example, a
possible message could be:
errors.range=The {0} must be between {1} and {2} The corresponding ActionError could be instantiated using the following statement:
ActionError range = new ActionError(“errors.range”,”Amount”,”5”,”10”); The message displayed to the user would be “The Amount must be between 5 and 10.”
Once the ActionError is created, it is added to the ActionErrors instance, which is
a list that contains all errors that occurred during validation. Upon completion, the
validate method returns the ActionErrors list. Struts uses this list to determine if the
entered data passed validation. If the validate method returns null or the ActionErrors list
is empty, Struts assumes the data passed validation, and proceeds to pass the ActionForm
to the appropriate Action class. However, if the list contains one or more ActionErrors,
Struts will automatically redirect the client to a page specified by the developer
(presumably the page where the user entered the data), display the messages contained
within ActionErrors, pre-populate the HTML form, and allow the user to correct the
information.
In order to make Struts aware of the RegistrationForm, we need to add the
following entry into the struts-config.xml file:
<struts-config> ... <form-beans> <form-bean name="registrationForm" type="ong.brian.masters_project.example1.RegistrationForm" /> </form-beans>
Page 22
... </struts-config> The <form-beans> element can contain one or more <form-bean> elements, each of
which lists a specific form bean. The <form-bean> element must contain the two
parameters. The first, name, specifies the name that will be used by the Struts controller
and its components to identify this bean. The second, type, is the fully qualified class
name of the form bean.
In order for the validation messages to be displayed, we also need to create a
properties file, which is shown in example 1-4, and make Struts aware of this file by
specifying its name and location in the struts-config.xml file, example 1-5.
Example 1-4 ApplicationResources.properties
errors.header=<ul style="color:red;font-family:arial,helvetica;list-style-type:square;margin-left:1.3in"> errors.footer=</ul> errors.required=<li>{0} is required</li> errors.eller-email=<li>Please enter a valid Eller College email address, i.e. [email protected] or [email protected]</li> errors.select=<li>Please select your {0}</li> Notice Struts also allows for formatting of the messages, with the use of errors.header
and errors.footer. The errors.header value will be printed prior to the messages contained
within the ActionErrors list, and the errors.footer value will be printed after. This allows
for the messages to be displayed, in this example, within an unordered list.
Example 1-5 Message Resources
<struts-config> ... <message-resources parameter="ong.brian.masters_project.messages.ApplicationResources" /> ... </struts-config> Note that the fully qualified name of the file must be used.
Page 23
The next step in the process is to create the views that will be presented to the
user. In this application, we will need two. This first will allow the user to enter or edit
his information; the second will display the information once it has been entered.
Example 1-6 shows register.jsp, which is the form page where the user will enter or
edit his information.
Example 1-6 register.jsp
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html:html> <head> <title>UA MIS Home Page</title> </head> <body> <!—- code removed, see Appendix A for complete listing --> <!-- Struts relevant information begins here --> <html:errors /> <html:form name="registrationForm" type="ong.brian.masters_project.example1.RegistrationForm" method="post" action="/example1/processRegistration.do" focus="firstName"> <table style="border-style:solid;border-color:black;border-width:1px;margin-left:1.0in"> <tr> <td width="150px">First Name</td> <td><html:text name="registrationForm" property="firstName" size="30" /></td> </tr> <tr> <td width="150px">Last Name</td> <td><html:text name="registrationForm" property="lastName" size="30" /></td> </tr> <tr> <td width="150px">Email</td> <td><html:text name="registrationForm" property="email" size="30" /></td> </tr> <tr><td width="150px">Degree Program</td> <td> <html:select name="registrationForm" property="degreeProgram"> <html:option value="" /> <html:option value="Master's" /> <html:option value="PhD" /> </html:select> </td> </tr>
Page 24
<tr> <td width="150px">Projected Graduation</td> <td> <html:select name="registrationForm" property="graduationSemester"> <html:option value="" /> <html:option value="Spring">Spring</html:option> <html:option value="Summer">Summer</html:option> <html:option value="Fall">Fall</html:option> </html:select> <html:select name="registrationForm" property="graduationYear"> <html:option value=""></html:option> <html:option value="2004">2004</html:option> <html:option value="2005">2005</html:option> <html:option value="2006">2006</html:option> <html:option value="2007">2007</html:option> <html:option value="2008">2008</html:option> <html:option value="2009">2009</html:option> </html:select> </td> </tr> </table> <input type="submit" value="Submit" style="margin-left:1.0in" /> </html:form> </body> </html:html> Note the first line of this file, which specifies that this page will use the struts-html tag
library. A few lines down the page, the first of these custom tags, <html:errors>, appears.
This tag is responsible for rendering the messages contained within the ActionErrors list,
if it is present. As previously mentioned, an ActionErrors list is returned after validate
method is called on the appropriate ActionForm. If the list is not empty, the error
messages contained within that list will be displayed to the user by the <html:errors> tag.
The next custom tag is the <html:form> tag. Not surprisingly, it renders a normal
<form> tag, however, there are several Struts specific attributes of this tag. The first,
name, is the name of an ActionForm specified in the <form-beans> section of the struts-
config.xml file, which is to be associated with this form. Type, the second parameter,
refers to the fully qualified class name of the ActionForm. The next parameter worth
noting is the action parameter, which in this case specifies the context relative URL to
which the information should be sent.
Page 25
Nested within the <html:form> tag are the actual form elements. Struts has
custom tags for all types of input types, including text boxes, radio buttons, select boxes,
and so forth. For each of these custom tags, the tag requires the property parameter to be
specified, and optionally the name attribute to be specified. The name attribute must
correspond to a name of a form bean specified in the <form-beans> section of the struts-
config.xml file, and the property attribute must refer to a getter/setter pair within that
form bean. For example, the first custom tag that renders a form element reads:
<html:text name="registrationForm" property="firstName" size="30" />
The name parameter refers to the ActionForm associated with this form, and the property
parameter refers the getFirstName(), setFirstName(String firstName) getter/setter
pair in the RegistrationForm.
One may wonder why go through all of this trouble using the Struts tags to render
normal form elements? The reason for this is quite simple: by using the Struts tags, the
framework will automatically populate the form elements with the values contained
within the associated ActionForm. This integration between the Struts ActionForm and
JSP is a very powerful aspect of the framework. For example, consider a situation in
which the information in RegistrationForm does not pass validation. In most situations,
the application should return the user to the form page where he entered the data.
Furthermore, the form should be populated with the values the user just entered so that
the user can view that information, and correct only those fields that are invalid (rather
than reenter all of the information). Without using the Struts tags, the developer would
have to program, likely using scriptlets within the JSP, to reset values of the form
elements to the values entered by the user, which not only is a tedious task, but also
creates more difficult JSPs to maintain because they are now littered with scriptlets.
Page 26
Once the user has entered the relevant information and the information has passed
validation, the information should be displayed to the user. This is done in
viewRegister.jsp, Example 1-7.
Example 1-7 viewRegister.jsp
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <html:html> <head> <title>UA MIS Home Page</title> </head> <body> <!—- code removed, see Appendix A for complete listing --> <!-- Struts relevant information example begins here --> <font style="font-family:arial;margin-left:1.0in"> Welcome <bean:write name="studentInformation" scope="session" ignore="true" property="firstName" />, here is your information: </font><p /> <table style="border-style:solid;border-color:black;border-width:1px;margin-left:1.0in"> <tr> <td width="150px">First Name:</td> <td> <bean:write name="studentInformation" scope="session" ignore="true" property="firstName" /> </td> </tr> <tr> <td width="150px">Last Name:</td> <td> <bean:write name="studentInformation" scope="session" ignore="true" property="lastName" /> </td> </tr> <tr> <td width="150px">Email:</td> <td> <bean:write name="studentInformation" scope="session" ignore="true" property="email" /> </td> </tr> <tr> <td width="150px">Degree Program:</td> <td> <bean:write name="studentInformation" scope="session" ignore="true" property="degreeProgram" /> </td> </tr>
Page 27
<tr> <td width="150px">Projected Graduation:</td> <td> <bean:write name="studentInformation" scope="session" ignore="true" property="graduationSemester" /> <bean:write name="studentInformation" scope="session" ignore="true" property="graduationYear" /> </td> </tr> </table> <br /> <html:link forward="editRegistration" style="margin-left:1.0in"> Edit Information </html:link> </body> </html:html> The viewRegister.jsp looks very similar to the register.jsp, with one important distinction.
Instead of using the struts-html tag library to render html form elements, this page uses
the struts-bean custom tag library, specifically the <bean:write> tag to display
information to the user. The <bean:write> tag is perhaps the most commonly used tag
within the bean tag library. The <bean:write> tag has parameters that are quite similar to
the tags in the html tag library. The name parameter specifies the name of a JavaBean
stored in some scope within the application. The scope obviously refers to the scope
(request, session or application) where the JavaBean is stored, which in this case is
“session”. The ignore parameter specifies if the property should be ignored when the
specified bean cannot be found. The property parameter is the value within the bean that
will be rendered by the <bean:write> tag.
At this point, most of the application is complete. The model is represented by
the StudentDTO class. We have created the means by which the model and the view
interact with the use of the RegistrationForm. The view components, register.jsp and
viewRegister.jsp, allow the user to enter/edit and review his data, respectively. The only
component that needs to be addressed is the controller. As previously mentioned, the
Page 28
Struts framework is based on the Command Pattern. Consequently, we do not need to
modify the ActionServlet itself, but instead created request specific commands or Actions.
For this application we will need to create two Action classes.
The first of these actions will handle requests in which the user wishes to enter or
edit his information. Likewise, this action is named EditRegistrationAction, and is
shown in Example 1-8.
Example 1-8 EditRegistrationAction.java
package ong.brian.masters_project.example1; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; /** * This source code is offered as a companion to Brian Ong's Master's Project. * It has no other use beyond a demonstration of certain aspects of the Apache * Software Foundation's Struts Web Application Framework. * @author Brian Ong * */ public class EditRegistrationAction extends Action { public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { RegistrationForm regForm = (RegistrationForm)form; //Get StudentDTO so that information can be used to populate //the RegistrationForm, which will then be used to populate the //Student Information Form in register.jsp StudentDTO student = (StudentDTO)req.getSession().getAttribute("studentInformation"); if(student != null) { regForm.setStudent(student); } return map.findForward("register"); } }
Page 29
This is a fairly rudimentary example of retrieving business information. The Action
simply checks the Session object for previously stored information. If that information is
present, the RegistrationForm is populated by passing the StudentDTO to the form. In a
production system, the user’s information would obviously not be stored in the Session
object, but in some form of Enterprise Information System (EIS), such as a backend
database. This Action would then look up the user’s information from that EIS, which
would then return the user’s StudentDTO.
The execute method completes by returning an ActionForward. The
ActionForward notifies the ActionServlet to which view to direct the user to. The
ActionForward contains a variable, path, which contains the URL to which the user
should be directed. However, it should be noted that the ActionForward is actually
“looked up” from the ActionMapping class that is passed to this Action’s execute method,
with the statement map.findForward(“register”). This mechanism allows for
runtime decisions to determine the next view. For example, a decision may be as follows.
boolean successful = false; //logic that determines if successful ... ActionForward nextPage; if(successful) { nextPage = map.findForward(“success”); } else { nextPage = map.findForward(“failure”); } return nextPage; While the application is nearly complete, a few pieces of the puzzle are still missing.
Specifically, how does the ActionMapping class know what ActionForward to return
when passed the value “register”? How does the ActionServlet know which Action class
Page 30
to delegate the request to? These questions are answered in the struts-config.xml file.
The file contains a section for “ActionMappings” which describes the necessary
information to configure when each Action is used and what information each Action
class needs. For the EditRegistrationAction, its corresponding Action Mapping
information is shown in Example 1-9.
Example 1-9 Action Mapping for EditRegistrationAction
<struts-config> ... <action-mappings> <action path="/example1/editRegistration" type="ong.brian.masters_project.example1.EditRegistrationAction" name="registrationForm" scope="request" validate="false"> <forward name="register" path="/example1/register.jsp" /> </action> ... </action-mappings> ... </struts-config>
The first parameter of the <action> tag, path, determines how the ActionServlet
delegates requests to a specific Action. Specifically, Struts delegates the request to a
specific Action based on the URL of the request. In this example, when a user makes a
request that has an URL that matches “/example1/editRegistration,” the request will be
delegated to the Action class specified by the type parameter, which in this case is the
EditRegistrationAction class. Also note that the path element is context relative, meaning
if the name of the server that is hosting the application if foo and the name of the web
application is bar, then the URL that would be delegated to the EditRegistrationAction
class would be http://foo.com/bar/example1/editRegistration.do.
Page 31
The name parameter must match one of the form beans listed in the <form-beans>
section of the configuration file, and specifies the form bean to associate with this Action
(which will be passed to the Action class as a parameter in the execute method). The
scope attribute specifies what scope the form bean should be stored, and must be either
request or scope (scope is default). The validate parameter specifies whether or not the
validate method on the specified form bean should be called (the default value is true). In
this situation, since the EditRegistrationAction is meant to look up the user information
and pre-populate the html form, and not to store the information, the validate method
should not be called, which is why the validate attribute is set to false.
Finally, the <action> tag can contain zero or more <forward> tags which specify
the path of the view that the request should be forwarded to upon completion of the
execute method. For example, at the end of the execute method of the
EditRegistrationAction uses the ActionMapping class to find the ActionForward that
corresponds to the value “register.” The ActionMapping class, which was configured
with the values found in the <action> tag for this Action class, looks for a forward that
has the name “register.” Of course, this ActionMapping has a forward with the name
“register,” and corresponds to the view with the path “/example1/register.jsp,” which
means that the request will be forwarded to register.jsp to render the view.
With the EditRegistrationAction in place, the application is almost complete. The
action that handles the request to process the information submitted by the user must be
added. Likewise, this Action is called ProcessRegistrationAction, which is show in
example 1-10.
Example 1-10 ProcessRegistrationAction.java
package ong.brian.masters_project.example1;
Page 32
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; /** * This source code is offered as a companion to Brian Ong's Master's Project. * It has no other use beyond a demonstration of certain aspects of the Apache * Software Foundation's Struts Web Application Framework. * @author Brian Ong * */ public class ProcessRegistrationAction extends Action { public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { //If form passes validation, place user information in Session Object //so that user information is remembered RegistrationForm regForm = (RegistrationForm)form; StudentDTO student = regForm.getStudent(); req.getSession().setAttribute("studentInformation",student); return map.findForward("viewRegistration"); } } The execute method within the ProcessRegistrationAction is quite simple in that it simply
stores the user information in the session object. In a production system, that information
would likely be stored in an EIS. The request is then forwarded to the
“viewRegistration” forward, which corresponds to the viewRegister.jsp page, where the
user can inspect the information just entered. This is more clearly illustrated in the action
mapping for the ProcessRegistrationAction, example 1-11.
Example 1-11 Action Mapping for ProcessRegistrationAction
<struts-config> ... <action-mappings> <action path="/example1/processRegistration" type="ong.brian.masters_project.example1.ProcessRegistrationAction" name="registrationForm" scope="request"
Page 33
input="/example1/register.jsp" validate="true"> <forward name="viewRegistration" path="/example1/viewRegister.jsp" /> </action> ... </action-mappings> ... </struts-config> Notice that here the validate parameter is set to true, because for this mapping, the user
has just submitted data, and we would like Struts to use its validation mechanism to
check that data. Also note that the action mapping for ProcessRegistrationAction
contains one more parameter than in EditRegistrationAction: the parameter “input.” This
parameter is used when the form bean associated with this action mapping fails validation.
When the validate method is called on the RegistrationForm, and if the ActionErrors list
that is returned is not empty, the request will be forwarded to the URL specified by the
parameter “input,” which in this case is register.jsp.
Up to this point, only brief fragments of the struts-config.xml file have been
shown. For good measure, example 1-12 displays the complete file.
Example 1-12 struts-config.xml
<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- ********** Form Beans ****************************************** --> <form-beans> <form-bean name="registrationForm"
type="ong.brian.masters_project.example1.RegistrationForm" /> </form-beans> <!-- ********** Global Forwards ************************************* --> <global-forwards> <forward name="editRegistration"
path="/example1/editRegistration.do" /> </global-forwards> <!-- ********** Action Mappings ************************************* -->
Page 34
<action-mappings> <action path="/example1/editRegistration" type="ong.brian.masters_project.example1.EditRegistrationAction" name="registrationForm" scope="request" validate="false"> <forward name="register" path="/example1/register.jsp" /> </action> <action path="/example1/processRegistration" type="ong.brian.masters_project.example1.ProcessRegistrationAction" name="registrationForm" scope="request" input="/example1/register.jsp" validate="true"> <forward name="viewRegistration" path="/example1/viewRegister.jsp" /> </action> </action-mappings> <!-- ********** Message Resources *********************************** --> <message-resources parameter="ong.brian.masters_project.messages.ApplicationResources" /> </struts-config>
Now that the application is complete, it is time to demonstrate the application and
Struts in action. Please see Appendix A for instructions on installing and running the
application. Once the application is running, point your web browser to:
http://localhost:8080/examples/example1/
Once there, the user is presented with the view shown in Figure 4. Notice that since this
is the first visit to this page, the form is mostly empty except for the projected graduation
semester and date, which are set to the default values, which are Spring and the current
year plus two.
Page 35
Figure 4 editRegistration.do
Let us now consider a situation in which a newly admitted student, John Doe, visits this
application to register himself in the system. He begins by filling in his first and last
name, but decides he wants to use his hotmail email address instead of an Eller College
issued address. Furthermore, he forgets to select the degree program to which he belongs
(Figure 5).
Figure 5 Sample Input
Page 36
John the proceeds to submit the information, but fortunately, due to Struts’ validation
mechanism, is notified that he has tried to enter an invalid email address, as well as failed
to select his degree program. Struts produces messages that explain the errors in the data,
and returns John to the form where he entered the data. Also note that the form
information that John just entered has been repopulated automatically, saving John the
effort of reentering the data (Figure 6).
Figure 6 Invalid Form Data
Once John reads the error messages, and corrects his information, he resubmits the data,
after which the data is accepted and John is forwarded to a page where he can view his
information, Figure 7.
Page 37
Figure 7 View User Information
At this point John decides that he wants to graduate a semester earlier, in the Fall of 2005,
so he decides to edit his information by clicking the “Edit Information” link. He is again
presented the HTML form where he initially entered his data, but in this case, the form is
already populated with his information. All he has to do is make the changes he wants,
specifically changing his graduation date, and then submit those changes (Figure 8).
Figure 8 Edit User Information
Page 38
After submitting the information, John’s information is then updated, Figure 9.
Figure 9 Updated Information
Although quite basic, this sample application demonstrates the aspects of the
Struts Framework that are needed to create robust, scalable, and extensible applications.
The application demonstrates the Struts’ form processing and validation mechanisms, as
demonstrated by John Doe entering information into the system. The interaction between
Struts and the presentation layer were shown with the use of Struts’ custom tag libraries.
The Action classes in this example demonstrate Struts’ ability to integrate with virtually
any kind of model, which in this case was a simple Data Transfer Object, but in more
sophisticated systems could be technologies such as Enterprise JavaBeans. Furthermore,
adding functionality to this application is fairly simple. To handle a new type of request,
the developer would need to: create a new ActionForm (if this new type of request
requires user input), create the Action classes necessary to handle the new type of request,
create the necessary views, and update struts-config.xml to make Struts aware of these
Page 39
changes. Overall, building web applications with the Struts Framework results in higher
quality applications that remain flexible, extensible and easy to maintain.
Page 40
Struts Best Practices
As with any technology, there are certain techniques that can be used to get more
out of that technology, as well as various pitfalls that should be avoided. Struts is no
different: while it does make development of web applications easier, there are certain
principles that can be used to better use Struts, as well as certain things that should be
avoided. This section details a few of the most important principles to adhere to when
using Struts.
Action Classes must be Thread Safe
Servlet containers are a multi- threaded environment. In such an environment,
many threads may access resources simultaneously. For example, it is possible that two
users request the same page at the same time. This is important to note in when
designing Action classes. The ActionServlet creates only a single instance of each
Action class, and uses that one instance to service all requests. This means that more
than one thread may access an Action class simultaneously. Therefore, care must be
taken to ensure that Action classes are thread-safe. The solution is quite simple: use only
local variables to store client specific information. Instance variables can be used;
however they must not store any client specific information. Although this explanation is
quite brief and the solution simple, it is of such importance that it must be mentioned.
The Action is not the Model
This is a point of disagreement or confusion for many regarding the Struts
Framework. Some articles and tutorials argue that the Action classes are part of the
Model, while others consider it part of the Controller.9 This is no small distinction. For
Page 41
example, if the Action is considered part of the Model, then the Action is the appropriate
place to code the business logic of the application.
However, there are compelling reasons to consider the Action part of the
controller and to avoid writing business logic into the execute method of the Action.
First, if Struts were replaced with another framework, it is likely that the Action classes
would also be replaced with something else, which would mean that all of the business
logic coded in the Actions would be lost. For this reason alone, the Action should not be
considered part of the Model, but instead tightly coupled to the Struts controller. Another
reason that the Action should not be considered the Model is to consider the scenario
when several different types of clients need to access the business tier. For example,
consider the possibility that the business tier also needs to be accessed by a Swing based
GUI client. If the business logic is written in the execute methods of the Action classes,
this logic would essentially be unusable by the Swing GUI and need to be rewritten for
the GUI client (that is, of course, unless you make your Swing GUI dependant on Struts
by importing the Action classes for use by the GUI – you should run not walk away from
this possibility).
That being said, when writing the execute method within Action classes, it is
incredibly easy to begin writing business logic, but great care and discipline must be used
to avoid this bad habit. For example, it would be quite easy to begin writing an execute
method that is as follows:
public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { java.sql.Connection con = getDatabaseConnection(); java.sql.PreparedStatement ps = con.prepareStatement(“INSERT INTO ...”); ps.executeUpdate();
Page 42
//next sql statement ... //next sql statement ... return map.findForward(“continue”); } One important problem with this approach is that the logic coded into the Action class,
such as the SQL statements above, is not even reusable within a Struts application. If
these same database queries need to be performed in other areas of the application, they
must be recoded there as well. The difficulty in avoiding this poor habit is that in order
to prevent business logic from creeping into the action, the business logic must be moved
to a separate “business layer.” However there are strategies and technologies that are
specifically tailored for this situation.
One strategy would be to use the Business Delegate design pattern. 10 In this
pattern, an object is created to handle the communication between the client tier (whether
or not it is a Struts web application, Swing GUI or other) and the business tier. For the
above Action, a business delegate might look like the following:
public class BusinessDelegate { public void performTransaction(DataTransferObject dto) { //logic to perform transaction } } Consequently, the above execute method would be changed to the following:
public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { BusinessDelegate bd = new BusinessDelegate(); bd.performTransaction(form.getDataTransferObject()); return map.findForward(“continue”); }
The second strategy would be to use technologies specifically designed for
business logic. In the J2EE platform, the primary technology designed for such use is
Page 43
Enterprise JavaBeans. There are two types of EJBs that specifically meet the needs of
performing business logic in this situation, Entity Beans and Session Beans. Entity
Beans represent persistent data, while Session Beans are designed to handle workflow,
which is an excellent place to code business logic (for more information on EJBs, please
refer to Enterprise JavaBeans by Richard Monson-Haefel, O’Reilly Publishing). In this
example, the business logic would be moved into a Session Bean:
public class BusinessTransactionBean implements SessionBean { ... public void performTransaction(DataTransferObject dto) { //logic to perform transaction } } The execute method would then be modified to use the BusinessTransaction Session
Bean to complete the transaction:
public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { //lookup reference to EJBHome object
javax.naming.InitialContext c = new javax.naming.InitialContext(); Object ref = c.lookup(“BusinessTransactionHome”); //cast remote reference to appropriate EJBHome
BusinessTransactionHome bth = (BusinessTransactionHome)javax.rmi.PortableRemoteObject.narrow( ref,BusinessTransactionHome.class);
//create EJB remote reference
BusinessTransaction bt = bth.create(); //perform transaction bt.performBusinessTransaction(form.getDataTransferObject()); //clean up bt.remove(); c.close(); return map.findForward(“continue”); }
Both of these strategies accomplish their intended goal of removing the business
logic from the Action classes. Additionally, this approach allows for reuse of the
Page 44
business logic within the application, as well as allowing other types of clients to access
the business tier. However, one possible issue remains. In both of these implementations,
the Action classes are aware of the types of business layers they are communicating with,
leading to a tight coupling between Struts and the business layer technology. If the
application is and always will be required to, for example, connect to the business tier
using EJBs, this may not be an issue. However, if additional flexibility is required, a
Factory Pattern can be used to hide the actual concrete implementation of the business
tier from Struts as follows:
public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { BusinessTierInterface bti = BusinessTierFactory.getInstance(); bti.performTransaction(form.getDataTransferObject()); return map.findForward(“continue”); }
When building web applications using Struts, perhaps the biggest caveat is coding
business logic into the Action classes. This is in part because it is very easy to code the
logic in the execute method and also in part because some tutorials actually state that the
Action classes are the Model. However, this practice, in fact, should be discouraged for
the reasons discussed above. A better strategy is for Struts to delegate the business logic
to a business layer.
Global Forwards and Global Exceptions
The Struts Framework provides two features, Global Forwards and Global
Exceptions, that significantly aid the developer in creating applications that are extensible
and easier to maintain.
Page 45
In previous examples, it has been shown how forwards are used to notify the
ActionServlet to which View the client should be directed upon completion of an Action
(Example 1-12). In these examples, the forwards were configured at the Action level –
configured within the <action> tag. These forwards are specific to each Action and only
available to that Action. However, there are circumstances when it would be preferable
for forwards to be available to the application as a whole. For instance, it is likely that
most applications will have a “main menu,” and that many pages within the application
will need to proceed to the main menu after completion of a request. Rather than
declaring this same forward for each Action, it would be advantageous to declare this
forward so that it is available to the entire application. Fortunately, Struts provides a
method to make a forward available to all Actions, and for the application as a whole.
Struts configuration file contains a section where global forwards can be described. For
example, the main menu forward could be placed in the struts.config.xml file as follows:
<struts-config> ... <global-forwards> <forward name="mainMenu" path="/mainMenu.do" /> </global-forwards> ... </struts-config> Declaring the forward in this manner makes the “mainMenu” forward available to all
Actions. Additionally, the struts-html tag library also provides a means for views to
access the forwards specified in the global forwards section. The <html:link> tag can be
used to render hyperlinks. One option of the <html:link> tag allows a forward described
in the global forwards section of the configuration file to be used to specify the URL to
which the hyperlink should point. This can be used within a JSP as follows:
Page 46
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html:html> <head><title>Global Forward Example</title></head> <body> <html:link forward=”mainMenu”>Main Menu</html:link> </body> </html:html>
The use of global forwards should be encouraged, as it makes maintenance of a
web application much easier. For example, in the example of the main menu page, it is
likely that references to this page will be sprinkled throughout the application, in various
Actions, as well as various JSPs. If the main menu page needs to be moved to a different
location, and if all of the references to the main menu page are made through the use of
the global forward, updating references to the main menu page would simply involve
changing its description in the Struts configuration file:
<struts-config> ... <global-forwards> <forward name="mainMenu" path="/newLocation/mainMenu.do" /> </global-forwards> ... </struts-config> However, if this approach were not used, and the links to the main menu page were all
hard coded into the application, specifying the new location of menu page would involve
hunting down all references to this page within the application and updating them
individually, which would be a maintenance nightmare, and would probably lead to
broken links.
Another useful feature of the Struts Framework that provides is its declarative
exception handling. For all applications, there are situations in which things do not go as
expected. Consequently, exception handling is a part of all applications, and web
applications built using Struts are no different. For web applications, where the
Page 47
applications resources may be distributed across many servers connected over a network,
additional elements of uncertainty enter the equation. For example, the database server
crashing or the network going down woule cause exceptions to be thrown in the web tier.
Before Struts’ declarative exception handling is discussed, let us first explore the
process of exception handling in a multi- tier architecture. As previously mentioned, it is
generally good practice to isolate the web tier and Struts from the underlying
implementation of the business tier. However, if the underlying technology within the
Business Delegate is a database, which is most often the case, the Business Delegate will
generally throw SQLExceptions. But if the Business Delegate notifies the web tier of an
exception by throwing an SQLException, the Business Delegate reveals the underlying
implementation of the business tier, thereby destroying all of the efforts of decoupling the
business tier from the web tier. Furthermore, if the implementation of the Business Tier
changes, to say using the Java Message Service to communicate, then a whole different
set of exceptions will be thrown, which further complicates the matter of decoupling the
two tiers.
The solution to this issue is to create an implementation independent exception
that will be thrown by the business tier. Perhaps it could be called a
BusinessTierException. Next we would modify the Business Delegate implementations
to throw this kind of exception when a problem occurs, rather than throw implementation
specific exceptions. The BusinessTierException could be used to wrap the original
exception as follows:
public class DatabaseTier implements BusinessTierInterface { public void performTransaction(DataTransferObject dto) throws BusinessTierException { try {
Page 48
//sql statements } catch (SQLException s) { throw new BusinessTierException(s); } } } Wrapping the original exception in an implementation independent exception, such as the
BusinessTierException, allows for the proper exception handling to occur in the web tier
and maintains the decoupling between the tiers.
Now with the proper type of exceptions in place, we can return to the topic of
Struts declarative exception handling. For each Action that communicates with the
business tier, the possibility exists that a BusinessTierException will be thrown. One
possible way of handling this would be for each Action to enclose its communications
within a try-catch block and deal with the exception programmatically, as follows:
public ActionForward execute( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception { try { BusinessTierInterface bti = BusinessTierFactory.getInstance(); bti.performTransaction(form.getDataTransferObject()); } catch (BusinessTierException b) { //log the exception //handle the exception } return map.findForward(“continue”); } This may be fine for small applications, but consider an application with 75 different
Actions accessing the business tier. This means that this same try-catch block, along with
the code in the catch block to handle the exception must be repeated 75 times. This
would be less than ideal.
Fortunately, the Struts Framework provides a more elegant approach that can
handle exceptions in a declarative means, instead of the programmatic means shown
Page 49
above. Struts can be configured to handle any sort of exception thrown by the Action
classes in its configuration file in the following manner:
<global-exceptions> <exception key="errors.business-tier" path="/error.jsp" scope="request" type="foo.bar.BusinessTierException" /> </global-exceptions> The parameter “key” refers to a key within the message resource properties file, of which
the corresponding value will be the message displayed to the user. The “path” parameter
refers to the view where the user should be forwarded. Scope determines where the error
message will be stored. Finally, the most important parameter, type, is the fully qualified
type of the exception that should handled. With this entry in place in the struts-
config.xml file, all Actions within this application can defer handling of the
BusinessTierException to the Struts Controller.
This means that each Action that communicates with the business tier no longer
needs to programmatically handle the BusinessTierException, meaning the try-catch
block can be removed from the above execute method. The key to making this all work
is that the execute method signature of the Action class declares that it throws
java.lang.Exception, which allows for any kind of exception to be thrown (but
presumably caught by the Struts Controller and handled through the declarative exception
handling). For the above example, this also means that those 75 Action classes that
access the business tier no longer need try-catch blocks to handle the
BusinessTierException, thereby removing all of the redundant code exception handling
code.
Page 50
Specialty Action Classes
In Struts based web applications, the sequence of events begins with a user
making a request, which is first delegated to the ActionServlet by the servlet container.
The ActionServlet carries out the processing common to all request, such as processing
form data, after which it delegates the request to the request specific Action by calling the
Action’s execute method. After the execute method completes, the request is forwarded
to a JSP to render the view. While this behavior is suitable for the majority of request,
there are certain situations where different kinds of behavior are needed. Struts, being a
rich and full- featured framework, supplies seven additional types of Action classes that
can be used “out-of-the-box” that support different kinds of behavior. These Actions are
in the package org.apache.struts.actions. Of the seven, the ForwardAction and
DispatchAction are the most useful.
The ForwardAction is useful when the only need for the Action is to simply
forward the request to a JSP. In general, it is good practice to place an Action in front of
a JSP rather than access the JSP directly. 11 This enforces the single point of entry, the
ActionServlet, between the client and the application, which is one of the main tenets of
the MVC design pattern. However, in this case where no logic needs to be imbedded in
the execute method, the ForwardAction eliminates the need to create an Action class with
an empty execute method. The ForwardAction can be used by adding the following entry
to the Struts configuration file:
<action path="/welcome" parameter="/welcome.jsp" type="org.apache.struts.actions.ForwardAction" scope="request" validate="false"> </action>
Page 51
The parameter attribute of the <action> tag specifies the view to which the request should
be forwarded.
The most useful of the specialty Actions is the DispatchAction. Unlike the Action
class, which has a single method which is called by the framework, the execute method,
the DispatchAction allows the developer to specify several methods that can be called by
the framework within a single Action class. This is particularly useful when there is a
page in which the user can make more than one type of request.
For example, consider an application for an online stock trading company. There
might be a page where the user can view his portfolio. On this hypothetical page, the
user has three possible requests that he can make. First, he can update the stock price of a
stock tha t he holds in his portfolio. Second, he can buy additional shares of that company.
Or third, he can sell his shares of that company. Each of these three activities requires
different logic, and with normal Action classes, a separate Action would need to be
created to handle each of these requests, resulting in three Actions for this single page.
This can lead to numerous Action classes, making the application more difficult to track
and maintain as there is not a one-to-one correlation between a page and the Actions
associated with that page.
Instead of this approach, the DispatchAction can be used. In order to use the
DispatchAction’s functionality, a class that extends the DispatchAction class must be
created. Then a method must be created for each of the business methods that this class
should support. In using the example from above, this class should support three
business methods: updateStockPrice(), sellShares() and buyShares(). In order for the
Page 52
framework to call these methods, each must have a specific signature. For example, the
updateStockPrice() method should have the following signature:
public ActionForward updateStockPrice( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws Exception; Notice that the return type, and the four parameters are the same as the execute method of
a normal Action class. The only difference is that each method within a DispatchAction
has a unique method name. The other important distinction between a regular Action and
the DispatchAction is its <action> descriptor in the configuration file. The
DispatchAction requires an additional attribute, parameter, which specifies the name of
the request parameter that should be used to determine which method to call.
<action path="/example2/processPortfolio" type="ong.brian.masters_project.example2.ProcessPortfolioAction" name="portfolioForm" scope="request" input="/example2/portfolio.jsp" parameter="method" validate="true">
<forward name="viewPortfolio" path="/example2/portfolio.jsp" /> </action> Here the parameter attribute specifies that the request parameter “method” should be used
to determine which method within the DispatchAction to call. For example, this
DispatchAction contains a method updateStockPrice(), and this method could be called as
follows:
http://foo.com/examples/example2/processPortfolio.do?method=updateStockPrice
Example Struts Application 2
This example application demonstrates a DispatchAction in action (Please see
Appendix A for complete source code, prepackaged web application and instructions for
running the application). Once the application is running, point your web browser to:
http://localhost:8080/examples/example2/
Page 53
After navigating to the above URL, the user is presented with the login page. On
this page, one of this online trading company’s most well known clients, Warren Buffet
logs in (Figure 10.
Figure 10 User Login
Once the user is logged in, he is presented with the portfolio.jsp page, which
displays his current portfolio value, cash and stock positions, a button to update the stock
prices, followed by options to buy or sell his stock (Figure 11).
Page 54
Figure 11 Customer Portfolio
As noted above, because there are three possible types of requests that the use can make
from this page, update stock prices, buy shares and sell shares, a DispatchAction is an
excellent way to group these methods into a single Action class. The DispatchAction
used in this example is displayed in Example 2-1.
Example 2-1 ProcessPortfolioAction.java
package ong.brian.masters_project.example2; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DispatchAction; /** * This source code is offered as a companion to Brian Ong's Master's Project. * It has no other use beyond a demonstration of certain aspects of the Apache * Software Foundation's Struts Web Application Framework. * @author Brian Ong *
Page 55
*/ public class ProcessPortfolioAction extends DispatchAction { private static final String VIEW_PORTFOLIO = "viewPortfolio"; private static final String USER_CONTAINER = "userContainer"; private static final String START = "start"; public ActionForward updateStockPrice( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res)
throws Exception { UserContainer uc =
(UserContainer)req.getSession().getAttribute(USER_CONTAINER); if(uc == null) return map.findForward(START); uc.updateStockPrice(); return map.findForward(VIEW_PORTFOLIO); } public ActionForward sellShares( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res)
throws Exception { UserContainer uc =
(UserContainer)req.getSession().getAttribute(USER_CONTAINER); if(uc == null) return map.findForward(START); PortfolioForm pf = (PortfolioForm)form; try { uc.sellShares(pf.getNoShares()); } catch (BusinessRuleException e) { //if error occurs selling shares, catch error //get message and create appropriate message
ActionErrors errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionError("errors.shares.sell",e.getMessage())); //save messages
saveErrors(req,errors); //return client to appropriate page return map.getInputForward(); } pf.reset(map,req); return map.findForward(VIEW_PORTFOLIO); } public ActionForward buyShares( ActionMapping map, ActionForm form, HttpServletRequest req, HttpServletResponse res)
throws Exception { UserContainer uc =
(UserContainer)req.getSession().getAttribute(USER_CONTAINER); if(uc == null) return map.findForward(START);
Page 56
PortfolioForm pf = (PortfolioForm)form; try { uc.buyShares(pf.getNoShares()); } catch (BusinessRuleException e) { ActionErrors errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_MESSAGE,
new ActionError("errors.shares.buy",e.getMessage())); saveErrors(req,errors); return map.getInputForward(); } pf.reset(map,req); return map.findForward(VIEW_PORTFOLIO); } //if method is unspecified, call updateStockPrice() protected ActionForward unspecified( ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3) throws Exception { return updateStockPrice(arg0, arg1, arg2, arg3); } } As expected, the ProcessPortfolioAction contains methods for each of the possible types
of request the user can make, updateStockPrice(), buyShares() and sellShares().
Additionally, the last method, unspecified(), allows the developer to handle a situation in
which no method is specified in the request. In the example above, the unspecified()
method calls the updateStockPrice() method.
It should also be noted that this example makes use of the Business Delegate
pattern discussed above. In this example, the UserContainer is the business delegate.
The business logic, such as updating the stock price or buying and selling shares, is not
contained within the Action, but is instead contained within the UserContainer. Also
note that the UserContainer throws an implementation independent exception, the
BusinessRuleException, if an exception occurs during the processing of the user request.
Page 57
Continuing with our example, Mr. Buffet now reviews his portfolio, which is
initially worth $50,000 - $25,000 in cash and $25,000 (500 shares at $50 per share) in
stock of XYZ Company. Mr. Buffet, being an avid day-trader, chooses to update the
stock prices in his portfolio in hopes of finding an advantageous trade. This request, not
surprisingly, results in a call to the updateStockPrice() method. Once his request is
completed, the updated stock price is displayed, and his portfolio value is updated as well
(Figure 12).
Figure 12 Updated Stock Price, Request to Buy Shares
After updating the stock price, Mr. Buffet sees that the stock price has significantly
decreased to $39.65 per share. Since Mr. Buffet is one of the most successful investors
of all time, he does not panic, but instead sees this as an excellent opportunity to increase
his holdings. He makes a request to buy 300 additional shares, which calls the
Page 58
buyShares() method. Once the transaction is complete, his new portfolio value is
displayed (Figure 13).
Figure 13 Updated Portfolio value, Request to Sell
Amazingly, after completing the transaction, the XYZ’s stock price jumps to $60.55 per
share, and Mr. Buffet realizes this is an excellent time to sell his shares in XYZ. He
makes a request to sell 400 shares, which calls the sellShares() method in
ProcessPortfolioAction. After the request is completed, his updated holdings and
portfolio value are displayed (Figure 14).
Page 59
Figure 14 Updated Holdings
Remarkably, in a matter of minutes, Mr. Buffet increased his portfolio value from
$50,000 to $60,391.07, which reinforces the fact that he is one of the most successful
investors of all time. Now that you have seen how the master does it, perhaps you should
try your luck with Online Trader!
Page 60
Struts Roadmap and Alternatives
Although the current release, version 1.1 released on June 29, 2003, is fairly
mature, Struts is still evolving and changing. In the most immediate future will be minor
modifications to the current framework. Version 1.2 will implement several minor, but
important changes. First, it will remove deprecations created from version 1.0 and
possibly 1.1. This is no small matter as some of the important features of Struts 1.1 that
have been deprecated are the ActionErrors and ActionError classes in favor of a more
broad based terminology for such messages, ActionMessages and ActionMessage.
Version 1.2 also promises to add support for wildcard mapping of Actions.
Beyond Version 1.2, the Struts developers have several other plans for the 1.x
version of the framework. Most of these are refactorings of the framework, however
there are certain features that may be added which should be noted. The first is adopting
Stxx which is a means of using XML and XLST technologies instead of JSP and the
Struts custom tag libraries to render the Views. Another possibility is integrating Cocoon
into the framework. Cocoon, like Stxx, is an XML presentation technology. However,
unlike Stxx, Cocoon is an Apache Software Foundation project.
A particularly interesting possibility would be to add the Struts Workflow
Extension to the framework. In certain circumstances, certain processes require the user
to proceed through several different pages and forms to complete that process. However,
in a web based application (because of the HTTP protocols and non-persistent
connections), it is very difficult to prevent the user from leaving a workflow process
before it is completed or entering in the middle of a process. The Struts Workflow
Extension is an extens ion to Struts that helps to prevent such occurrences by tracking the
Page 61
user’s movements through the application (however, it cannot prevent a user from
navigating away from the site).
Beyond the 1.x versions of the framework, there are plans for Struts 2.x (which
the developers have nicknamed Struts “The Next Generation”). This version of the
framework will include much broader changes to the framework. Most importantly,
Struts 2.x will drop many of its custom tag libraries in favor of the JSP Standard Tag
Library (JSTL), and support the new Java Server Faces (JSF) API. Craig McClanahan,
the creator of Struts is also prominently involved in the JSF specifications, so it is not
surprising that Struts and JSF will merge in the future. The main emphasis is to move to
the more standard technologies of JSTL and JSF, making the Struts custom tag libraries
obsolete.
With such changes on the horizon, it is important for anyone developing
applications with Struts to remain mindful of these changes, as it will likely have an
effect on those applications in the future. More complete information regarding the
Struts development roadmap can be found at http://jakarta.apache.org/struts/status.html.
Although Struts is the most popular web application framework for the J2EE
platform, it is certainly not the only one. Several other frameworks exist, some of which
are discusses below. This list is not comprehensive, but should provide an adequate view
of the current landscape of web application frameworks.
The Barracuda Framework is also a Model 2 based architecture, but has some
significant differences compared to Struts. It provides a model event-notification
mechanism. Instead of relying on a JSP approach alone, it relies on XMLC to create
Views. XMLC is a Java-based compiler that reads an HTML or XML document and
Page 62
then creates a Java class that can recreate the document when executed. While this
approach is supposed to allow greater flexibility and extensibility to the framework, one
downside is that Barracuda has a steeper learning curve than Struts.12 More information
on Barracuda can be found at http://barracudamvc.org/Barracuda/index.html.
Another open source framework, Freemarker, can perhaps be thought of more like
a view generation technology than a full web application framework. In Freemaker,
HTML pages are stored in templates, which are eventually compiled into Java objects.
These template files are pluggable at runtime and can be stored in various places, such as
local files or in a database. Freemarker uses its own template language and claims
performance nearing that of static HTML pages. Freemarker can be found at
http://freemarker.sourceforge.net/.
A technology that is similar to Freemarker is Velocity. Velocity, like Struts, is a
part of the Jakarta project, which is one of the subprojects of the Apache Software
Foundation. In addition to producing HTML, Velocity can also generate SQL, PostScript,
and XML from templates. Velocity can be used as a standalone program or as a
component of another system. 13 The Velocity home page is
http://jakarta.apache.org/velocity/index.html.
Turbine, another Jakarta project, is a full web application framework and is
similar to Struts. However, there are some important differences. Turbine, in addition to
supporting JSP, also supports Velocity for its View technologies. Turbine makes use of
several other Jakarta projects for much of its functionality, such as Torque (an object-to-
relational mapping tool) for its database layer, and Intake for its HTML form validation.
Information on Turbine can be found at http://jakarta.apache.org/turbine/index.html.
Page 63
The final alternative to be discussed here is Maverick. Maverick is very similar to
Struts in that it implements the Command design pattern. Like Struts, Maverick also uses
extension mapping to delegate requests to the Maverick Servlet, and URL mapping to
delegate the request to the specific command. Instead of extending an Action, a
Maverick developer creates a class that implements the Command interface, which has a
single method, go(), which is analogous to execute(). Maverick also uses an XML based
configuration file. There seems to be two main differences between Maverick and Struts.
First, Maverick does not appear to offer the form processing and validation of Struts.
Second, Maverick supports other view technologies, such as Velocity and XSLT. Visit
the Maverick site at http://mav.sourceforge.net/ for more information.
Page 64
Conclusion
In the previous discussions, we have taken a tour of the Struts Web Application
Framework. This tour began with an explanation of the design philosophy of the
framework, which primarily is an implementation of the Controller aspect of the
Command Design pattern. Following was the first sample application, which illustrated
all of the parts necessary to build a Struts-based web application. The Struts Best
Practices section is somewhat unique in terms of works discussing how to use the Struts
Framework. These rules of thumb were learned through this author’s development
experience using Struts. However, these discussions were not intended to be a
comprehensive treatise on the subject. Their purpose was to demonstrate how Struts can
be used to create robust, extensible and maintainable web applications. For a more
comprehensive treatment of Struts, there are several books on this topic.
Perhaps one of the best initial introductions to Struts is covered in the book
Beginning JSP Web Development by Jayson Falkner, et. Al (Wrox). The book has
excellent introductory examples on the basics of the framework. Its main purpose is to
teach developers just enough to build Struts applications quickly, without discussing the
more complex aspects of the framework. This book, to use human development as an
analogy, takes the developer from birth to the ability to walk. One note of caution, this
edition was published in 2001, and covers Struts 1.0. There are significant differences
between Struts 1.0 and 1.1, and while the Struts designers were careful to make Struts 1.1
backward compatible with 1.0, this makes some of the examples in the book out of date.
However, the publisher released a new version of the book, Beginning JSP 2.0 in 2003. I
have not read this book, but it presumably updates its coverage of Struts.
Page 65
Struts Kick Start by James Turner and Kevin Bedell (Sams) is one of several
books dedicated to the Struts Framework. Like Beginning JSP Web Development, this
book is an introduction to the framework. However, being an entire book on the subject,
it does provide a more in depth coverage of how to build applications using Struts. The
book, though, is definitely skewed towards a beginner- level treatment of Struts as it
simply gives examples on how to use Struts, but not necessarily how to use Struts well.
Perhaps the best aspect of the book is its reference section of the Struts tag libraries. This
section is quite a time saver, as it enables the developer to quickly find information on a
custom tag and demonstrates its proper usage with examples. Also like Beginning JSP,
this book takes the developer from birth to being able to walk.
Perhaps the best book on this subject is Programming Jakarta Struts by Chuck
Cavaness (O’Reilly). This book features the most complete coverage of the Struts
Framework that not only describes how to build Struts applications, but also to build
well-designed Struts applications. Like all O’Reilly published books, Programming
Jakarta Struts goes beyond simply giving the developer examples and explanations of
how to use a given technology, but also explains the inner workings of the technology
and the theory behind its design, giving the developer a much greater understanding of
how the technology works. While some developers may consider this superfluous
information, understanding how the technology works is indispensable in designing
applications that make the best use of the technology. This book is also covers some of
the more sophisticated aspects of Struts, such as extending the framework in the cases
where custom behavior is required. Furthermore, this book provides more information in
the way of developing well-designed Struts applications. For example in the chapter on
Page 66
integrating Struts with EJBs, the author provides an excellent example and design pattern,
the EJBHomeFactory pattern, which increases the application’s efficiency when
interfacing with EJBs. However, these aspects of the book make it less palatable to
beginners, and, in fact, the first example application is a bit more complex than those in
the previously mentioned books. Again using the human development analogy, this book
takes the reader from being able to crawl to being able to run.
In closing, with web applications growing ever more complex, application
developers are faced with the difficult task of fulfilling the requirements of the
application, while also building in flexibility for the future. Fortunately, the Struts
Framework provides a powerful foundation upon which to build these applications.
Using the proven MVC architecture, the framework provides a flexible and extensible
backbone for the application. The form processing and form validation mechanisms not
only speed development but also result in more robust applications. While these are just
a few of the framework’s many features, they illustrate Struts’ remarkable capabilities.
Armed with the Struts Framework, developers have a mighty tool at their disposal,
enabling them to create the extensible, robust and maintainable web applications.
1 Denoncourt , “Struts: A Standard Architecture for Web Applications.” 2 Denoncourt , “Struts: A Standard Architecture for Web Applications.” 3 http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/web-tier/web-tier4.html. 4 Turner, James and Kevin Bedell. Struts Kick Start. P. 20. 5 Crawford, William and Jonathan Kaplan. J2EE Design Patterns. P. 39. 6 The Apache Struts Web Application Framework. http://struts.apache.org. 7 Gamma, Erich, et. Al. Design Patterns: Elements of Reusable Object Oriented Software. P. 233. 8 Crawford, William and Jonathan Kaplan. J2EE Design Patterns. P. 49. 9 Cavaness, Chuck. Programming Jakarta Struts. P. 44. 10 Crawford, William and Jonathan Kaplan. J2EE Design Patterns. P. 174. 11 http://www.onjava.com/pub/a/onjava/2002/10/30/jakarta.html?page=3 12 Cavaness, Chuck. Programming Jakarta Struts. P. 13. 13 Cavaness, Chuck. Programming Jakarta Struts. P. 15.
Page 67
Appendix A: Source Code and Example Applications To run the sample applications complete the following instructions. 1. Download and install the Java SDK (Software Development Kit)
Instructions for downloading and installing the Java SDK can be found at
http://java.sun.com. Be sure to download the full SDK, and not just the JRE (Java
Runtime Environment). Once installed, type the following in a command shell to check
that the installation is complete:
java –version
If successful, you should see the following:
However, if you get java: Command not found errors, you must add the java/bin directory
to your path. To set the path permanently in a UNIX/LINUX system, set the path in your
startup file. For C shell (csh), edit the startup file (~/.cshrc):
set path=(/usr/local/java/j2sdk1.4.2_03/bin $path)
For ksh, bash or sh, edit the profile file (~/.profile):
PATH=/usr/local/java/j2sdk1.4.2_03/bin:$PATH
For Windows, right-click “My Computer,” and select properties. In the System
Properties window, click the “Advanced” tab. In the Advanced tab, click the
“Environment Variables” button. In the Environment Variables Window, in the lower
text area labeled “System Variables,” locate the variable called “Path.” Highlight “Path”
Page 68
and then click “Edit.” Now add the path to the java/bin directory to the path variable as
follows:
2. For UNIX/LINUX systems, create a JAVA_HOME environment variable
JAVA_HOME must point to the top level directory of your java installation.
For C shell (csh), edit the startup file (~/.cshrc):
set JAVA_HOME=(/usr/local/java/j2sdk1.4.2_03)
For ksh, bash or sh, edit the profile file (~/.profile):
JAVA_HOME =/usr/local/java/j2sdk1.4.2_03
3. Download and install the Tomcat Servlet Container
Visit the Tomcat Home Page, http://jakarta.apache.org/tomcat/index.html, for the latest
version, as well as instructions for downloading and installing Tomcat. Note: when
Page 69
installing Tomcat 5 on a Windows machine, it will run as a service by default (meaning it
will run at startup by default). To override this behavior, after Tomcat is installed, go to
Control Panel à Performance and Maintenance à Services. In the Services Window,
right-click on the service called “Apache Tomcat,” and select properties. From there,
change the Startup Type to “Manual.”
To start Tomcat, open a command shell, navigate to the TOMCAT_HOME/bin directory,
where TOMCAT_HOME is the top level directory of the Tomcat installation. In the bin
directory, type the following command:
./startup.sh (UNIX/LINUX) or startup.bat (Windows)
To stop Tomcat, type ./shutdown.sh or shutdown.bat.
4. Download the Application Source Code and Prepackaged WAR File
Download and unzip the following file:
Page 70
http://www.u.arizona.edu/~bong/masters_project/examples.zip
Once unpacked, the examples file will have the directory structure shown below.
The prepackaged_app directory contains a precompiled,
prepackaged version of the web application (known as a
WAR file – Web Application ARchive ), called
examples.war. This file is ready for deployment within any
servlet container. The source folder contains the source
code used for the sample applications. The src directory
contains the source code for all of the Java classes used
within this application. The web.jsp directory contains all of the JSPs. Especially
important within the web.jsp folder is the WEB-INF folder, which contains the web.xml
file and the struts-config.xml file.
5. Deploying the Prepackaged Application
To deploy the prepackaged WAR file, copy the file examples.war file
(examples/prepackaged_app/examples.war) to the TOMCAT_HOME/webapps directory.
Then start Tomcat (Note: if running Tomcat 5.x, Tomcat can be running prior to copying
the war file to the webapps directory). Deployment of the application should complete
within a few seconds. Once Tomcat is running, point your browser to:
http://localhost:8080/examples/
6. Building the Application from the Source Code
We must first begin by obtaining the Struts binary, which can be found at
http://jakarta.apache.org/struts/. Download and unpack the Struts binary. Once unpacked,
Page 71
navigate to jakarta-struts-1.1/lib. In this directory, copy all 10 jar files (*.jar) to the
examples/source/lib directory. Second, copy all 6 Tag Library Descriptor files (*.tld) to
the examples/source/web.jsp/WEB-INF folder.
The application was designed to be built using Ant. To download and install Ant, visit
the official site at http://ant.apache.org/. For complete information on building Java
applications using Ant, refer to Ant: The Definitive Guide, by Jesse Tilly and Eric Burke
(O’Reilly).
Once Ant is installed, navigate to the examples/source folder. In this folder, there will be
two files, build.xml, and build.properties. The build.xml file is an Ant “build script” that
contains the instructions for building this project. The build.properties file contains key
value pairs that are variables upon which the build.xml file relies.
In order to use Ant to build the sample application, first open the build.properties file. It
contains a single key-value pair, TOMCAT_HOME. Edit this entry to point to the top
level directory of your Tomcat installation.
Now you are ready to build the application using Ant. The build.xml file contains three
main build targets, compile, build and deploy, with compile being the default target. The
“compile” target instructs Ant to compile all of the Java source code and placing the
compiled byte code in the examples/source/compile directory. The “build” target, which
depends on the compile target, creates the complete web application, with the correct
Page 72
directory structure, in the examples/source/build folder. Finally, the deploy target, which
depends on the build target, creates the examples.war file in the examples/source/deploy
directory, and copies the examples.war file to the TOMCAT_HOME/webapps directory,
thereby deploying the application. So to deploy the application, navigate to
examples/source and enter the following command:
ant deploy
The build.xml file also provides two targets for cleaning up: clean and undeploy. The
clean target deletes all files within the source folder that were created during the compile,
build or deploy targets. The undeploy target deletes the examples.war file and
corresponding examples folder from the TOMCAT_HOME/webapps directory, thereby
undeploying the application from Tomcat. One note, Tomcat must be stopped in order to
use the undeploy target.
Descriptions of all of these targets can be found with the following command:
ant -projecthelp
Building the application using Ant will allow you to make changes and experiment with
the source code of this application.
Page 73
Bibliography The Apache Struts Web Application Framework. http://struts.apache.org.
Cavaness, Chuck. Programming Jakarta Struts. O’Reilly & Associates, Inc. Sebastopol, CA. 2003.
Crawford, William and Jonathan Kaplan. J2EE Design Patterns. O’Reilly & Associates, Inc. Sebastopol, CA. 2003.
Denoncourt, Don. “Struts: A Standard Architecture for Web Applications.” http://www.e-promag.com/eparchive/index.cfm?fuseaction=viewarticle&ContentID=1742. April 2002.
Designing Enterprise Applications with the J2EE Platform, Second Edition. http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/DEA2eTOC.html
Falkner, Jayson, et. Al. Beginning JSP Web Development. Wrox Press Ltd. Birmingham, UK. 2001.
Gamma, Erich, et. Al. Design Patterns: Elements of Reusable Object Oriented Software. Addison-Wesley. Reading, Massachusetts. 1995.
Monson-Haefel, Richard. Enterprise JavaBeans. O’Reilly & Associates, Inc. Sebastopol, CA. 3rd Edition: 2001.
Tilly, Jesse and Eric M. Burke. Ant: The Definitive Guide. O’Reilly & Associates, Inc. Sebastopol, CA. 2002.
Turner, James and Kevin Bedell. Struts Kick Start. Sams Publishing. Indianapolis, Indiana. 2003.