Free Training


C Language  |  CSS  |  MainFrame  |  VBScript  |  PHP  |  XML  |  C++ Tutorials  |  Ajax  |  JavaScript  |  CSS3  |  UML  |  jQuery  |  Microsoft AJAX

Jakarta Struts Tutorials

 
Home Tech Articles Jakarta Struts
 

Struts Lesson II

 

LESSON II - Introduction

Just like in Lesson 1, the application you will create mimics entering an employee into a database. The user will be required to enter an employee's name, age, and department.

Concepts introduced in Lesson II:

Validation (in ActionForm)
ErrorMessages
ActionMessages
Pre-poluation of form
Dealing with Errors thrown in Actions
html:select and html:options tag

In case you are starting with this lesson and skipping Lesson I, remember this Lesson does assume you understand the concepts of Lesson I.

If you have already completed Lesson I you can simply make a copy of struts_lesson_1 in your webapps directory and rename it struts_lesson_2. As you procede through this lesson simply modify any existing code so that it matches what is presented in this lesson. (Obviously you will have to add any new components as well). Of course you can always just copy and paste from this lesson.

LESSON II - 1 - Setup

These Lessons assume you are using Tomcat 4.1 and have downloaded Struts 1.1-rc2 or greater.

Directory structure under tomcat should look like:

webapps
|
|
struts_lesson_2
|
|
--- WEB-INF
|
|--- classes
| |
| --- net
| |
| -- exforsys
|--- lib
|
--- src
|
--- net
|
-- exforsys

Copy .tld files from struts into struts_lesson_2 application: 

In the struts/contrib/struts-el/lib you should find the following files which you need to add to your struts_lesson_2/WEB-INF directory.


c.tld
struts-bean-el.tld
struts-html-el.tld
struts-logic-el.tld

Copy .jar files from struts into struts_lesson_2 application:

In the same struts/contrib/struts-el/lib directory, copy the following jar files to the struts_lesson_2/WEB-INF/lib directory.

commons-beanutils.jar
commons-collections.jar
commons-digester.jar
commons-logging.jar
jstl.jar
standard.jar
struts-el.jar
struts.jar

(Note we are using the tld files and jars in the contributed struts-el directory since this will force us to use the standard JSTL tags whenever possible).

LESSON II - 2 - Create web.xml

Create web.xml file in struts_lesson_2/WEB-INF/ :

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

<!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>Struts lesson 2</display-name>

