Code and Lesson - Andy Ma


Introduction
This application is based on Struts2 web framework. I try to use the most easy way to create the appliation. So if you've learned Struts1.x, here I'll show you how easy it is to migrate to Struts2. Also assumes the basic knowledge of java web application.
This download application demonstrates a very simple application that handles your typical CRUD - Create, Retrieve, Update, Delete operations.
Special thanks to Rick Reumann, for the service layer and dao layer of this application use his code in struts crud example on this site.
Requirements
This application requires an application server that implements the Servlet 2.4 and JavaServer Pages 2.0 specifications. The examples should all work on Tomcat 5.x and Tomcat 6.0
Jars
This application uses following jars:
  • struts2-core-2.0.6.jar
  • xwork-2.0.1.jar
  • commons-logging-1.1.jar.jar
  • freemarker-2.3.8.jar
  • ognl-2.6.11.jar
You can just use the jars that come with the source code for this application, but if you want the latest versions:

Try to visit:http://struts.apache.org/downloads.html, find the latest release of struts2 and download the struts-2.x.x-lib.zip

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >

    <display-name>Struts2CRUD</display-name>
    <description/>
   
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
        org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <error-page>
        <error-code>404</error-code>
        <location>/pagenotfound.jsp</location>
    </error-page>
    
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/error.jsp</location>
    </error-page>

</web-app>
Notice that Struts2 uses a Filter named FilterDispatcher to play the role of Controller in a mvc pattern, meanwhile Struts1.x uses a Servlet to do the job. Just as other web application, if we use a filter, we should config the filter and filter mapping elements as shown above. And typically, these two elements are the only infomation related to Struts2 we'd define in the web deploy descriptior.
struts.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

   <include file="struts-default.xml"/>

    <package name="default" extends="struts-default">

        <action name="getAllEmployees" method="getAllEmployees" class="net.reumann.struts2demo.action.EmployeeAction">
            <result name="success">/resources/employees.jsp</result>
        </action>
        
        <action name="setUpForInsertOrUpdate" method="setUpForInsertOrUpdate" class="net.reumann.struts2demo.action.EmployeeAction">
            <result name="success">/resources/employeeForm.jsp</result>
        </action>

        <action name="insertOrUpdate" method="insertOrUpdate" class="net.reumann.struts2demo.action.EmployeeAction">
            <result name="success" type="redirect-action" >getAllEmployees</result>
            <result name="input">/resources/employeeForm.jsp</result>
        </action>
        </action-mappings>

        <action name="delete" method="deleteEmployee" class="net.reumann.struts2demo.action.EmployeeAction">
            <result name="success" type="redirect-action" >getAllEmployees</result>
        </action>
    </package>   
</struts>
struts.xml file is the place where we configure the components and action mappings for our Struts2 application. It plays the same role as struts-config.xml in a Struts application. One thing to remind you is that the struts.xml file is in the classes folder not WEB-INF folder.

Let's take a look at this file. The first two elements is about versioning and encoding info and DTD for the XML file. Then followed the include tag. This tag is intend to modularize the application by including other configuration files. Here, since we are creating a single module app, we just include a struts-default.xml. As the name implies, struts-default.xml file contains the basic and some default configure info for Struts2 app. Which is packed into struts2-core jar file in the lib. We don't have to modify it. Just remember to include it in your struts.xml like we show here.

The package node in the file is for the purpose of defining a logic path of a group related actions. Here we don't want to specify a path so we just use "default" and we want use some default params in the struts-default.xml so we extends it as we do in our everyday java programming to reuse code. See those action definations, there's some thing special. Right! the result type definition. That gives us a clear idea that Struts2 have more than one result type. If we do not specify result type in the result element , we actually use the default type. That means we send the action result jsp file directly. Another type we use in our app is "redirect-action" type, just as the name says, it redirects the action result to another action.

struts.propertifies
struts.custom.i18n.resources=MessageResources
struts.ui.theme=simple
We use this file to tell struts2 we would use a file named MessageResources as our global message resource.(For Struts2 supports multi resource files).The second line we specify that we want struts tag use simple theme of jsp tag. Without this line, struts2's jsp tag would output some css style on it's tag by default. But I think we in many cases want control the style ourselves. So I choose to use simple theme.
EmployeeAction

