Struts

75
The Struts Web Application Framework Architecture and Best Practices By Brian Ong Master’s Project Department of Management Information Systems May 9, 2004

description

Struts Web Framework

Transcript of Struts

Page 1: Struts

The Struts Web Application Framework

Architecture and Best Practices

By

Brian Ong

Master’s Project Department of Management Information Systems

May 9, 2004

Page 2: Struts

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 3: Struts

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 4: Struts

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 5: Struts

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 6: Struts

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

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 8: Struts

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 9: Struts

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 10: Struts

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 11: Struts

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 12: Struts

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 13: Struts

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 14: Struts

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 15: Struts

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 16: Struts

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 17: Struts

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 18: Struts

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 19: Struts

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 20: Struts

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 21: Struts

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 22: Struts

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 23: Struts

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 24: Struts

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 25: Struts

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 26: Struts

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> &nbsp;&nbsp; <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 27: Struts

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 28: Struts

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 29: Struts

Page 27

<tr> <td width="150px">Projected Graduation:</td> <td> <bean:write name="studentInformation" scope="session" ignore="true" property="graduationSemester" /> &nbsp;&nbsp; <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 30: Struts

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 31: Struts

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 32: Struts

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 33: Struts

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 34: Struts

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 35: Struts

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 36: Struts

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 37: Struts

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 38: Struts

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 39: Struts

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 40: Struts

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 41: Struts

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 42: Struts

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 43: Struts

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 44: Struts

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 45: Struts

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 46: Struts

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 47: Struts

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 48: Struts

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 49: Struts

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 50: Struts

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 51: Struts

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 52: Struts

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 53: Struts

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 54: Struts

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 55: Struts

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 56: Struts

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 57: Struts

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 58: Struts

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 59: Struts

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 60: Struts

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 61: Struts

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 62: Struts

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 63: Struts

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 64: Struts

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 65: Struts

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 66: Struts

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 67: 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 68: Struts

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 69: Struts

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 70: Struts

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 71: Struts

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 72: Struts

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 73: Struts

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 74: Struts

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 75: Struts

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.