Key Takeaways
- Jakarta Struts is a J2EE Web application framework that helps developers create structured, maintainable web applications by utilizing a Model-View-Controller (MVC) architecture.
- Struts offers a balanced approach to learning through example applications that are neither too trivial nor overly complex, facilitating easier understanding without overwhelming the beginner.
- The framework includes minimal external dependencies in its setup, making it accessible and straightforward for newcomers to download and run the example applications without hassle.
- Key components required to build applications using Struts include Jakarta Struts 1.1 or later, MySQL 4.0.18 or later, MySQL Connector/J 3.0, and Apache Tomcat 5.0.28 or later.
- Struts applications are built around a central configuration file (`struts-config.xml`) that dictates the flow of the application and maps user requests to specific Action classes.
- The tutorial provides a practical introduction to Struts by guiding the reader through the development of a functional online discussion forum, emphasizing hands-on learning.
Just over a year ago, I started to learn how to use the Jakarta Struts J2EE Web application framework. During this journey, I realised that I found some aspects of Struts confusing at first, and that many other developers might well suffer the same difficulties. This article is an attempt to address that problem.
Many good books on the framework are now available, and the online documentation is quite mature for an open-source project; in spite of this, I have often had to refer to a wide variety of resources in order to build a good understanding of Struts. And there are still some areas in which the documentation is sparse.
However, this article isn’t an attempt to provide a definitive source of documentation. Rather, it’s intended to allow Struts newcomers to hit the ground running, to get a feel for what the framework is like, and understand what it can and can’t do.
Of the Struts example applications I’ve seen, most have been either too trivial or too complex, the complex ones having lots of external dependencies that must be resolved before the example can even be compiled and run. In this tutorial, I’ll attempt to hit a spot between these two extremes. I’ve deliberately created an application that has the minimum of external dependencies, so you should be able to actually run the code without too much difficulty! Everything you’ll need is open-source and can be downloaded for free — one of the advantages of J2EE.
The example application that we’re going to build is an online discussion forum, which I’ve given the rather generic name of “Web Forum”. I’ve chosen an application of this type because it should be familiar, and because you can create quite a lot of functionality without the application becoming too complex. Our Web Forum is fully functional and reasonably well-debugged; hopefully, it should be fun to learn how it all fits together.
It’s important to note that this code shouldn’t be considered an example of a production-quality J2EE system. In particular, the exception handling is extremely — and deliberately — simplistic. This isn’t a tutorial on persistence strategies, object-oriented design or Web application GUI design; it’s simply a vehicle for learning Struts. Undoubtedly, some aspects of what I’ve created here could be improved upon, but that’s not important right now. Let’s get started!
Ingredients
To start, let’s talk about the ingredients you’ll need to create the application. I developed the Web Forum using Oracle JDeveloper because I’m familiar with that IDE. You can use your Java tool of choice for writing the code.
Apart from the source code itself, here’s a list of the other elements you’ll need:
- Jakarta Struts 1.1 or later (Web application framework)
- MySQL 4.0.18 or later (Database server)
- MySQL Connector/J 3.0 (JDBC driver for MySQL)
- Apache Tomcat 5.0.28; or later (J2EE Web container)
I also used the MySQL Control Centre to create the database and control the database server, although the MySQL command line can be used if that’s what you prefer. Note that, because this isn’t a MySQL tutorial, some familiarity with MySQL is assumed. As a minimum, you’ll need to know how to start and stop the database server, how to create a new database, and you’ll need the ability to run a provided SQL script to create tables.
Source Code Downloads
These are the files we’ll use through this tutorial.
- Download struts_webforum_jars.zip (855 KB)
- Download struts_webforum_src.zip (52 KB)
- Download struts_webforum_war.zip (1,122 KB)
To reduce the download sizes, I’ve split the Struts JAR files that go into WEB-INF/lib
into a separate download. The pre-compiled WAR file is also available.
The Application
Let’s take a brief tour of the Web Forum’s functionality. I’d thought about some of this before I wrote a line of code, but other aspects became apparent as I was building the app. The fact that there are plenty of existing forums to serve as inspiration certainly helped!
Upon starting the application, the user is presented with a list of topics, and information about each topic, such as:
- the date and time it was posted
- the number of replies
- the topic’s author
The 16 most recent topics are displayed, ordered from the newest to the oldest:
At this point, the user can click on a topic to view it, along with any follow-up replies:
The date and time of the post are displayed, as well as the authors’ names, the dates on which they registered with the system, and the number of posts they’ve made. The user can register if he or she is a new user, or log in if they’re an existing user. A user must be logged in before he or she can create a new topic or a new reply. The user can elect to log in automatically during registration, or at the Login screen:
The auto-log in feature uses a cookie that expires after thirty days. During registration, the user must provide a unique user name, his or her real name (or screen alias) and a password. All of these fields are mandatory, apart from surname, and the user must confirm his or her chosen password by entering it twice:
Upon successful registration, the user is taken back to the topics screen, on which he or she can create a new topic:
Both the subject and message fields are mandatory. Users are allowed to use the HTML <b>
, <i>
or <u>
tags for basic text formatting (bold, italic or underline) within the message field. Once the user creates a new topic, he or she is taken back to the Topics screen.
The topic hyperlinks ensure that users can see which topics they’ve already read. The hyperlinks to topics that the user has already read are displayed using the browser’s visited link colour. However, each topic link contains an encoding of the number of replies that have been made to that topic. Therefore, new replies to a topic cause that topic’s hyperlink to be displayed using the browser’s unvisited colour. Thanks to Joel Spolsky for this brilliantly simple idea.
When viewing a topic, users can compose a new reply; again, the message field is mandatory and accepts basic formatting tags:
The Persistence Layer
The persistence layer in the Web Forum application is very simple. There are two database tables: one for posts and another for users. The posts table stores topics and their replies. The users table stores the details of the registered users of the application. These tables are accessed using data access objects (DAOs), which are just Java objects that perform simple object-relational mapping. In other words, they take a Java object and persist its contents into a table, and vice-versa.
The DAOs have the appropriate SQL statements embedded into them, although this probably isn’t good practice: database administrators like such things to be externalised so they can be tuned. I have also created for all the DAOs an abstract superclass, which contains utility methods that obtain a JDBC connection from a data source and close database resources, etc.
This is the schema for the posts table:
The most interesting feature of this table is the ParentID column, which links replies back to their parent topic. The Subject column is null when a post is a reply and not a topic. I’m storing the number of replies to each topic in a ReplyCount column. Technically, this is redundant because it could be calculated, but I’m storing it because MySQL doesn’t currently support nested SQL SELECT statements.
The CreationDate column originally auto-updated, but I changed this because the date and time of the original topic were being updated to the current date and time whenever a reply was added to that topic.
Here’s the schema for the users table:
The MySQL SQL script that creates these tables is in src/sql/create_tables.sql
. This script can be run from a query window in MySQL Control Centre. Note that it assumes the existence of a database named webforum
. Also, because we’re not building the functionality to create new topics in this article, you’ll have to manually insert some test data so that you have some topics to display.
Business Object Layer
I didn’t have to do much thinking to come up with the business objects in the application: they’re pretty obvious. There are classes for individual posts and users, as well as collections of posts and users. The class model — excluding the Struts classes — is shown below:
Some of these classes, such as UserCookie, will be covered later in the series. As can be seen from the class model, a Posts class contains a collection of Post objects at runtime, and features methods for retrieving this collection and for adding a new post. The Post class itself has attributes that correspond to the columns in the posts table, and overloaded constructors that are invoked depending upon whether the post is a topic or a reply. Accessors and mutators (getters and setters) are not shown on the class model.
The Users class contains a collection of User objects at runtime, and features methods for retrieving this collection and for adding new users. In fact, I deprecated the getUsers method after I discovered that I’d coded it, but didn’t actually call it from anywhere! The User class has attributes that mirror the columns in the users table, as well as a convenient getDisplayName method that returns the user’s first names and surname with a space character in the middle.
Project Structure
The organisation of the source code folder tree is shown below:
Java source code files go under src, and the Java package hierarchy is rooted at com.johntopley.webforum
. The public_html
folder corresponds to the root of the Web application.
I always put JSPs under pages because this allows them to be protected by the Web container using J2EE declarative security. Anything in public_html
and its sub-folders really should be regarded as public.
I also like to separate the Struts configuration files into a config folder, although usually you’ll see them stored directly under WEB-INF
.
The application entry point is public_html/index.jsp
, which is declared as a welcome file in the web.xml
Web application deployment descriptor. Let’s take a look at index.jsp:
<%@ taglib uri=http://jakarta.apache.org/struts/tags-logic
prefix="logic" %>
<logic:redirect forward="ViewTopics" />
All this page does is transfer control to the Struts framework, because if we’re going to use a framework, we want to be using it as soon as possible. Struts ships with a number of JSP tag libraries (taglibs), and here we’re using the logic
taglib. This handles the conditional generation of output text, looping over object collections and application flow management. In this case, the redirect
tag performs an HTTP redirect to a Struts logical URL. More about that in a moment.
One thing to note about the Web Forum application is that we’re using the Servlet 2.3 specification syntax to reference the taglibs using URIs, rather than referring to TLD files in the web.xml
file. This is documented in section 5.4.3 of The Struts User’s Guide.
The Heart of Struts
The heart of Struts is the config/struts-config.xml
file. This file defines the flow of the application and tells Struts which classes to use for what. The Struts ActionServlet is a controller class that reads this file and receives all incoming requests for the Web application. The ActionServlet needs to be configured as a servlet in the web.xml file:
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/config/struts-config.xml</param-value>
</init-param>
.
.
.
The main part of the struts-config.xml
file covered by this article is:
<struts-config>
<global-forwards>
<forward name="ViewTopics" path="/ViewTopics.do"/>
</global-forwards>
<action-mappings>
<action
path="/ViewTopics"
type="com.johntopley.webforum.controller.action.ViewTopicsAction"
scope="request">
<forward
name="Topics"
path="/WEB-INF/pages/topics.jsp"
/>
</action>
</action-mappings>
</struts-config>
Struts introduces a layer of indirection into Web applications because it uses logical URLs. This means that the address you see in the browser’s address bar does not correspond to the physical location of that resource on the Web server. This allows developers to move resources around easily without breaking things. The Struts name for the association of a logical name with a resource is an ActionForward, often just called a Forward. The Struts configuration file contains a global-forwards
section that allows the configuration of Forwards that are available throughout a Struts application. These are effectively the application’s entry points.
Another key Struts concept is the Action class. Actions are simply Java servlets, so anything that a servlet can do, an Action class can do. Actions are used to process requests for specific URLs. Generally, they should act as a thin layer around the business objects layer, which does the real work.
Actions are referred to by ActionMappings, which, again, are logical URLs. The Struts convention is that ActionMappings end with .do
. The web.xml
file needs to be configured so that the Struts ActionServlet is used to process any URL matching the pattern *.do
, as shown here:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Flow Of Events
Let’s recap where we’ve got to so far. The index.jsp
page redirects to the ViewTopics
global ActionForward, and this passes control to the /ViewTopics
Action. Struts knows which Action class to invoke because the type
attribute in the ActionMapping gives the fully-qualified name of the associated Java class, which must inherit from org.apache.struts.action.Action
, and must have an execute
method with the following signature:
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception
The mapping
parameter is an ActionMapping object that represents the ActionMapping that invoked this Action. The form
parameter is used if an HTML form is associated with the request. The request
and response
parameters highlight the fact that an Action class is just a specialisation of a servlet.
The Web Forum application uses a BaseAction
abstract class that inherits from the org.apache.struts.action.Action
class mentioned earlier. The other Action classes within the application inherit from BaseAction
, so it serves as an extension point at which functionality common to all Action classes can be added if required. This should be considered good practice.
Important: Action classes should not use instance variables, as they are not thread-safe. State should be shared by storing values in the request, session, or servlet context objects, depending on their scope.
This is the execute method in ViewTopicsAction.java
:
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception
{
request.setAttribute(KeyConstants.POST_LIST_KEY,
new PostsDAO().getTopics());
return mapping.findForward(ForwardConstants.TOPICS_PAGE);
}
A new PostsDAO
object is instantiated and its getTopics
method is called. This method uses the following SQL statement to query the posts
table:
SELECT p.PostID, p.Subject, p.ReplyCount, p.UserID, p.CreationDate
FROM Posts p
WHERE p.ParentID = 0
ORDER BY p.CreationDate DESC
The SQL WHERE
clause ensures that only topics are selected — not replies. The getTopics
method returns an instance of the Posts
class, i.e., an ordered collection of Post
objects. This instance is stored in the HTTP request under the key referred to by the KeyConstants.POST_LIST_KEY
constant. The JSP that displays the list of topics will use this Posts
object stored in the request. Finally, the findForward
method of the ActionMapping
class is invoked. This takes, as a String parameter, the name of a Struts Forward to pass control to. The ForwardConstants
class contains all of the Forward names used within the Web Forum.
As well as global ActionForwards, Struts also has local ActionForwards. These are simply Forwards whose scope is local to a single ActionMapping. In other words, they are not globally visible within the application.
Important: Struts gives precedence to local ActionForwards over global ActionForwards.
A local Forward is used within the /ViewTopics
ActionMapping to hold a reference to the JSP that displays the list of topics:
<forward name="Topics" path="/WEB-INF/pages/topics.jsp"/>
We finally have a physical path to a page! topics.jsp is included with the source code downloads for this article (linked above), so you can see the list of topics.
Building the Application Using Apache Ant
If you’re not familiar with the Apache Ant build tool, spend a few moments reading Apache Ant Demystified, which should get you up to speed quickly. The build file for the Web Forum application shouldn’t look too unfamiliar, although it does use some more of Ant’s built-in tasks. Another key difference is that, because this application is rather more than a simple “Hello World” app, we have to deal with the classpath. The good news is that Ant makes this easy too!
To compile the application, we need to have the J2EE libraries on the classpath. Fortunately, the Apache Tomcat Web container already comes with these libraries, so the question becomes one of referencing. Rather than hard-code Tomcat’s installation folder into our build file, let’s set up an operating system environment variable called TOMCAT_HOME
that refers to the root of the Tomcat installation folder, e.g. C:Program FilesApache Software FoundationTomcat 5.0. The procedure for doing this is exactly the same as the one we used to create the ANT_HOME
and JAVA_HOME
environment variables in Apache Ant Demystified.
With that done, we can add a couple of property declarations to the top of our build.xml file:
<project name="webforum" default="dist" basedir=".">
<description>
Build file for the Struts WebForum application.
</description>
<property environment="env" />
<property name="tomcat.dir" location="${env.TOMCAT_HOME}" />
The first property declaration simply says that we want to use the identifier env to refer to operating system environment variables. The second property sets the location of tomcat.dir
to whatever env.TOMCAT_HOME
is. Thus, from now on, when we want to refer to Tomcat’s installation folder, we can simply use ${tomcat.dir}
.
Later, we set up a property named j2ee.lib.dir
that points to the location at which Tomcat keeps its J2EE libraries:
<property name="j2ee.lib.dir" location="${tomcat.dir}/common/lib" />
We now need to deal with setting up the classpath. Ant provides several ways to do this; we’re going to set up a path structure which we’ll assign the unique ID of classpath:
<path id="classpath">
<pathelement path="${j2ee.lib.dir}/servlet-api.jar" />
<fileset dir="${web.lib.dir}">
<include name="**/*.jar" />
</fileset>
</path>
The snippet above shows that there are two elements within this path structure. The first element is an individual reference to the servlet-api.jar
file, which is referenced using the pathelement
tag. The second element is Ant’s fileset
tag, which in our case has been set up to include all the JAR files within the folder represented by the ${web.lib.dir}
property. To clarify, we’re setting up a classpath that includes Tomcat’s servlet-api.jar
file and all the JAR files within public_html/WEB-INF/lib
.
In order to use this classpath, the javac
task within our build file’s compile
target contains a nested classpath
tag, which has a refid
attribute set to the name of the path structure that we defined above:
<javac srcdir="${src.dir}" destdir="${classes.dir}">
<classpath refid="classpath" />
</javac>
That’s how we make sure that the Java compiler can find all the libraries it needs to compile our application. The javadocs
target also makes use of this classpath, which we defined once and can then re-use anywhere within the build file.
The next task is to package the compiled code into a WAR file (Web ARchive) so that it can be run by Tomcat. We create a dist
target that looks like this:
<target name="dist" depends="compile"
description="Create the binary distribution.">
<delete dir="${dist.dir}" />
<mkdir dir="${dist.dir}" />
<war basedir="${web.src.dir}"
destfile="${dist.warfile}"
compress="true"
webxml="${web.src.dir}/WEB-INF/web.xml"
excludes="**/web.xml">
<classes dir="${classes.dir}" />
</war>
</target>
Note that this target depends on the compile
target, which makes sure that we always compile the code before we try to create a distribution in which it’s included! After deleting and recreating the output directory, we use Ant’s war
task to create the WAR file. The basedir
attribute points to a property that represents the public_html
folder — the root of our application. The destfile
attribute simply specifies the name of the output WAR file that’s to be created. We can choose whether or not to compress the file using — you guessed it — the compress
attribute; the webxml
attribute tells the war
task where our Web application deployment descriptor is located. The use of this last attribute automatically ensures that the web.xml
file is included in the WAR file. Thus, we use the excludes
attribute to tell Ant not to generate a warning because it thinks we’re trying to include web.xml
twice. Finally, a nested classes
element tells the war
task where our compiled classes are located.
If you run the dist
target and then open /dist/WebForum.war
using WinZip (or download the pre-compiled version from this site), you can see that the layout within the archive precisely mirrors the source code tree mentioned earlier. The compiled code has been placed within WEB-INF/classes
and the Struts and MySQL JDBC JAR files are within WEB-INF/lib
.
Running The Application Using Apache Tomcat
These instructions describe how to configure the Apache Tomcat Web container to use a MySQL data source. They’ve been tested using Tomcat 5.0.28 and MySQL 4.0.18, but should work with Tomcat 4 and other versions of MySQL, too. Note that TOMCAT_HOME
refers to the root of the Tomcat installation folder, e.g. C:Program FilesApache Software FoundationTomcat 5.0.
Copy the MySQL JDBC driver JAR file to TOMCAT_HOME/common/lib
.
Edit the TOMCAT_HOME/conf/server.xml
file so that it includes a context definition for the Web Forum application. The main elements of the server.xml
file are shown below:
<Server>
<Service>
<Engine>
<Host>
:
<Context>
<!-- Existing context definitions. -->
</Context>
:
<!--
| Paste Web Forum context definition (shown below) here.
+-->
</Host>
</Engine>
</Service>
</Server>
Add the following context definition for the Web Forum application to the appropriate place in the server.xml
file, substituting your MySQL webforum database user name and password where the italics indicate:
<!--
| Begin WebForum context definition.
+-->
<Context path="/WebForum" docBase="WebForum"
reloadable="true">
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="localhost_WebForum." suffix=".txt" timestamp="true"/>
<Resource name="jdbc/WebForumDS" auth="Container"
type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/WebForumDS">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!--
| The JDBC connection URL for connecting to your MySQL DB.
| The autoReconnect=true argument to the URL makes sure that the
| MySQL JDBC Driver will automatically reconnect if mysqld closed
| the connection. mysqld by default closes idle connections after
| 8 hours.
+-->
<parameter>
<name>url</name>
<value>jdbc:mysql://localhost:3306/webforum?
autoReconnect=true</value>
</parameter>
<!--
| MySQL username and password for DB connections.
+-->
<parameter>
<name>username</name>
<value>username</value>
</parameter>
<parameter>
<name>password</name>
<value>password</value>
</parameter>
<!--
| Class name for MySQL JDBC driver.
+-->
<parameter>
<name>driverClassName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<!--
| Maximum number of DB connections in pool. Make sure you
| configure your mysqld max_connections large enough to handle
| all of your DB connections. Set to 0 for no limit.
+-->
<parameter>
<name>maxActive</name>
<value>100</value>
</parameter>
<!--
| Maximum number of idle DB connections to retain in pool.
| Set to 0 for no limit.
+-->
<parameter>
<name>maxIdle</name>
<value>30</value>
</parameter>
<!--
| Maximum time to wait for a DB connection to become available
| in ms, in this example 10 seconds. An exception is thrown if
| this timeout is exceeded.
| Set to -1 to wait indefinitely.
+-->
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
</ResourceParams>
</Context>
<!--
| End WebForum context definition.
+-->
Next, copy the dist/WebForum.war
we created earlier to TOMCAT_HOME/webapps
. This archive will be automatically expanded and the Web Forum deployed the next time Tomcat is started.
After starting the MySQL and Tomcat servers, the application can be accessed in a Web browser using the URL: http://localhost:8080/WebForum/
We’ve covered a lot of important ground with this instalment. Next time, we’ll take a look at how the Topics page works, and move on from there. See you then!
Frequently Asked Questions (FAQs) about Struts
What is the difference between Struts 1 and Struts 2?
Struts 1 was the initial release of the Apache Struts framework, which was a revolutionary tool for building web applications. However, Struts 2, the successor to Struts 1, is more powerful and flexible. It incorporates many enhancements and new features such as a simpler, more modular architecture, improved tag features, better integration with Spring, and support for annotation-based configuration. Struts 2 also uses the WebWork framework which makes it more efficient and easier to use.
How does Struts work in Java?
Struts is a framework used for developing Java web applications. It uses the Model-View-Controller (MVC) design pattern to separate the application logic into three interconnected components. The Model represents the application’s data structure and business logic. The View is the user interface or the presentation of data. The Controller handles the user’s requests and orchestrates the model and view. Struts provide various components like Action classes, ActionForm beans, and configuration files to help in implementing MVC in a web application.
How can I install and set up Struts?
To install and set up Struts, you first need to download the latest version of the Struts library from the official Apache Struts website. Once downloaded, extract the zip file and add the Struts libraries to your Java project. You also need to set up the Struts configuration file (struts-config.xml) which is used to define the routing of the application. This file should be placed in the WEB-INF directory of your application.
What are the key components of Struts?
Struts framework is composed of several key components. These include the Controller Servlet, Action Classes, ActionForm Classes, struts-config.xml, and JSP pages. The Controller Servlet is the heart of the Struts framework that routes requests to appropriate handlers. Action Classes contain the business logic that is executed for each user request. ActionForm Classes are used to capture input from the form fields in the View. The struts-config.xml file is the configuration file that maps user requests to corresponding Action classes.
How can I handle form data in Struts?
In Struts, form data is typically handled using ActionForm classes. These classes encapsulate the form data and provide a way to validate and process the data. Each form in the application has a corresponding ActionForm class. When a form is submitted, the Struts framework automatically populates the corresponding ActionForm object with the form data. This object can then be used in the Action class to process the form data.
How does Struts handle validation?
Struts provides a robust validation framework that can be used to validate user input. This is done using validation rules defined in an XML file. When a form is submitted, the Struts framework checks these rules and if any rule is violated, it generates an error message which is then displayed on the form.
What is the role of the struts-config.xml file?
The struts-config.xml file is a crucial component of any Struts application. It is used to define the routing of the application. This file contains information about the Action classes, ActionForm classes, and their mappings. It also defines the navigation rules for the application.
How can I integrate Struts with other Java frameworks?
Struts can be easily integrated with other Java frameworks like Spring, Hibernate, etc. This is done by configuring the Struts framework to use the services provided by these other frameworks. For example, to integrate Struts with Spring, you need to configure the Struts Action classes as Spring beans in the Spring configuration file.
What are Struts tags?
Struts tags are custom JSP tags provided by the Struts framework. These tags simplify the development of JSP pages by providing functionality for form processing, data output, error handling, etc. Struts tags are grouped into several tag libraries including the HTML tag library, Bean tag library, Logic tag library, and Tiles tag library.
How can I handle exceptions in Struts?
Struts provides a centralized exception handling mechanism. You can define exception handling rules in the struts-config.xml file. When an exception occurs, the Struts framework checks these rules and forwards the request to an appropriate error handling page. This makes exception handling in Struts applications consistent and easy to manage.
John is a J2EE developer based in the UK, with a particular interest in user interface development and Web standards. John's Weblog and knowledge base is at http://www.johntopley.com/.