Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Cleanup and minor editing: still work-in-progress.

...

Code Block
xml
xml
<web-app>
	    <display-name>Struts 2 CRUD Demo</display-name>
	    <filter>
		        <filter-name>struts</filter-name>
		        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
	    </filter>

	    <filter-mapping>
		        <filter-name>struts</filter-name>
		        <url-pattern>/*</url-pattern>
	    </filter-mapping>
</web-app>

More information on See web.xml for further details.

WEB-INF/classes/struts.xml

...

Let us take a more detailed look at our struts.xml:

Code Block
xml
xml
<struts>
	    <!-- IncludeConfiguration Strutsfor 2the default (from Struts 2 JAR)package. -->
	<include file="struts-default.xml"/>

	<!-- Configuration for the default package. -->
	<package name="default" extends="struts-default">

		        <!-- Default interceptor stack. -->
		        <default-interceptor-ref name="paramsPrepareParamsStack"/>
		
        
        <action name="index" class="com.aurifa.struts2.tutorial.action.EmployeeAction" method="list">
			            <result name="success">/WEB-INF/jsp/employees.jsp</result>
			            <!-- we don't need the full stack here -->
			            <interceptor-ref name="basicStack"/>
		        </action>
		
        <action name="crud" class="com.aurifa.struts2.tutorial.action.EmployeeAction" method="input">
			            <result name="success" type="redirect-action">index</result>
			            <result name="input">/WEB-INF/jsp/employeeForm.jsp</result>
			            <result name="error">/WEB-INF/jsp/error.jsp</result>
		        </action>
	    </package>
</struts>

There are four major elements covered in this example: package, interceptor, action, and result.

Packages are a way to group actions, results, result types, interceptors, and interceptor-stacks into a logical configuration unit. Conceptually, packages are similiar to objects in that they can be extended and have individual parts that can be overridden by "sub" packages. See Package Configuration for further details.

Interceptors allow you to define code to be executed before and/or after the execution of an Action method. For now, we will configure our example to use the {{paramsPrepareParamsStack }} interceptor stack; you can ignore this for now. See Interceptor Configuration for further details.

The action mappings are the basic "unit-of-work" in the framework. Essentially, the action maps an identifier to a handler class. When a request matches the action's name, the framework uses the mapping to determine how to process the request.

We define two actions in our example using First thing you encounter is the inclusion of the struts-default.xml. This file, located in the root of your struts2 core jar, contains the default interceptors, interceptor stacks and results so you don't have to register them.
Next up are the packages - packages can inherit from other packages - and are used to group actions together on different namespaces. Not important right now, so we are going to use the default one. We also specified we want to use the paramsPrepareParams interceptor stack, for reasons explained later.
Two actions are defined, and they both use the same Action class, EmployeeAction. One is registered with the name 'index', and will be used for the index page, while the other one, 'crud' will be used to execute the various create/read/update/delete actions. You also note they list different method attributes: one will execute, by default, the list method while our crud action goes with the input method.
After that, we arrive at the results. Remember how we told each Action execution has to return a String result ? Well, here they are. See Action Configuration for further details.

Results define what happens after an action executes. Each Action execution returns a String result which is used to select from the list of configured result elements.

For our index action, we don't require any input, we will assume nothing goes wrong, so we only list the success result (Note: you can register global results as well). This result uses, under the hood, the default DispatcherResult result-type to dispatch the request to an employees.jsp file. Also note the fact that since we didn't need the full stack (no validation, fileupload, preparing, or other funky things), we prefer to use the basic interceptor stack for this request.

It gets more interesting for our crud Action: besides the success result, we also specify the input result (which dispatches to our input form) and the error result (which is returned when an exception is thrown during the Action execution - for example a database exception). You can also see we specified a different result-type for our success result, in casu case the 'redirect-action'. This is nothing more than a fancy redirect which will append the chosen S2 suffix, so we could have also used the redirect result-type, with index.action as its text. But this approach is slightly better since it is suffix agnostic (you can switch suffixes very easily in S2, so that is why we always advise to program suffix agnostic and let the framework handle it for you).

More information on struts.xmlSee Result Configuration for further details.

WEB-INF/classes/struts.properties

This file only contains two linelines, and is used to set Struts 2 specific settings (such as which IoC container to use, which fileuploader, what templates, etc ..). You don't really need it, but for i18n reasons, we use it to register our resource bundle 'guest.properties'. Note that you can register properties file on different levels in Struts 2.

...

More information on struts.properties

Info

It is recommended that configuration be handled with your struts.xml file: see Constant Configuration for details.

WEB-INF/classes/guest.properties

...

The ValueStack is like a normal stack. You can put objects on it, remove them from it, and query it. And even more, you can query it with expressions! It's the heart of Struts 2, and allows easy access to a wide range of objects from nearly any place in the framework: interceptors, results, tags, ... you name it. Now, when you execute your Action, it will be

Your action is placed on top of the stack when it is executed.

So, once Once it's on the valueStack, you could query for it 'employees' - a special glue language called OGNL will then transform that request to 'top.getEmployees()' - and since top is the top of your stack, and your Action is located just there, it will invoke the getEmployees() method on your Action, and return the result.

...

S2 defines a lot of tags that will dramatically speed up your development. They can alter or query the value stack, render input forms, javascript widgets, loop iteratable objectsiterate over collection, and so on. On top of that, they have different themes, which add even more functionality/layout by the switch of a parameter. Themes are out of the scope of this example, but you should definitely check them out.you should definitely check them out; see Themes and Templates.

Code Block
xml
xml
Code Block
xmlxml

<!-- /struts-tags is automatically found, you don't have to register it in your web.xml -->
<%@taglib prefix="s" uri="/struts-tags" %>

<head>
    <link href="<s:url value='/css/main.css'/>" rel="stylesheet" type="text/css"/>
    <title><s:text name="label.employees"/></title>
</head>

...

The s:url tag allows you to build urls for actions, possibly with parameters for actions (rather than . This saves you from having to type them out manually, remember (or change) what action suffix you're using, or include the web-app context. See Url for further details.

The , and the s:text tag will look up keys in resource bundles in the valueStack (depending on the locale). So adding a new language would be a breeze. In these cases we build an url '/css/main' and we display the value of a key 'label.employees' from our guest.properties resource bundle. See Text for further details.

Code Block
xml
xml
<s:url id="url" action="crud!input" />

<a href="<s:property value="#url"/>">Add New Employee</a>

There's something special about this as well. We already know what the s:url tag does, but here we specified an id, and our action attribute looks really weird.
First, about the action value, forget about that for now - we'll get to that in a minute.
Then there's that id attribute. A lot of tags (esp. those that generate/display something) share it, and it allows you to store the result of the tag on the valueStack, as #(id_value). Thus, our generated url would be available on the stack as #url, and as you can see, we use that same #url to output it later on in our anchor tag.

/a>

Here we use s:url to generate a URL and assign it to an ID so we can refer to it later.

The s:property tag provides access to objects on the OGNL value stack. Our object, url, may be accessed using the # character: non-root objects on the value stack need the # character. See OGNL for further details.More information on <a href="http://wiki.opensymphony.com/display/WW/Tags" >the Struts 2 tags</a>>

The ActionMapper

The S2 ActionMapper doesn't just map names to Actions - it can also change the method, or even action, by using special requests. The weird value we encountered above, crud!input, tells the ActionMapper to invoke the input()/doInput() method of the Action known as crud. So for example, you could have several slighly different methods, and rather than having to register each of them in the struts.xml file, you can use the ! notation to specify which method to execute.

Info

You may also use Wildcard Mappings to run specific methods rather than use the ! character.

Another thing is the fact that you can override which action/method to invoke based on a special name:action parameter, which we'll use later on in our employeeForm.jsp to make a nice cancel button.After this small intermezzo, back to the employee listingnice cancel button.

More information on the ActionMapper

...

Code Block
xml
xml
<s:iterator value="employees" status="status">
	    <tr class="<s:if test="#status.even">even</s:if><s:else>odd</s:else>">

The s:iterator tag does pretty much what you expect from it: it loops through an array/list/collection, and pushes each object it encounters on the stack on each iteration. It also has a helper attribute called status, which generates a status object that keep track of the index, tells you if you're in an even or odd row, and so on, while you're inside the s:iterator tag. See iterator for further details.

As you can see, we use that same status object in our next tag, the s:if and its compagnoncompanion, the s:else tag. The test attribute of the s:if tag allows you to query the valueStack with an OGNL expression, which is exactly what we do here: see if the status object from our s:iterator tag we put on the valueStack, returns true when the method isEven() is invoked. The corresponding s:else method is then executed if the test method return false.

Note

Unlike JSTL's c:choose/c:when/c:otherwise tags you do not nest s:if/s:else inside a "parent" tag.

Finally, there's the s:property tag, which is used for displaying objects/expressions from the ValueStack. Examine this:

...

This might be a little confusing at first, but it's very simple. This actually translates to top.getAge(), meaning we'll execute a getAge() on the top object on our ValueStack. Which happens to be .. the Action? Nope. The employees List? Closer. The current Employee object in the employee list? Bingo.

Why? Because we told you what the s:iterator tag did: it places each object on the top of the stack. Since the employees List contains Employee objects, an Employee object gets 'on top' of the stack. So thatThat's why you can simply type age (or top.age) use "age" for s:property's value attribute and have the employee's age printed out.

...

Code Block
public String doInput() {
	    return INPUT;
}

Nothing to it. So let's take a look at how we are going to edit an Employee.

Editting, or better, preparing for editing, comes in many flavours. Fact is, you often need 'extra' objects, like a list of possible departments in our case, to be set up before rendering the insert or edit page. In S2, there are quite some ways to accomplish this, but two approaches are recommended:

- Using a prepare() method and interceptor to setup any additional objects you need
- Using a an s:action tag to use another action to create the objects for you - for example, a DepartmentAction could return a list of Department objects.
Here we'll show you both, but in the example application you'll only find the first one (might change, that's why we're adding it here).

...

A little rehearsal: the prepare interceptor, when listed in your interceptor stack, will call the prepare method on our Action. We use this prepare method to set up our deparments, so the list will be available whenever our crud action is called.

We also use this to retrieve an employee bean whenever an id is set - which is precisely how we make the difference between inserting and editing (and we use a similar method in the doSave() method in our EmployeeAction). So, visiting the crud!input action without an employee.id parameter to retrieve the employee, will result in an empty form, while passing the parameter will result in an Employee object to be retrieved for editing.

More information on the See Prepare interceptor for further details.

The s:action approach

The other commonly used approach is to use the s:action tag. The s:action tag allows you to execute (additional) S2 actions, which makes them perfect to generate objects for input forms, such as select boxes. You could create for example, a Department action, which does nothing more than listing an doList() method to set up a list of departments, and getter for it, called getDepartments().

...

But first, we find another useful tag: s:set.

Code Block
xml
xml
<s:if test="employee==null || employee.employeeId == null">
	    <s:set name="title" value="%{'Add new employee'}"/>
</s:if>
<s:else>
	    <s:set name="title" value="%{'Update employee'}"/>
</s:else>

The set tag allows you to store certain objects on the stack (as well as their scope - request/session/page/...), which is what we're going to do here because out of sheer laziness (and performance reasons) we refuse to do the same if/else more than once. As you can guess, it relies on the same principle as the id attribute of the s:url tag we saw earlier, meaning I can access it with #title on the ValueStack.

More information on the s:if tag, s:else tag

...

Code Block
xml
xml
<s:form action="crud!save.action" method="post">
    <s:textfield name="employee.firstName" value="%{employee.firstName}" label="%{getText('label.firstName')}" size="40"/>
    <s:textfield name="employee.lastName" value="%{employee.lastName}" label="%{getText('label.lastName')}" size="40"/>
    <s:textfield name="employee.age" value="%{employee.age}" label="%{getText('label.age')}" size="20"/>
    <s:select name="employee.department.departmentId" value="%{employee.department.departmentId}" list="departments" listKey="departmentId" listValue="name"/>
    <s:hidden name="employee.employeeId" value="%{employee.employeeId}"/>
    <s:submit value="%{getText('button.label.submit')}"/>
    <s:submit value="%{getText('button.label.cancel')}" name="redirect-action:index"/>
</s:form>

Wow - a lot of code at once. Let's dissect it tag by tag. It may seem complicated, but as you'll see, it's actually really easy.

The s:form tag generates a standard html form (note: there's a small bug when using the action!method notation without suffix, that's why I am including .action here), while the first s:textfield will generate an <input type="text" .. /> - but wait, it does more. It transforms your first tag from this:

...

Into this:

Code Block
xml
xml
<tr>
	    <td class="tdLabel">
		        <label for="crud!save_employee_firstName" class="label">First Name:</label>
	    </td>
	    <td>
		        <input type="text" name="employee.firstName" size="40" value="" id="crud!save_employee_firstName"/>
	    </td>
</tr>

Let's analyze that s:textfield tag in greater detail. First thing we encounter is the name attribute, which is similar to the HTML input tag's name attribute. So it is by itself, not very special. However, as we've seen above, this allows S2 to call the getEmployee().setFirstName() method - and even, if necessary, create the Employee object for you. So no

No more tens of property setters in your action - just one setter for a good ol' POJO (Plain Old Java Object), and you can call its setters right away (Note: for those who are concerned about mailicious injections, you can limit what can be set on your POJO)!

The value attribute uses a special %{..} notation, to indicate the value is not just a string 'employee.firstName', but in fact an expression that should be looked up on the ValueStack. So this one This will make OGNL analyze the content, and look up on the ValueStack to see if it can find a method named getEmployee(), which returns a POJO which has a getFirstName() method on it.

Now, when such an expression returns null (since our Employee pojo is NOT initialised - we are doing an insert here, remember ?), S2 creates the Employee object for you, and its getFirstName() returns, of course, null. So, we're actually cheating here, because it will allow us to reuse that same form when we are going to edit an Employee. In that case, our getEmployee() would return an initialised object, so its getFirstName() would return a value, and thus display it in our input field ! Great - reusal of forms is always nice (of course, you can always use two seperate forms, but you don't need to).

...

Now, we saw earlier that we could use the s:text tag to retrieve values from resource bundles for i18n reasons. Now that begs the question, how do we use those same values in our tags? Let's say we want to i18n'ize our textfield label. Something like this ?:

Code Block
xml
xml
<s:textfield name="employee.firstName" value="%{employee.firstName}" label="<s:text name="label.firstName"/>" size="40"/>

...

Note

There currently is a small bug in Struts 2.0.5 that does not select the correct value by default. You can solve
this temporarily by using value="%{employee.department.departmentId.toString()}".

We already covered the name attribute, so let's look at the list attribute: departments. Hmm, this might seems strange at first. Where does it come from? Well, perhaps it makes much more sense to see this: list="%{departments}". Yes, it is in fact an expression on the ValueStack that will query our action for the departments we've setup before ! Then, why are we missing the %{..} ? The answer is twofold: you can still write %{departments}, and it would work as you expected. But you have to realize, that whatever you are going to iterate is going to be an expression! So, being pretty lazy and with all this Ruby-on-rails 'minimalistic' code, we decided we might as well save you a couple of RSI-related finger moves and let you discard the %{..}.

...

By the way, did you remember the s:action tag we used as an alternative to the prepare method to fill up the select box? We can now use the action we executed and stored on the ValueStack by referencing it by its id:

...

So, instead of getting the departments from the current action, we used the s:action to execute another one, and now we're using that action to get a different action that retrieves the department List. This allows you to split up, and reuse Actions (Note: you can go even further and program your own components with built-in actions, but that's out of scope).

Let's get back to the original plan.

Now, the listKey and listValue attributes tell WW s:select what it should use as keys and values in our select box - and what do you know, those (hidden) expressions are going to be invoked on each object it encounters in your list - in this case, getDepartmentId() and getName().

Finally, the value attribute will tell S2 where to place the typical 'selected' attribute in our generated options, and it will print it as soon as the value expression equals the key expression. Thus, in our case, as soon as your employee.getDepartment().getDepartmentId() equals the getDepartmentId() expression on one of the Department objects in our departments List. Since we don't have an employee we're editing, the expression would return null, so no selected attribute would be printed.

...

The second one is more interesting though. It also submits the form to the same crud!save action as the first one, but lists a special name attribute. This name attribute will cause the ActionMapper to intercept the call, and in this case, redirect it to another action. No more fiddling with javascript to have forms with multiple submit buttons - it's all done for you, without any javascript or the troubles that come with it.

More information about <a href="http://wiki.opensymphony.com/display/WW/Themes +and+Templates">themes and templates</a>, <a href="http://wiki.opensymphony.com/display/WW/Form+Tags">form tags</a>, <a href="http://wiki.opensymphony.com/display/WW/ActionMapper">action mapping</a>and Templates, Form Tags, and ActionMapper.

Validation

Alright, so you've set up the form for adding and updating employees. This is the point where you normally start sweating, you hands start shaking and you feel slightly dizzy. Have no fear, S2 is here!

S2 uses XWork's validation framework internally. It allows you to validate Models and Actions using a set of specialised (field)validators, grouped together in an xml file named YourModel-validation.xml or ActionName-validation (and, in the case of the alias, ActionName-alias-validation.xml).

Since we only want to validate the crud action, we create a file EmployeeAction-crud-validation.xml and place it in our classpath (mostly next to our compiled Action class).

...

Field validators will not validate input fields - they are named field validators because they will automatically mark the input field in your form with the validation error, whereas normal validators would create actionErrors.

So, first First we apply a requiredstring validator to the getEmployee().getFirstName() return value. There are quite a few validators, ranging from requiredstring, required, intrange, emailaddress, url, .. etc. Writing your own validator is not hard, and it can be reused easily. Here we use the requiredstring, which does 2 checks:

...

Why the stringlength greater than 0? Well, if you are submitting an empty text input field, the variable name will be send, and thus will S2 set our field name to an empty "" String (contrary to not sending a parameter normally results in nothing being set, or null)HTML forms will submit an empty string (not a null!) for an empty text input, so simply testing for null would fail if we required at least one character to be submitted.

The employee.lastName validation is exactly the same as the firstName. The only difference is in the validation of the age property.

We use in fact 2 validators: a 'required' one, which will make sure that our age is in fact set, and not null, and another one that will check the range to make sure our employee is between 18 and 65 years old. If course, if the first validator fails, we shouldn't continue processing, so that's why we can specify the short-circuit attribute. If a validator short-cicuits the validation, validation is failed and skips the current (field) validator.

...

It does not stop there. The text you pass, be it hardcoded or a looked-up value, can in fact contain OGNL expressions as well !

Take a look at the value we get back from the 'errors.required.age.limit'-key:

...