web     learntechnology.net
WebWork CRUD Example (Larry Meadors)
[ print format ]
Application and Lesson by Larry Meadors

wwdemo source code
wwdemo.war

(Note from Rick Reumann: Also check out a similar WebWork lesson and application written by Philip Philip Luppens found here.)

Introduction

This is the first lesson of three to get us to a full-blown webwork application using spring. Where to start? How about the beginning..get the webwork download here: http://opensymphony.com/webwork. There is a ton of stuff there, so we won't talk about all of it, just the parts that we really need here.

The web deployment descriptor - web.xml

OK, there are three interesting parts here, two for spring, and one for webwork:

  1. The spring configuration location
  2. The webwork filter and mapping
  3. The spring context loader
We provide a hint for Spring to be able to find our spring configuration using a web context parameter:
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/classes/net/reumann/demo/spring.xml</param-value>
</context-param>
The filter definition looks like this:
<filter>
  <filter-name>webwork</filter-name>
  <filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class>
</filter>

<filter-mapping>
  <filter-name>webwork</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
Finally, we declare the Spring component:
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

So, what does all this do? Well, for the most part, it's like the back of your toilet - if it works, just leave it alone.

There is one part that we do need to look at more, and that is the spring configuration file - spring.xml, that is the next section.

The Spring configuration file - spring.xml

Since this is sort of a brain-dead simple application, the configuration file is really pretty simple. Here it is in all it's glory:

<beans>
  <!-- DAO Configuration -->
  <bean id="employeeDao" class="net.reumann.demo.persistence.EmployeeNoDBdao" />
  <bean id="departmentDao" class="net.reumann.demo.persistence.DepartmentNoDBdao"/>
  <bean id="employeeAction" class="net.reumann.demo.webwork.employee.EmployeeAction">
    <constructor-arg ref="employeeDao" />
    <constructor-arg ref="departmentDao" />
  </bean>
</beans>
Well, now. What's all that XML? This simply defines instances of our DAO classes - employeeDao and departmentDao which are instances of EmployeeNoDBdao and DepartmentNoDBdao (and imlement EmployeeDao and DepartmentDao interfaces) respectively. After defining them, it defines the EmployeeAction instance (the one with the id of "employeeAction") and passes the two dao instances into the constructor for the action.

So, lets look at that constructor real quickly (don't worry, we'll come back to this class later in more detail)...

public EmployeeAction(EmployeeDao employeeDao, 
    DepartmentDao departmentDao) {
  this.employeeDao = employeeDao;
  this.departmentDao = departmentDao;
}
All we are doing is saving them to instance variables so we can use them later, but notice how we are saving them - as the interfaces, not the implementations that we defined in the spring.xml above. We are doing this for a reason - in a later lesson, we will replace those memory based DAO classes with real ones that work with a database without touching the class that uses them. Cool, huh?

As far as the other Java source in the application, we are going to ignore it for the most part. I mentioned the DAO classes, which are really just dummy placeholders. There are two beans (so there, Vic!) but you have all seen get/set methods before, so I will not waste your time or mine talking about them.

The rest of the configuration...

OK then, that leaves two more configuration files and then we will be all ready to look at out our EmployeeAction class and the two jsp files that make the user-facing part of the application work. Hang in there - we are almost through this.

