Page tree
Skip to end of metadata
Go to start of metadata

About this Tutorial

This tutorial will show you how to create master/detail screens with Spring MVC. The list (master) screen will have the ability to sort columns, as well as page 25 records at a time. The form (detail) screen will use an responsive CSS form layout (courtesy of Bootstrap). You will also configure client and server-side validation to improve your users' experience.

IntelliJ IDEA Rocks

Icon

We highly recommend using IntelliJ IDEA when developing web applications in Java. Not only is its Java and JUnit support fantastic, but it has excellent CSS and JavaScript support. Using JRebel with IDEA is likely to provide you with the most pleasant Java development experiences you've ever had.

Icon

This tutorial assumes you've created a project with the appfuse-basic-spring archetype and have already completed the Persistence and Services tutorials. If you're using the appfuse-modular-spring archetype, please morph your mind into using the web module as the root directory. If you created your project with a different web framework than Spring MVC, you're likely to be confused and nothing will work in this tutorial. (wink)

Table of Contents

  1. Create a PersonControllerTest
  2. Create a PersonController that will fetch people
  3. Create persons.jsp that shows search results
  4. Create a PersonFormControllerTest and PersonFormController
  5. Create personform.jsp to edit a person
  6. Configure Validation
  7. Create a Canoo WebTest to test browser-like actions
  8. Add link to menu

Source Code

Icon

The code for this tutorial is located in the "tutorial-spring" module of the appfuse-demos project on GitHub. Use the following command to check it out from Subversion:

svn co https://github.com/appfuse/appfuse-demos/trunk/tutorial-spring

Create a PersonControllerTest 

Testing is an important part of any application, and testing a Spring MVC web application is pretty easy. Not only are Spring's controllers lightweight, they're also easy to test using Spring's Mock library. This library has mock implements for much of the Servlet API and makes it quite simple to test Spring Controllers.

Create a PersonControllerTest.java class in src/test/java/**/webapp/controller:

This class will not compile until you create the PersonController class.

Create a PersonController that will fetch people 

Create a PersonController.java class in src/main/java/**/webapp/controller:

AppFuse leverages Spring MVC's convention over configuration features. This means the following:

Run the PersonControllerTest using your IDE or mvn test -Dtest=PersonControllerTest.

Create persons.jsp to show search results 

Create a src/main/webapp/WEB-INF/pages/persons.jsp page to display the list of people:

Open src/main/resources/ApplicationResources.properties and add i18n keys/values for the various "person" properties:

Run mvn jetty:run and open http://localhost:8080/persons in your browser. Login with admin/admin and you should see a screen similar to the figure below.

Security settings for AppFuse specify that most url-patterns should be protected (except for /signup and /passwordHint). This guarantees that clients must go through a Controller to get to a JSP (or at least the ones in WEB-INF/pages).

CSS Customization

Icon

If you want to customize the CSS for a particular page, you can add <body id="pageName"/> to the top of the file. This will be slurped up by SiteMesh and put into the final page. You can then customize your CSS on a page-by-page basis using something like the following:

body#pageName element.class { background-color: blue } 

Create a PersonFormControllerTest and PersonFormController

To start creating the detail screen, create a PersonFormControllerTest.java class in src/test/java/**/webapp/controller:

Nothing will compile at this point; you need to create the PersonFormController that you're referring to in this test.

In src/main/java/**/webapp/controller, create a PersonFormController.java class that extends AppFuse's BaseFormController. Populate it with the following code:

You might notice a number of keys in this file - "person.deleted", "person.added" and "person.updated". These are all keys that need to be in your i18n bundle (ApplicationResources.properties). You should've added these at the beginning of this tutorial. If you want to customize these messages, to add the a person's name or something, simply add a {0} placeholder in the key's message and then use the getText(key, stringtoreplace, locale) method.

If you look at your PersonFormControllerTest, all the tests depend on having a record with id=1 in the database (and testRemove depends on id=2), so let's add those records to our sample data file (src/test/resources/sample-data.xml). Adding it at the bottom should work - order is not important since it (currently) does not relate to any other tables. If you already have this table, make sure the 2nd record exists so testRemove() doesn't fail.

DbUnit loads this file before you run any tests, so these records will be available to your PersonFormControllerTest class. Save all your files and run the tests in PersonFormControllerTest using the command mvn test -Dtest=PersonFormControllerTest.

BUILD SUCCESS
Total time: 9.317s

Create personform.jsp to edit a person

Create a src/main/webapp/WEB-INF/pages/personform.jsp page to display the form:

Run mvn jetty:run, open your browser to http://localhost:8080/persons, and click on the Add button.