<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>ApplicationResources</param-value>
</init-param>
<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>3</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>3</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/do/*</url-pattern>
</servlet-mapping>

<!-- The Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<!-- tag libs -->
<taglib>
<taglib-uri>struts/bean-el</taglib-uri>
<taglib-location>/WEB-INF/struts-bean-el.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>struts/html-el</taglib-uri>
<taglib-location>/WEB-INF/struts-html-el.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>struts/logic-el</taglib-uri>
<taglib-location>/WEB-INF/struts-logic-el.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>jstl/c</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>

</web-app>

LESSON II - 3 - Create struts-config.xml

Create struts-config.xml file in struts_lesson_2/WEB-INF/ :

<!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 Bean Definitions -->
<form-beans>
<form-bean name="employeeForm" type="net.exforsys.EmployeeForm"/>
</form-beans>

<!-- Global forwards -->
<global-forwards>
<forward name="error" path="/error.jsp"/>
</global-forwards>

<!-- Action Mapping Definitions -->
<action-mappings>

<action path="/setUpEmployeeForm"
type="net.exforsys.SetUpEmployeeAction"
name="employeeForm"
scope="request"
validate="false"
>
<forward
name="continue"
path="/employeeForm.jsp"/>
</action>

<action path="/insertEmployee"
type="net.exforsys.InsertEmployeeAction"
name="employeeForm"
scope="request"
validate="true"
input="/employeeForm.jsp"
>
<forward
name="success"
path="/confirmation.jsp"/>
</action>

</action-mappings>
<!-- message resources -->
<message-resources
parameter="ApplicationResources"
null="false" />
</struts-config>

Comments: First thing to notice is the addition of a global-forward. Global forwards allow your entire application to share the same forward. In our case since we defined one for "error," any time one of our actions comes up with an error we can have our mapping findForward("error") and be forwarded to an error page. If we didn't use a global forward but wanted to always forward to this error page in case of an error, then all of our action mappings would have to declare this forward.

The action mapping: action path="/setupEmployeeForm" calls SetupEmployeeAction which will be used to populate some information that our employeeForm.jsp will need.

The action mapping: action path="/insertEmployee" has validate="true" and also has input="/employeeForm.jsp." Setting validate equal to true will make sure the validate method that we will write in our ActionForm is called. The input attribute will also be necessary so that if validation returns any errors our application will know what page (or possibly an action) to return to. In this case if validating returns any errors the user is returned back to the same employeeForm.jsp page.

LESSON II - 4 - Create ActionForm

Create EmployeeForm

package net.exforsys;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;

import javax.servlet.http.HttpServletRequest;

public class EmployeeForm extends ActionForm {

private String name;
private String age;
private String department;

public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
public void setDepartment(String department) {
this.department = department;
}

public String getName() {
return name;
}
public String getAge() {
return age;
}
public String getDepartment() {
return department;
}

public ActionErrors validate( ActionMapping mapping, HttpServletRequest request ) {
ActionErrors errors = new ActionErrors();

if ( getName() == null || getName().length() < 1 ) {
errors.add("name",new ActionError("error.name.required"));
}

if ( getAge() == null || getAge().length() < 1 ) {
errors.add("age",new ActionError("error.age.required"));
}
else {
try {
Integer.parseInt( getAge() );
} catch( NumberFormatException ne ) {
errors.add("age",new ActionError("error.age.integer"));
}
}
return errors;
}
}

The EmployeeForm contains a validate method which it inherits from ActionForm. By setting validate to true in the struts-config file we can have our form elements validated here before we get to the Action class. In the case above we are validating to make sure that the user enters a name and an age and that the age is an integer.

The ActionErrors object holds a Map of all the ActionError objects that we add to it. Using some Struts tags we can then easilly display these messages nicely on a JSP page (in the case of validation errors the display is usually back on the same JSP form that the user tried to submit).

Looking more closely at adding an ActionError to our ActionErrors object, if we want to add to add an error message if our name does not validate how we want we can do:

new ActionError("error.name.required")

Later when we create our ApplicationResources.properties you will see the line:

error.name.required=Name is required.

What happens is our ActionError will look up error.name.required in this ApplicationRescources.properties file and display the appropriate message when we using error/message tags on our JSP pages.

Note: Before you create validate methods in your ActionForms make sure to first check out the next Lesson. There is a much cleaner (and easier) way to handle your form validation which is described in the next lesson.

LESSON II - 5 - Create EmployeeDTO

We need a Data Transfer Object like we used in Lesson I.

Create EmployeeDTO:

package net.exforsys;

public class EmployeeDTO {
private String name;
private int age;
private int department;

public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setDepartment(int department) {
this.department = department;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getDepartment() {
return department;
}
}

LESSON II - 6 - Create DepartmentBean

Our JSP page is going to display a list of departments that the user can select from. Create a DepartmentBean to hold the department information: id and description.

Create DepartmentBean:

package net.exforsys;

public class DepartmentBean {

private int id;
private String description;

public DepartmentBean() {
}
public DepartmentBean( int id, String description ) {
this.id = id;
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

LESSON II - 7 - Create DatabaseException

DatabaseException is an Exception that our Model/business layer can throw so we need to create this object.

Create DatabaseException:

package net.exforsys;

public class DatabaseException extends Exception {
public DatabaseException() {
super();
}

public DatabaseException(String message) {
super(message);
}
}


LESSON II - 8 - Create EmployeeService

The EmployeeService class is an object that represents our bridge to the Model layer of our architecture. You can build your business/model components in many different ways, using different design patters. Since Struts really only deals with the View and Controller aspects of an MVC architecture we only need a basic class here to mimic some business requirements that, in real life, we would handle in other components.

The EmployeeService is going to take care of doing the insertEmployee operation and we're also going to add in a method to return a list of DepartmentBeans. I also want to simulate what would happen if an Exception was thrown when calling a method from our service, so a method doInsert() was added in this class which will throw a DatabaseException if you uncomment out the appropriate line.

Create EmployeeService:

package net.exforsys;

import java.util.Collection;
import java.util.ArrayList;

public class EmployeeService {
public EmployeeDTO insertEmployee( EmployeeDTO employee ) throws DatabaseException {
//in real life call other business classes to do insert
try {
doInsert( employee );
}
catch( DatabaseException de ) {
//log error
throw de;
}
return employee;
}
public Collection getDepartments() {
//call business layer to return Collection of Department beans
//since we aren't dealing with the model layer, we'll mimic it here
ArrayList list = new ArrayList(3);
list.add( new DepartmentBean( 1, "Accounting"));
list.add( new DepartmentBean( 2, "IT"));
list.add( new DepartmentBean( 3, "Shipping"));
return list;
}

//this wouldn't be in this service class, but would be in some other business class/DAO
private void doInsert( EmployeeDTO employee ) throws DatabaseException {
//to test an Exception thrown uncomment line below
//throw new DatabaseException();
}
}

LESSON II - 9 - Create SetUpEmployeeAction

Instead of going directly to a JSP page with a form, you often might want to pre-populate some information the JSP page might need or you may need to pre-populate a corresponding FormBean with some information. One way you can do this is by submitting to an Action class before forwarding to your JSP page.

In lesson 1, we had the following mapping in our struts-config file:

<action path="/setupEmployeeForm" forward="/employeeForm.jsp"/>

Before we forward directly to the employeeForm.jsp as shown above, we are going to first submit to a SetupEmployeeAction which you see in the mapping:

<action path="/setupEmployeeForm"
type="net.exforsys.SetUpEmployeeAction"
name="employeeForm"
scope="request"
validate="false"
>
<forward
name="continue"
path="/employeeForm.jsp"/>
</action>

Create SetUpEmployeeAction:

package net.exforsys;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Collection;

public final class SetUpEmployeeAction extends Action {

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

EmployeeService service = new EmployeeService();
Collection departments = service.getDepartments();
HttpSession session = request.getSession();
session.setAttribute( "departments", departments );
EmployeeForm employeeForm = (EmployeeForm)form;
employeeForm.setDepartment("2");
return (mapping.findForward("continue"));
}
}

Notice we have put a Collection of DepartmentBeans into session scope and we also set the department value of our ActionForm to "2".

NOTE: It is not really a good idea to hard code your forwards as String literals like I have been doing. It's better to define these as constants in some constants Interface. If ever you decide to change the names that you use for these forwards in your struts-config.xml file you then only need to change the forward definitions in one other class.

LESSON II - 10 - Create InsertEmployeeAction

Create InsertEmployeeAction:

package net.exforsys;

import org.apache.struts.action.*;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class InsertEmployeeAction extends Action {

public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
EmployeeService service = new EmployeeService();
EmployeeForm employeeForm = (EmployeeForm) form;
EmployeeDTO employeeDTO = new EmployeeDTO();
BeanUtils.copyProperties( employeeDTO, employeeForm );
try {
service.insertEmployee( employeeDTO );
ActionMessages messages = new ActionMessages();
ActionMessage message = new ActionMessage("message.employee.insert.success",employeeDTO.getName() );
messages.add( ActionMessages.GLOBAL_MESSAGE, message );
saveMessages( request, messages );
request.setAttribute("employee",employeeDTO);
return (mapping.findForward("success"));
}
catch( DatabaseException de ) {
ActionErrors errors = new ActionErrors();
ActionError error = new ActionError("error.employee.databaseException");
errors.add( ActionErrors.GLOBAL_ERROR, error );
saveErrors( request, errors );
return (mapping.findForward("error"));
}
}
}


The InsertEmployeeAction is responsible for calling our EmployeeService class and passing it the EmployeeDTO to perform the insert. First we use BeanUtils to copy the properties from the EmployeeForm to our EmployeeDTO. Since insertEmployee() can throw a DatabaseException we have to handle that.

*** In the next lesson you will see how Exception handling is greatly improved by using declarative Exception handling, which eliminates the need of try/catch blocks and redundant error handling code.

Notice we use the same type of ActionErrors set up as we did in the EmployeeForm, the only difference being we are adding the error with the key ActionErrors.GLOBAL_ERROR which is a generic way you can add error messages to display at the top of a results page.

If a DatabaseException is not thrown we are assuming the insert was successful and we set up an ActionMessage and add it to an ActionMessages instance which we will use on our confirmation.jsp page.

LESSON II - 11 - Create Application Resources

Create ApplicationResources.properties file in struts_lesson_2/WEB-INF/classes/ and in src/ :

#-- titles --
title.error=ERROR PAGE
title.employeeApp=EMPLOYEE APPLICATION
title.employee.employeeform=EMPLOYEE FORM
title.employee.insert.confirmation=EMPLOYEE INSERT CONFIRMATION

#-- messages --
message.employee.insert.success=You successfully added the employee {0}.

#-- error messages --
error.employee.databaseException=Database error please contact support center.
error.name.required=Name is required.
error.age.required=Age is required.
error.age.integer=Age must be a whole number.

#-- errors headers
errors.validation.header=Validation Error:

#-- buttons --
button.submit=SUBMIT


LESSON II - 12 - Create Style Sheet

Create struts.css file in struts_lesson_2/ :

body { font-family: arial,sans-serif;
color: black;
background-color: #F1FFD2;
font-size: 13pt;
}
#success { color: green; font-weight: bold; }
#error { color: red; font-weight: bold; }
#errorsHeader { color: red; font-weight: bold; font-size: 14pt; }


LESSON II - 13 - Create index.jsp

Create index.jsp file in struts_lesson_2/ :

<%@ taglib uri="struts/bean-el" prefix="bean" %>
<%@ taglib uri="struts/html-el" prefix="html" %>
<html>
<head>
<link href="<html:rewrite page="/struts.css" />" rel="stylesheet" type="text/css">
<title><bean:message key="title.employeeApp"/></title>
</head>
<body>
<h1><bean:message key="title.employeeApp"/></h1>
<br>
<html:link page="/do/setUpEmployeeForm">Add An Employee</html:link>
</body>
</html>

LESSON II - 14 - Create employeeForm.jsp

Create employeForm.jsp in struts_lesson_2/ :

<%@ taglib uri="struts/bean-el" prefix="bean" %>
<%@ taglib uri="struts/logic-el" prefix="logic" %>
<%@ taglib uri="struts/html-el" prefix="html" %>
<%@ taglib uri="jstl/c" prefix="c" %>
<html>
<head>
<link href="<html:rewrite page="/struts.css" />" rel="stylesheet" type="text/css">
<title><bean:message key="title.employee.employeeform"/></title>
</head>
<body>

<h1><bean:message key="title.employee.employeeform"/></h1>

<logic:messagesPresent>
<span id="errorsHeader"><bean:message key="errors.validation.header"/></span>
<html:messages id="error">
<li><c:out value="${error}"/></li>
</html:messages>
<hr>
</logic:messagesPresent>

<html:form action="insertEmployee" focus="name">
<table>
<tr>
<td >Name:</td>
<td><html:text property="name"/></td>
</tr>
<tr>
<td>Age:</td>
<td><html:text property="age"/></td>
</tr>
<tr>
<td>Department:</td>
<td>
<html:select name="employeeForm" property="department">
<html:options collection="departments" property="id" labelProperty="description"/>
</html:select>
</td>
</tr>
</table>
<html:submit><bean:message key="button.submit"/></html:submit>
</html:form>
</body>
</html>

Few new things introduced here. First notice how we are displaying error messages (ActionError messages we created). The logic:messagesPresent tag checks to see if any ActionMessages are in scope (remember ActionErrors is a subclass of ActionMessages). The html:messages tag then will loop through all messages in scope. Setting the id="error" just gives us a handle to use for our JSTL c:out tag to display the message in the current iteration of our loop.

Note: You'll see some Struts developers simply use the <html:errors/> tag to display error messages. This does work well and the tag will also look in your resources file for an errors.header and errors.footer definition and use them as a header and footer for your error message display. You'll also have to add some HTML code such as <LI> or <BR> before all of your validation errors definitions in the resources file. Personally I'd rather have a bit more code as above and keep the formatting of HTML done by style sheets vs having HTML formating code in your resources file.

The Department form field provides a Select box and we simply use the struts html:select and html:options tags to generate our options. The html:select tag will set the initial selection to whatever the department property is set to in our EmployeeForm (remember we set it to "2" in our SetUpEmployeeAction). The select tag will also populate the department property in our EmployeeForm with whatever option the user selects.

html:options will loop through whatever collection we provide. In this case we had put the Collection departments into request scope in our EmployeeSetUpAction. The property="id" portion will set the value of the option tag for that iteration to the id property in the current DepartmentBean and the labelValue will set what the user sees- in this case the 'description' field in the current DepartmentBean. Just view the source code at runtime and you'll see what gets generated from this JSP based on the select and options tags.

Finally, to test how the validation works try not entering a name and/or age. (Also try entering a non-number for age).

LESSON II - 15 - Create confirmation.jsp

Create confirmation.jsp in struts_lesson_2 /:

<%@ taglib uri="struts/bean-el" prefix="bean" %>
<%@ taglib uri="struts/logic-el" prefix="logic" %>
<%@ taglib uri="struts/html-el" prefix="html" %>
<%@ taglib uri="jstl/c" prefix="c" %>
<html>
<head>
<title><bean:message key="title.employee.insert.confirmation"/></title>
<link href="<html:rewrite page="/struts.css" />" rel="stylesheet" type="text/css">
</head>
<body>
<h1><bean:message key="title.employee.insert.confirmation"/></h1>
<br>
<logic:messagesPresent message="true">
<html:messages id="message" message="true">
<span id="success"><c:out value="${message}"/></span><br>
</html:messages>
</logic:messagesPresent>

<br><br>
NAME: <c:out value="${employee.name}"/><br>
AGE: <c:out value="${employee.age}"/>
</body>
</html>


Here we will display the ActionMessages created in our InsertEmployeeAction. It works the same way as it does on the employeeForm.jsp except we added message="true" to the logic:messagesPresent portion. We need to add message="true" so that the tag will look for ActionMessages and not actionErrors.

If this was going to be a generic confirmation page used for other processes, we would remove the code specific to Employee such as the title and the NAME and AGE displays. Then we could use this page to display whatever message we wanted by just setting up an appropriate ActionMessage before forwarding to this page.

LESSON II - 16 - Create error.jsp

Create error.jsp in struts_lesson_2/ :

<%@ taglib uri="struts/bean-el" prefix="bean" %>
<%@ taglib uri="struts/html-el" prefix="html" %>
<%@ taglib uri="jstl/c" prefix="c" %>
<html>
<head>
<link href="<html:rewrite page="/struts.css" />" rel="stylesheet" type="text/css">
<title><bean:message key="title.error"/></title>
</head>
<body>
<h1><bean:message key="title.error"/></h1>

You have reached this page because of the following error(s):<br>
<br>

<html:messages id="error">
<span id="error"><c:out value="${error}"/></span><br>
</html:messages>

</body>
</html>

This page is really simple. Here we just use the <html:messages> tag to loop through and display the error messages that are in the ActionErorrs object in scope. I didn't bother to use <logic:messagesPresent> construct since I'm not using headers or anything that could be displayed even if an ActionError was not present.

This ends Lesson II


Read Next: Struts Lesson III



 

 

Comments


kartik sridhar said:

  This is an excellent tutorial for anyperson wanting to develop a struts based application. Gives a complete overview of how to go about things !! very informative !!
October 15, 2007, 4:04 am

Post Your Comment:

Members Please Login
Your Name:*
e-mail ID:(required for notification)*
Image Verification: 
 
 Subscribe    

Weekly Offers

Sponsored Links