public class EmployeeAction extends ActionSupport {
	
    private static EmployeeService empService = new EmployeeDaoService();
    
    private static DepartmentService deptService = new DepartmentDaoService();
    
    private Employee employee;
    
    private List<Employee> employees;
    
    private List<Department> departments;
    
    
	public String getAllEmployees(){
    	 employees = empService.getAllEmployees();
    	 return "success";
      }
      
        public String setUpForInsertOrUpdate(){
		prep();
		if (employee != null && employee.getEmployeeId() != null) {
			employee = empService.getEmployee(employee.getEmployeeId());
		}
		return "success";
	} 
	  
	public String insertOrUpdate(){
        	if(!validationSuccessful()){
    	    	return "input";
        	}else{  
    		    if (employee.getEmployeeId() == null) {
    		    	empService.insertEmployee(employee);
    	    	} else {
    		    	empService.updateEmployee(employee);
    	        }
        	
        	}
    	return "success"; 
      }
    
        public String deleteEmployee(){
		empService.deleteEmployee(employee.getEmployeeId());
		addActionMessage("Ok");
		return "success";
	}
    
        private void prep(){
		departments = deptService.getAllDepartments();
		Map session = ActionContext.getContext().getSession();
		session.put("departments", departments); 
		}
	 
	private boolean validationSuccessful(){ 
		    if(employee.getFirstName()==null||employee.getFirstName().trim().length()<1){
    		    addActionMessage("FirstName is required");
    	    } 
    	    if(employee.getLastName()==null||employee.getLastName().trim().length()<1){
    		addActionMessage("LastName is required"); 
    		} 
    		if(employee.getAge()!=null){  
    		  if(employee.getAge()>90||employee.getAge()<15){
    		  addActionMessage("Make sure the age U input is correct");
    	    	}
             }
        	if(this.hasActionMessages()){
    		return false; 
    		}else{ 
    		return true;
    	}
    		
       }
    
  
    
	public List getEmployees() {
		return employees;
	}
	public void setEmployees(List employees) {
		this.employees = employees;
	}
	public Employee getEmployee() {
		return employee;
	}
	public void setEmployee(Employee employee) {
		this.employee = employee;
	}
	
}
To create a struts2 action class, the most convinent way is to extends the ActionSupport class, which has implements the necessary Interface which Struts2 requires .
  • Then pay attention to red-colored method in our EmployeeAction class, those are methods we configured in the action mapping. struts2 have one requirements on mapping methods, that is return a String type as the result in accordance to the result name we configured in struts.xml file. Take the first method getEmployees() as an example. we return "success". so, to 'recieve' this result we define <result name="success"> as the result of getEmployee action in our struts.xml.

  • Another intersting thing you can see in the class is that there are several getter and setter for the properties of the class. That's because Struts2 needs those methods to automatically obtain value to transfer to pages(In our app, it is List employees) or store value from page form(In our app, it is employee). I think there is an easy way to understand is that you imagine the page is get value from our action class and set value to it. While In Struts1.x we usually set value and get value from formbeans not directly from action class.

  • Also notice that how we send value into Session Object in the prep() method. The recommanded way to use Session,Request or Response in Struts2 is through ActionContext Class. This class wrapped those objects and provides convinent way to access them.

    To simplify this application, I choose validate the user's input manually . Though Struts2 contains a validation framework. I think next time I'll show you how to use the validation framework in Struts2.

employees.jsp

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%> 
<html>
<head>
    <link href="<s:url value="/resources/main.css"/>" rel="stylesheet" type="text/css"/>
    <title><s:text name="label.employees"/></title>
</head>
<body>
<div class="titleDiv"><s:text name="application.title"/></div>	
<h1><s:text name="label.employees"/></h1>		   
	<table width=600 align=center>
	    <tr> <s:url id="insert" action="setUpForInsertOrUpdate"/>
	         <td><s:a href="%{insert}">Click Here to Add New Employee</s:a></td>
	     </tr>
	</table><br/>