Fill in the first name and last name fields and click the Save button. This should route you to the list screen, where a success message flashes and the new person displays in the list.

Displaying success messages

Icon

The src/main/webapp/common/messages.jsp file in AppFuse renders the success message in this screen. This file is included in decorators/default.jsp. It also handles displaying error messages:

<%-- Error Messages --%>
<c:if test="${not empty errors}">
    <div class="alert alert-danger alert-dismissable">
        <a href="#" data-dismiss="alert" class="close">&times;</a>
        <c:forEach var="error" items="${errors}">
            <c:out value="${error}"/><br />
        </c:forEach>
    </div>
    <c:remove var="errors"/>
</c:if>
<%-- Success Messages --%>
<c:if test="${not empty successMessages}">
    <div class="alert alert-success alert-dismissable">
        <a href="#" data-dismiss="alert" class="close">&times;</a>
        <c:forEach var="msg" items="${successMessages}">
            <c:out value="${msg}"/><br />
        </c:forEach>
    </div>
    <c:remove var="successMessages" scope="session"/>
</c:if>

Configure Validation

Spring MVC supports a number of different options for configuring validation. AppFuse currently uses Spring Modules' Commons Validator support. However, you can change this to use another validation framework if you like. The instructions below show you how to configure Commons Validator with Spring MVC.

Open src/main/webapp/WEB-INF/validation.xml and add rules for the person object, so both the first and last names are required:

After making these changes and saving all your files, the first and last name fields should be required. To test, go to http://localhost:8080/personform and try to add a new person with no first or last name. You should see the following validation errors:

To enable client-side validation, you need to make the following changes to personform.jsp:

  1. Add an onsubmit() handler to the form.
  2. Add bCancel=true to the onclick() handlers of the delete and cancel buttons (to cancel validation when they're clicked).
  3. Add JSP Tags after the form to render the validation JavaScript functions.

Below is the revised contents of the <form:form> tag with these changes. Replace the <form:form> in your personform.jsp with these changes.

After saving all your files and running mvn jetty:run, client-side validation should kick in when you try to save this form. To test, go to http://localhost:8080/personform and try to add a new person with no first or last name. You should get the following JavaScript alert:

Create a Canoo WebTest to test browser-like actions

The next (optional) step in this tutorial is to create a Canoo WebTest to test your UI. This step is optional, because you can run the same tests manually through your browser. Regardless, it's a good idea to automate as much of your testing as possible.

You can use the following URLs to test the different actions for adding, editing and saving a person.

WebTest Recorder

Icon

There is a WebTest Recorder Firefox plugin that allows you to record your tests, rather than manually writing them.

Canoo tests are pretty slick in that they're simply configured in an XML file. To add tests for add, edit, save and delete, open src/test/resources/web-tests.xml and add the following XML. You'll notice that this fragment has a target named PersonTests that runs all the related tests.

To include the PersonTests when all Canoo tests are run, add it as a dependency to the "run-all-tests" target in src/test/resources/web-test.xml.

After adding this, you should be able to run mvn verify -Pitest and have these tests execute. If this command results in "BUILD SUCCESS" - nice work!

Add link to menu 

The last step is to make the list, add, edit and delete functions visible to the user. The simplest way is to add a new link to the list of links in src/main/webapp/WEB-INF/pages/home.jsp.

Where menu.viewPeople is an entry in src/main/resources/ApplicationResources.properties.

menu.viewPeople=View People

The other (more likely) alternative is that you'll want to add it to the menu. To do this, add the following to src/main/webapp/WEB-INF/menu-config.xml:

Make sure the above XML is inside the <Menus> tag, but not within another <Menu>. Then open src/main/webapp/common/menu.jsp and add the following code to it:

Now if you run mvn jetty:run and go to http://localhost:8080/home, you should see something like the screenshot below.

Notice that there is a new link in your main screen (from mainMenu.jsp) and on the top in your menu bar (from menu.jsp).

That's it!
You've completed the full lifecycle of developing a set of master-detail pages with AppFuse and Spring MVC - Congratulations!

Because it's so much fun to watch tests fly by and success happen, run all your tests again using mvn install -Pitest.

Happy Day!

BUILD SUCCESS
Total time: 49.279s

2 Comments

  1. Currently (AppFuse 2.0.1) experiencing a problem running multiple tests with hsqldb, or launching the app with Jetty, because the database lock doesn't get released. The error will be a localized (for your project) variation of this:

    For now, always specify a test instead of trying to run all of them. And, skip tests when running the application by using:

  2. This should be fixed in 2.2.1.