Child pages
  • CRUD Demo I

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

...

Due to the export of the wiki to static pages, attachements are not visible. You can find the .war with the source here

...

The .zip file you can download on this site can be dropped in your servlet container (rename it to .war then) and contains the source code under the WEB-INF directory. The layout is also kept as simple as possible:

...

Configuration files

WEB-INF/web.xml

The webapplication's descriptor file contains one filter and its mapping. By default, the filter is mapped to /*, meaning all requests will be intercepted, but only those ending with a specific suffix (.action, by default) and certain special paths (for static files) will be processed and handled by Struts 2.

...

See web.xml for further details.

...

struts.xml contains the configuration for XWork: actions, validation, interceptors and results are defined in there.
To understand these terms, we'll need to take a look at Struts 2's (and XWork's) architecture. A basic request goes a bit like this:
A request is submitted, and our ActionMapper will try to determine the correct Action to execute. To find it, it will look up the registered names in your struts.xml. If one is found, before executing the Action, it will loop through a defined stack of interceptors.

...

Interceptors are a very important part of S2 - they will be invoked before and after your action is executed and the view is rendered, and as such, they are perfect for validation, authentication, open-session-in-view patterns, catching exceptions, setting or altering parameters, hiding complex operations, and more. S2 provides a number of prebuilt stacks with a ranging number of features, but nothing keeps you from defining your own interceptor stack with custom interceptors.

...

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

...

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

...

This file only contains two lines, 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

...

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

...

Since 90% of the code is identical to the other CRUD examples displayed on this site, we'll only analyze the Action class, EmployeeAction.
As always, first things first - the class definition:

...

Now, first there is the extending of the ActionSupport class - although you don't have to extend it, it provides a lot of useful extras, so you are encouraged to extend and override parts of it, but you don't have to. The interface we are implementing is a bit more interesting. The Preparable interface only defines one method, public void prepare(). By implementing this interface in your Action, you tell the prepare interceptor to call this method on your Action - so that makes it perfect to, for example, retrieve objects from a database, which is what we're 'faking' here by requesting an Employee object from the EmployeeService.

...

Let's explore the doList method, which is the default method we specified in our xworkstruts.xml for our index action.

...

Surprisingly simple, no? Simply fill in the employees object, and return "success" (defined as a final static variable SUCCESS). That's it? That's it. The only thing you need now to show the list in your view layer is a simple getter for employees in your Action. The reason for this is another Struts 2 feature: the ValueStack.

...

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, iterate 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; see Themes and Templates.

...

The s:url tag allows you to build urls for actions, possibly with parameters. 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 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.

...

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

...

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

...

Take a look at the following lines from our employees.jsp:

...

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.

...

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.

...

Easy enough: return "input" from your Action, and register the result in your struts.xml with a dispatcher to employeeForm.jsp.

...

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

...

Register the DepartmentAction in your struts.xml as 'department', and you can simply call it in your page, and use the id approach we used in the s:url tag to store the result on the stack under a custom name called allDepartments:

...

Now, this, by itself, does not do much. We'll show you later when we talk about the employeeForm page how to use it as an alternative approach.

...

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

...

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

The actual 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, 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:

...

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.

...

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:

...

Ugh. No, that's ugly, and it wouldn't work either. The solution is much cleaner and simpler: use another expression! If you were to check the extra methods provided by making our EmployeeAction extends ActionSupport, you would see a method called getText(String key). This method will look up a value in the resource bundle by its key, which is exactly what we need. So, the label would become something like this: %{getText('our_key')}. Makes sense? Thought so.

...

...

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

...

Another quick intermezzo: you can use expressions to make your own list in an expression - which is perfect for small yes/no and male/female/eunuch selections - like this:

...

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 s:action to execute 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).

...

Finally, the last two tags, the submission tags. Submission tags, yes. One for submitting the form, and another one to cancel it. The first submit button, submits the form to the form's action attribute, in case crud!save.

...

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.

...

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).

...

A very important reminder: validation is once again done by an interceptor, so it should be in your stack. Even more important, validators 'query your Action/Model' and NOT your request! Keep this in mind at all times.

...

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:

  • check the availabilty of the string (!= null)
  • check the length of the string to be > 0

Why the stringlength greater than 0? 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.

...

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

...

And guess what: min and max are indeed the two parameters we just set before ! Using OGNL expressions you can retrieve whatever you want from your validator/action/context, giving you really nice error messages (we like nice error messages). In fact, you can not only use these expressions in your error messages, but you can even set the min and max parameters dynamically. Different types of employees could have different age requirements - OGNL and polymorphy to the rescue !

...