The xwork.xml file is similar to the struts-config.xml file, but so much simpler that it hurts. Granted, it is not without a eye-of-newt and hair-of-bat (i.e., black magic that I don't understand yet), but the relevant parts are real simple. Here is the stanza of it that defines our application:

<action name="employee" class="net.reumann.demo.webwork.employee.EmployeeAction">
  <result name="list">/WEB-INF/jsp/employee/employeeList.jspx</result>
  <result name="input">/WEB-INF/jsp/employee/employeeForm.jspx</result>
  <result name="success" type="redirect">employee.action</result>
</action>
Well, gee, more XML. Don't get worried, it's easy to understand. We are defining 3 results: list, input, and success. The list and input results are simple forwards to jsp files (well, jspx files...we'll get to them later, too) but the last one is a redirect to the starting point of the application. We will use that for responding to requests that change data, so that a refresh doesn't repeat the change (i.e., adding a duplicate user, etc).

One more config file...then we are done with them. This one is just a properties file, so no XML, and it is mostly defaults from the webworks distribution's blank web app demo. Here are the two settings I changed:

webwork.devMode=false
webwork.objectFactory=spring
Everything else in that file is just the default settings from the blank web app.

OK, while this may look like a pile of configuration, the only files here that you will change with any regularity are the spring.xml and xwork.xml files when you add or change actions and results.

So...is there any code here?

Yep, there is, and now that we have all the plumbing in place, we can start working with the code. Yay.

For this application, there is only one Java class, and 2 jspx files which we will look at in that order. First, lets look at the EmployeeAction.java file. (Remember, if you are coming here as a struts developer, the Action and ActionForm classes are merged, and this is the class that does what they did in struts.) As always, I am not going to waste time talking about stupid stuff (i.e., get/set methods that we have all seem a billion times and are in the source), so lets look at just the relevant stuff.

We already looked at the constructor above, so this is just a reminder that we have access to the DAO classes in all methods on the action. Our application will support 5 actions: list, add, edit, save, and delete. An action handler must be public, must return a string, and must have no parameters. So, let's look at the action handlers for our application:

public String execute() throws Exception {
  return list();
}

public String list() {
  return "list";
}

public String add() {
  employee = new Employee();
  return Action.INPUT;
}
public String edit() {
  employee = employeeDao.getEmployee(id);
  return Action.INPUT;
}

public String save() {
  if (nullOrZero(employee.getEmployeeId())) {
    employeeDao.insert(employee);
  } else {
    employeeDao.update(employee);
  }
    return Action.SUCCESS;
}

public String delete() {
  employeeDao.delete(id);
  return Action.SUCCESS;
}
Now, because this is just a simple CRUD application, we are accessing the DAO right in the action class. In a more complex application, this would not be adequate, but for this, it'll do.

The execute() method is the default action if nothing is specified on the URL calling the action. It will just call (and return) the list() method. The list() method just returns the string "list", which maps to the result of the same name in the xwork.xml file, and then forwards to the employeeList.jspx page.

The add() and edit() methods are so similar that we'll look at them together. One creates a new Employee bean, the other gets one out of the DAO layer, then they both return Action.INPUT. Like the list() method, this maps to the result named "input", and forwards to the employeeForm.jspx page.

The save() method has to think a bit more. Since it is used to both create and update data, it looks for the key (the employeeId property), then calls the appropriate DAO method, and returns Action.SUCCESS which maps to the result named "success" and redirects to the default action of the application.

The delete() method calls the delete method on the DAO, and also returns Action.SUCCESS which maps to the result named "success" and redirects to the default action of the application.

I know, you are thinking 'Just a minute here...won't the list page need a list from the DAO?!' Well, you are correct, it will. Where does that come from? Those tricksy webwork guys thought of everything. Our action class implementes the Preparable interface, which adds a prepare() method so we have one more method to look at:

public void prepare() {
  departmentList = departmentDao.getAllDepartments();
  employeeList = employeeDao.getAllEmployees();
}
This populates two lists that are exposed to our pages as read-only properties. Speaking of our pages...

What a view

OK, we have seen everything interesting in this sucker except for the view. We have two jspx files that make up the user interface:

  • employeeList.jspx
  • employeeForm.jspx
A jspx file is just a JSP that is also valid XML. The employeeList.jspx page is 99% simple JSP+JSTL, so I am not going to cover much of it. There are good tutorials for those all over the net. Two things I wil ltalk about are the add and edit links. They look funny, so let's examine them:
<a href="employee!add.action">Add New Employee</a>
What in blazes is href="employee!add.action"? Well, this is telling webwork that we want the add action handler of the employee action. That's it. The edit link does the same thing, but also adds a parameter ( href="employee!edit.action?id=<{emp.employeeId}"), which is mapped to the id property of the action class, which we used in the edit() method above. Slick, huh?

The employeeForm.jspx page is more interesting, because it uses some webwork tags. We will look at that in more detail, just not the JSTL stuff:

<w:form action="employee!save.action">
  <w:hidden name="employee.employeeId"/>
  <w:textfield name="employee.firstName" label="First Name" size="40" maxlength="40"/>
  <w:textfield name="employee.lastName" label="Last Name" size="40" maxlength="40"/>
  <w:textfield name="employee.age" label="Age" size="3" maxlength="3"/>
  <w:select label="Department"
    name="employee.departmentId"
    list="departmentList"
    listKey="departmentId"
    listValue="name"
    />
  <w:submit name="action" value="save" cssClass="butStnd"/>
  <w:submit name="action" value="cancel" cssClass="butStnd"/>
</w:form>
Whoa, that's it? Yep. No formatting required. This is because of the automatic theming that webwork applies to the form, which puts it in a table with two columns - one for the labels, and one for the input fields. The name attribute of the tags tells the name of the property in the action handler that the field is bound to. So, employee.age means the age property of the employee property of the action class.

All done.

OK, you now have in your hands all the parts you need to build a simple CRUD application with webwork. In the next lesson, we will hook it up to a database using iBATIS.