<table align=center class="borderAll">
    <tr>
        <th><s:text name="label.firstName"/></th>
        <th><s:text name="label.lastName"/></th>
        <th><s:text name="label.age"/></th>
        <th><s:text name="label.department"/></th>
        <th> </th>
    </tr>
    <s:iterator value="employees" status="status">
        <tr class="<s:if test="#status.even">even</s:if><s:else>odd</s:else>">
            <td class="nowrap"><s:property value="firstName"/></td>
            <td class="nowrap"><s:property value="lastName"/></td>
            <td class="nowrap"><s:property value="age"/></td>
            <td class="nowrap"><s:property value="department.name"/></td>
            <td class="nowrap"><s:url id="update" action="setUpForInsertOrUpdate">
		       		   <s:param name="employee.employeeId" value="employeeId"/>
		       		</s:url> <s:a href="%{update}">Edit</s:a>
                   
                                <s:url id="delete" action="delete">
                                   <s:param name="employee.employeeId" value="employeeId"/>
		       		</s:url> <s:a href="%{delete}">Delete</s:a>
            </td>
        </tr>  		
	 </s:iterator>
    </table>
</body>
</html>
Struts2 provides many useful jsp tags.All of them are starting with 's:' symbol as shown above, for example <s:text/> and <s:a href/> . To use them we annouce them at the beginning of a jsp page with <%@ taglib prefix="s" uri="/struts-tags"%>. I won't cover too much tag usage on this lesson. For a detailed reference ,visit http://struts.apache.org/2.x/docs/struts-tags.html .

There are two place I mark red color at the following employeeForm.jsp. One is a link to the other action and another is a <s:hidden/>tag. You'll see that I use a html link rather than struts2 tag <s:a/>. The reason I do this is that if there are <s:hidden/> and <s:a/> on the same page, Struts2 will do a silly thing. It will automatically add the param in you <s:hidden/>tag (In our case it is employee.employeeId) onto <s:a/> link. Ofcourse We won't want that happen. To avoid this, the most easy way is to use a plain html <a href/>tag as shown below.

employeeForm.jsp

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
    <link href="<s:url value="/resources/main.css"/>" rel="stylesheet" type="text/css"/>
</head>
<body>
<center>
<div class="titleDiv"><s:text name="application.title"/></div>
<h1><s:if test="employee==null || employee.employeeId == null">
	 <s:text name="label.employee.add"/>
   </s:if>
   <s:else>
	 <s:text name="label.employee.edit"/>
   </s:else></h1>
   
	<table width=600 align=center>
	    <tr><td><a href="getAllEmployees.action">Click Here to View Employees</a></td>
	     </tr>
	</table>	 
	 <table>
		<tr><td align="left" style="font:bold;color:red">
	          <s:fielderror/>
                  <s:actionerror/>
                  <s:actionmessage/></td></tr>
        </table>
     		 	
    <s:form>
     <table align="center" class="borderAll">
     	 
         <tr><td class="tdLabel"><s:text name="label.firstName"/></td>
         	        <td><s:textfield name="employee.firstName" size="30"/></td>
         </tr>
        <tr>
            <td class="tdLabel"><s:text name="label.lastName"/></td>
                            <td><s:textfield name="employee.lastName" size="30"/></td>
        </tr>
        <tr><td class="tdLabel"><s:text name="label.age"/></td>
        	                <td><s:textfield name="employee.age" size="20"/></td>
        </tr>
         <tr>
            <td class="tdLabel"><s:text name="label.department"/></td>
            <td><s:select name="employee.department.departmentId" 
                list="#session.departments" 
                listKey="departmentId" 
                listValue="name"
                />
            </td>
             <s:hidden name="employee.employeeId"/>
        </tr>
    </table>

    <table> 
    	<tr>
    	  <td><s:submit action="insertOrUpdate" key="button.label.submit" cssClass="butStnd"/></td>
    	  <td><s:reset key="button.label.cancel" cssClass="butStnd"/></td>
    	<tr>
    </table> 		  		 
   </s:form>
</center>		
</body>
</html>
Till now, we've handled the web tier of the application with struts2. And only the web tier should strus2 takes responsibility of. For the detailed description of service layer and dao layer, view Rick Reumann's Struts CRUD lesson on this site.
Code and Lesson - Andy Ma