Building Web Applications with Apache Struts
This tutorial takes us through the basics of using NetBeans IDE to develop web applications that make use of the Apache Struts framework. The Struts framework enables us to create maintainable, extensible, and flexible web applications based on standard technologies, such as JSP pages, JavaBeans, resource bundles, and XML.
Struts framework employs the Model-View-Controller (MVC) architecture. When we use Struts, the framework provides we with a controller – a Servlet, which is defined in the Struts libraries that are included in the IDE, and which is automatically registered in the web.xml deployment descriptor when we indicate that we want to use Struts. The Struts Servlet uses the struts-config.xml file to map incoming requests to a Struts “action” class. An action class receives a Struts action form bean as input, which serves as a transfer object between the action class and the view, which is typically a JavaServer Pages (JSP) page. Because many web applications use JSP pages for the view, Struts provides custom tag libraries which facilitate interaction with HTML forms.
At the end of this topic, we will build a very simple login page. We will have a simple MVC application that displays a login page and returns a success page upon submitting data that passes validation. We will learn several basic features provided by Struts, as well as how these features are implemented using the IDE. Specifically, we will use Struts tags in JSP pages, maintain user data with a Struts ActionForm
bean, and implement forwarding logic using a Struts Action
object. We will see how to implement simple validation to our application, including setting up warning message for a failed login attempt.
Softwares Used
To complete this topic, we will need the following software resources :—
- NetBeans IDE 8.2
- Java Development Kit (JDK) 8
- GlassFish server 4.1.1
Building a Struts Application
In the IDE, we create here a Struts application in the same way as we create any other web application in the IDE – using the New Web Application wizard, with the additional step of indicating that we want the Struts libraries and configuration files to be included in our application.
1. Choose File > New Project. Under Categories, select Web. Under Projects, select Web Application and click Next.
2. In the Name and Location panel, do the following:
- Under Project Name, enter CustomStrutsApp.
- Change the Project Location to any directory on the computer. From now on, this directory is referred to as $PROJECTHOME.
3. In the Server and Settings panel, select the server to which we want to deploy our application. Only servers that are registered with the IDE are listed. (To register a server, click Add next to the Server drop-down list). We have selected here GlassFish Server with Java EE version 5. Notice that the Context Path is /CustomStrutsApp. Click Next.
4. In the Frameworks panel, select Struts 1.3.10:
Do not change any of the values in the lower region of this panel. They serve the following purposes:
- Action Servlet Name: Hardcoded specification of the name of the servlet entry for the Struts action servlet. The web.xml deployment descriptor contains a servlet entry for the action servlet, specifying the appropriate Struts specific parameters, such as the name of the servlet class and the path to the struts-config.xml configuration file.
- Action URL Pattern: Allows the appropriate patterns which should be mapped to the Struts action controller to be specified. This generates a corresponding web.xml servlet mapping entry to map the specified URI pattern to the action servlet. By default, only the *.do pattern is mapped.
- Application Resource: Lets us specify the resource bundle which will be used in the struts-config.xml file for localizing messages. We change its default value and it becomes techguru.myapp.struts.ApplicationResource.
- Add Struts TLDs: Lets us generate tag library descriptors for the Struts tag libraries. A tag library descriptor is an XML document which contains additional information about the entire tag library as well as each individual tag. In general, this is not necessary, because we can refer to on-line URIs rather than local TLD files.
5. Click Finish. The IDE creates the project folder in our file system. As with any web application in the IDE, the project folder contains all of our sources and the IDE’s project metadata, such as the Ant build script. However, our web application in addition has all of the Struts libraries on its classpath. Not only are they on the application’s classpath, but they are included in the project and will be packaged with it later when we build the project.
The project opens in the IDE. We can view its logical structure in the Projects window and its file structure in the Files window. For example, the Projects window should now look as follows:
In the Configuration Files node, the application includes all the Struts-specific configuration files, of which struts-config.xml is the most important. Also, within the Configuration Files node, in order to handle Struts processing, the Struts controller servlet is mapped in the web.xml deployment descriptor:
<web-app>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
Above, the Struts servlet (org.apache.struts.action.ActionServlet) is specified as the servlet that controls all requests for the mapping .do. In addition, the web.xml file specifies that the Struts servlet is configured by means of the struts-config.xml file that is found in the WEB-INF folder.
Creating JSP Pages
Begin by creating two JSP pages for the application. The first displays a form. The second is the view returned when login is successful.
- Creating a Login Page
- Creating a Success Page
Creating a Login Page
- Right-click the
CustomStrutsApp
project node, choose New > JSP, and name the new filelogin
. Click Finish. Thelogin.jsp
file opens in the Source Editor. - In the Source Editor, change the content of both the
<title>
and<h2>
tags toLogin Form
. - Add the following two taglib directives to the top of the file:
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
Many web applications use JSP pages for views in the MVC paradigm, so Struts provides custom tag libraries which facilitate interaction with HTML forms. These can be easily applied to a JSP file using the IDE’s support for code completion. When we type in the Source Editor, the IDE provides us with code completion for Struts tags, as well as the Struts Javadoc. We can also invoke code completion manually by pressing Ctrl-Space:
ActionForm
bean) with the data collected from the form. The html taglib offers an interface between the view and other components necessary to a web application. For example, below we replace common htmlform
tags with Struts’<html:form>
tags. One benefit this provides is that it causes the server to locate or create a bean object that corresponds to the value provided forhtml:form
‘saction
element.
4. Below the <h2>
tags, add the following line:
<html:form action="/login">
<html:submit value="Login" />
</html:form>
Using the Palette (Window > Palette) in the right region of the IDE, we can drag a Table item from the HTML category to a point just above the <html:submit value="Login"/>
line. The Insert Table dialog box displays. Setting the rows to 3
, columns to 2
, and leave all other settings at 0
and clicking OK we will get a tabular structure. See it below.
This code (login.jsp) after some modification would look like this:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<html:form action="/login">
<table border="0">
<tbody>
<tr>
<td colspan="2">
<bean:write name="LoginForm" property="error" filter="false"/>
</td>
</tr>
<tr>
<td>Enter your name:</td>
<td><html:text property="name" /></td>
</tr>
<tr>
<td>Enter your email:</td>
<td><html:text property="email" /></td>
</tr>
<tr>
<td></td>
<td><html:submit value="Login" /></td>
</tr>
</tbody>
</table>
</html:form>
</body>
</html>
The
html:text
element enables us to match the input fields from the form with properties in the form bean that will be created in the next step. So for example, the value of property
must match a field declared in the form bean associated with this form using the <bean:write>
tag. This will be added in the final step for “error message” (shown in bold font).Creating a Success Page
- Right-click the
MyStrutsApp
project node, choose New > JSP, and name the new filesuccess
. In the Folder field, click the adjacent Browse button and selectWEB-INF
from the dialog that displays. Click Select Folder to enter WEB-INF in the Folder field. Any files contained in the WEB-INF folder are not directly accessible to client requests. In order forsuccess.jsp
to be properly displayed, it must contain processed data. Click Finish. - In the Source Editor, change the content of the newly created page to the following:
<head>
<title>Login Success</title>
</head>
<body>
<h2>Congratulations!</h2>
<p>You have successfully logged in.</p>
<p>Your name is: .</p>
<p>Your email address is: .</p>
</body> - Add a bean taglib directive to the top of the file:
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
- Add the following
<bean:write>
tags (changes in bold):<p>Your name is: <bean:write name="LoginForm" property="name" />.</p> <p>Your email address is: <bean:write name="LoginForm" property="email" />.</p>
By employing the<bean:write>
tags, we make use of the bean taglib to locate theActionForm
bean we are about to create, and display the user data saved forname
andemail
.
This code (success.jsp) after some modification would look like this:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<html>
<head>
<title>Login Success</title>
</head>
<body>
<h2>Congratulations!</h2>
<p>You have successfully logged in.</p>
<p>Your name is: <bean:write name="LoginForm" property="name" />.</p>
<p>Your email address is: <bean:write name="LoginForm" property="email" />.</p>
</body>
</html>
Creating an ActionForm
Bean
A Struts ActionForm
bean is used to persist data between requests. For example, if a user submits a form, the data is temporarily stored in the form bean so that it can either be redisplayed in the form page (if the data is in an invalid format or if login fails) or displayed in a login success page (if data passes validation).
- Right-click the
MyStrutsApp
project node and choose New > Other. Under Categories choose Struts, then under File Types choose Struts ActionForm Bean. Click Next. - Type in
LoginForm
for the Class Name. Then selecttechguru.myapp.struts
in the Package drop-down list and click Finish. The IDE creates theLoginForm
bean and opens it in the Source Editor. By default, the IDE provides it with aString
calledname
and anint
callednumber
. Both fields have accessor methods defined for them. Also, the IDE adds a bean declaration to thestruts-config.xml
file. If we open thestruts-config.xml
file in the Source Editor, we can see the following declaration, which was added by the wizard:<form-beans> <form-bean name="LoginForm" type="techguru.myapp.struts.LoginForm" /> </form-beans>
The IDE provides navigation support in the
struts-config.xml
file. Hold down the Ctrl key and hover our mouse over theLoginForm
bean’s fully qualified class name. The name becomes a link, enabling us to navigate directly to the class in the Source Editor: - In the
LoginForm
bean in the Source Editor, create fields and accompanying accessor methods that correspond to thename
andemail
text input fields that we created inlogin.jsp
. Becausename
has already been created in theLoginForm
skeleton, we only need to implementemail
.Add the following declaration beneath
name
(changes in bold):private String name; private String email;
To create accessor methods, place the cursor on
email
and press Alt-Insert.Select Getter and Setter, then in the dialog that displays, select
email : String
and click Generate. Accessor methods are generated for theemail
field.
Creating an Action
Class
The Action
class contains the business logic in the application. When form data is received, it is the execute()
method of an Action
object that processes the data and determines which view to forward the processed data to. Because the Action
class is integral to the Struts framework, NetBeans IDE provides us with a wizard.
- In the Projects window, right-click the
MyStrutsApp
project node and choose New > Other. From the Struts category choose Struts Action and click Next. - In the Name and Location panel, change the name to
LoginAction
. - Select
techguru.myapp.struts
in the Package drop-down list. - Type
/login
in Action Path. This value must match the value we set for theaction
attribute of the<html:form>
tags inlogin.jsp
. Make sure settings appear as in the screenshot below, then click Next. - In the third step of the wizard, we are given the opportunity to associate the
Action
class with a form bean. Notice that theLoginForm
bean we previously created is listed as an option for ActionForm Bean Name. Make the following adjustments to the panel:- Delete the forward slash for the Input Resource field
- Set Scope to Request (Session is the default scope setting in Struts.)
- Deselect the Validate ActionForm Bean option
Click Finish. The LoginAction
class is generated, and the file opens in the Source Editor. Also note that the following action
entry is added to the struts-config.xml
file:
<action-mappings> <action name="LoginForm" path="/login" scope="request" type="techguru.myapp.struts.LoginAction" validate="false"/> <action path="/Welcome" forward="/welcomeStruts.jsp"/> </action-mappings>
The name
and scope
attributes apply to the form bean that is associated with the action. Specifically, when an incoming request matches /login
, the Struts framework automatically instantiates a LoginForm
object and populates it with the form data sent in the request. The default value of validate()
is set to true
. This tells the framework to call the validate()
method of the form bean. We deselected this option in the wizard however because we will hand-code simple validation in the next step, which does not require the validate()
method.
Implementing Validation
In the Source Editor, browse through the LoginAction
class and look at the execute()
method:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return mapping.findForward(SUCCESS); }
Notice the definition of SUCCESS
, listed beneath the LoginAction
class declaration:
private final static String SUCCESS = "success";
Currently, the mapping.findForward
method is set to unconditionally forward any request to an output view called success
. This is not really desirable; we want to first perform some sort of validation on the incoming data to determine whether to send the success
view, or any different view.
- Accessing Bean Data and Preparing a Forwarding Condition
- Setting Up an Error Message
Accessing Bean Data and Preparing a Forwarding Condition
- Type in the following code within the body of the
execute()
method:// extract user data LoginForm formBean = (LoginForm)form; String name = formBean.getName(); String email = formBean.getEmail();
In order to use the incoming form data, we need to takeexecute()
‘sActionForm
argument and cast it asLoginForm
, then apply the getter methods that we created earlier. - Type in the following conditional clause to perform validation on the incoming data:
// perform validation if ((name == null) || // name parameter does not exist email == null || // email parameter does not exist name.equals("") || // name parameter is empty email.indexOf("@") == -1) { // email lacks '@' return mapping.findForward(FAILURE); }
At this stage, theexecute()
method should look as follows:public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // extract user data LoginForm formBean = (LoginForm) form; String name = formBean.getName(); String email = formBean.getEmail(); // perform validation if ((name == null) || // name parameter does not exist email == null || // email parameter does not exist name.equals("") || // name parameter is empty email.indexOf("@") == -1) { // email lacks '@' return mapping.findForward(FAILURE); } return mapping.findForward(SUCCESS); }
- Add a declaration for
FAILURE
to theLoginAction
class (changes in bold):private final static String SUCCESS = "success"; private final static String FAILURE = "failure";
Using the above logic, the execute()
method forwards the request to the success
view if the user provides an entry for both name
and email
fields, and the email entered contains an ‘@’ sign. Otherwise, the failure
view is forwarded. As will be demonstrated below in Adding forward
Entries to struts-config.xml
, we can set the failure
view to point back to the form page, so that the user has another chance to enter data in the correct format.
Setting Up an Error Message
If the login form is returned, it would be good to inform the user that validation failed. We can accomplish this by adding an error
field in the form bean, and an appropriate <bean:write>
tag to the form in login.jsp
. Finally, in the Action
object, set the error message to be displayed in the event that the failure
view is chosen.
- Open
LoginForm
and add anerror
field to the class:// error message private String error;
- Add a getter method and a setter method for
error
, as shown above. - Modify the setter method so that it appears as follows:
public void setError() { this.error = "<span style='color:red'>Please provide valid entries for both fields</span>"; }
- Open
login.jsp
and make the following changes:<html:form action="/login"> <table border="0"> <tbody> <tr> <td colspan="2"> <bean:write name="LoginForm" property="error" filter="false"/> </td> </tr> <tr> <td>Enter your name:</td> <td><html:text property="name" /></td> </tr>
- In
LoginAction
, within theif
conditional clause, add a statement to set the error message before forwarding thefailure
condition (changes in bold):if ((name == null) || // name parameter does not exist email == null || // email parameter does not exist name.equals("") || // name parameter is empty email.indexOf("@") == -1) { // email lacks '@' formBean.setError(); return mapping.findForward(FAILURE); }
Our completed LoginAction
class should now appear as follows:
public class LoginAction extends org.apache.struts.action.Action { private final static String SUCCESS = "success"; private final static String FAILURE = "failure"; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // extract user data LoginForm formBean = (LoginForm)form; String name = formBean.getName(); String email = formBean.getEmail(); // perform validation if ((name == null) || // name parameter does not exist email == null || // email parameter does not exist name.equals("") || // name parameter is empty email.indexOf("@") == -1) { // email lacks '@' formBean.setError(); return mapping.findForward(FAILURE); } return mapping.findForward(SUCCESS); } }
These two Java codes after final modification are given below.
LoginForm.java
package techguru.myapp.struts;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
public class LoginForm extends org.apache.struts.action.ActionForm {
private String name;
private String email;
// error message
private String error;
public String getName() {
return name;
}
public void setName(String string) {
name = string;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getError() {
return error;
}
public void setError() {
this.error = "<span style='color:red'>Please provide valid entries for both fields</span>";
}
public LoginForm() {
super();
// TODO Auto-generated constructor stub
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if (getName() == null || getName().length() < 1) {
errors.add("name", new ActionMessage("error.name.required"));
// TODO: add 'error.name.required' key to your resources
}
return errors;
}
}
LoginAction.java
package techguru.myapp.struts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class LoginAction extends org.apache.struts.action.Action {
private static final String SUCCESS = "success";
private final static String FAILURE = "failure";
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
// extract user data
LoginForm formBean = (LoginForm) form;
String name = formBean.getName();
String email = formBean.getEmail();
// perform validation
if ((name == null) || // name parameter does not exist
email == null || // email parameter does not exist
name.equals("") || // name parameter is empty
email.indexOf("@") == -1) { // email lacks '@'
formBean.setError();
return mapping.findForward(FAILURE);
}
return mapping.findForward(SUCCESS);
}
}
Adding forward
Entries to struts-config.xml
In order for the application to match JSP pages with forwarding conditions returned by LoginAction
‘s execute()
method, we need to add forward
entries to the struts-config.xml
file.
- Open
struts-config.xml
in the Source Editor, right-click anywhere in theaction
entry forLoginForm
, and choose Struts > Add Forward. - In the Add Forward dialog box, type
success
in Forward Name. Enter the path tosuccess.jsp
in the Resource File field (i.e.,/success.jsp
). The dialog box should now look as follows:
Click Add. Note that the followingforward
entry was added tostruts-config.xml
(changes in bold):<action name="LoginForm" path="/login" scope="request" type="com.myapp.struts.LoginAction" validate="false"> <forward name="success" path="/success.jsp"/> </action>
- Perform the same action to add a forward entry for
failure
. Set the Resource File path to/login.jsp
. The followingforward
entry is added tostruts-config.xml
(changes in bold):<forward name="success" path="success.jsp"/> <forward name="failure" path="/login.jsp"/>
Configuring and Running the Application
The IDE uses an Ant build script to build and run our web application. The IDE generated the build script when we created the project, basing it on the options we entered in the New Project wizard. Before we build and run the application, we need to set the application’s default entry point to login.jsp
. See the steps below:
- Setting the Welcome Page
- Running the Application
Setting the Welcome Page
- In the Projects window, double-click the
web.xml
deployment descriptor. The tabs listed along the top of the Source Editor provide you with an interface to theweb.xml
file. Click on the Pages tab. In the Welcome Files field, enterlogin.jsp
.
Now click on the Source tab to view the file. Note that login.jsp
is now listed in the welcome-file
entry:
<welcome-file>login.jsp</welcome-file>
Running the Application
- In the Projects window, right-click the project node and choose Run. The IDE builds the web application and deploys it, using the server we specified when creating the project. The browser opens and displays the
login.jsp
page. We type in some data that should fail validation, i.e., either leave either field blank, or enter an email address with a missing ‘@’ sign:
When we click Login, the login form page redisplays, containing an error message:
Try entering data that should pass validation. Upon clicking Login, we are presented with the success page:
Note :~
We can also add a simple stylesheet to this project by selecting the Web Pages node in the Projects window of IDE and press Ctrl-V). The file (e.g. ext.css) is added to our project.
We can link the stylesheet to our JSP pages by simply adding a reference between the <head>
tags of both login.jsp
and success.jsp
:
<link rel="stylesheet" type="text/css" href="ext.css">
Conclusion
This concludes the Introduction to the Struts framework in NetBeans IDE. This tutorial demonstrates how to construct a simple web MVC application in NetBeans IDE using the Struts Framework, and introduces the IDE’s interface for developing web applications. We have also seen how to use Struts tags in JSP pages, temporarily store user data in a Struts ActionForm
bean, and implement forwarding logic using a Struts Action
object. We have implemented simple validation to our application, including setting up warning message for a failed login attempt.
The basic purpose of the Java Servlets in Struts is to handle requests made by the client or by web browsers. Here, JSPs are used to design the dynamic web pages; while Servlets help to route request which has been made by the web browsers to the appropriate JSP. The use of Servlet as a router helps to make the web applications easier to design, create, and maintain. Struts framework is purely based on MVC design pattern.