Access Keys:
Skip to content (Access Key - 0)

Using Spring MVC

Labels

springmvc springmvc Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

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 elegant CSS form layout (courtesy of Wufoo). You will also configure client and server-side validation to improve your users' experience.

IntelliJ IDEA Rocks
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.
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.

Table of Contents

  1. Introduction to Spring MVC
  2. Create a PersonControllerTest
  3. Create a PersonController that will fetch people
  4. Create persons.jsp that shows search results
  5. Create a PersonFormControllerTest and PersonFormController
  6. Create personform.jsp to edit a person
  7. Configure Validation
  8. Create a Canoo WebTest to test browser-like actions
  9. Add link to menu
Source Code
The code for this tutorial is located in the "tutorial-spring" of the appfuse-demos project on Google Code. Use the following command to check it out from Subversion:
svn checkout http://appfuse-demos.googlecode.com/svn/trunk/tutorial-spring

Introduction to Spring MVC

Spring 2.x had two main types of controllers: a Controller interface and a SimpleFormController class. The Controller is best suited for displaying read-only data (such as list screens), while the SimpleFormController handles forms (such as edit, save, delete). The Controller interface is quite simple, containing a single handleRequest(request, response) method.

As of version 2.5, Spring MVC began supporting annotations and made configuration much simpler.

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:

package org.appfuse.tutorial.webapp.controller;

import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;

import org.junit.Test;
import static org.junit.Assert.*;

public class PersonControllerTest extends BaseControllerTestCase {
    @Autowired
    private PersonController controller;

    @Test
    public void testHandleRequest() throws Exception {
        ModelAndView mav = controller.handleRequest();
        ModelMap m = mav.getModelMap();
        assertNotNull(m.get("personList"));
        assertTrue(((List) m.get("personList")).size() > 0);
    }
}

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:

package org.appfuse.tutorial.webapp.controller;

import org.appfuse.service.GenericManager;
import org.appfuse.tutorial.model.Person;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/persons*")
public class PersonController {
    private GenericManager<Person, Long> personManager;

    @Autowired
    public void setPersonManager(@Qualifier("personManager") GenericManager<Person, Long> personManager) {
        this.personManager = personManager;
    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView handleRequest()
    throws Exception {
        return new ModelAndView().addObject(personManager.getAll());
    }
}

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:

<%@ include file="/common/taglibs.jsp"%>

<head>
    <title><fmt:message key="personList.title"/></title>
    <meta name="heading" content="<fmt:message key='personList.heading'/>"/>
    <meta name="menu" content="PersonMenu"/>
</head>

<input type="button" style="margin-right: 5px" onclick="location.href='<c:url value="/personform"/>'" value="<fmt:message key="button.add"/>"/>
<input type="button" onclick="location.href='<c:url value="/mainMenu"/>'" value="<fmt:message key="button.done"/>"/>

<display:table name="personList" class="table" requestURI="" id="personList" export="true" pagesize="25">
    <display:column property="id" sortable="true" href="personform" media="html"
        paramId="id" paramProperty="id" titleKey="person.id"/>
    <display:column property="id" media="csv excel xml pdf" titleKey="person.id"/>
    <display:column property="firstName" sortable="true" titleKey="person.firstName"/>
    <display:column property="lastName" sortable="true" titleKey="person.lastName"/>

    <display:setProperty name="paging.banner.item_name"><fmt:message key="personList.person"/></display:setProperty>
    <display:setProperty name="paging.banner.items_name"><fmt:message key="personList.persons"/></display:setProperty>

    <display:setProperty name="export.excel.filename"><fmt:message key="personList.title"/>.xls</display:setProperty>
    <display:setProperty name="export.csv.filename"><fmt:message key="personList.title"/>.csv</display:setProperty>
    <display:setProperty name="export.pdf.filename"><fmt:message key="personList.title"/>.pdf</display:setProperty>
</display:table>

<input type="button" style="margin-right: 5px" onclick="location.href='<c:url value="/personform"/>'" value="<fmt:message key="button.add"/>"/>
<input type="button" onclick="location.href='<c:url value="/mainMenu"/>'" value="<fmt:message key="button.done"/>"/>

<script type="text/javascript">
    highlightTableRows("personList");
</script> 

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

# -- person form --
person.id=Id
person.firstName=First Name
person.lastName=Last Name

person.added=Person has been added successfully.
person.updated=Person has been updated successfully.
person.deleted=Person has been deleted successfully.

# -- person list page --
personList.title=Person List
personList.heading=Persons

# -- person detail page --
personDetail.title=Person Detail
personDetail.heading=Person Information

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
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:

package org.appfuse.tutorial.webapp.controller;

import org.appfuse.tutorial.model.Person;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;

import static org.junit.Assert.*;

public class PersonFormControllerTest extends BaseControllerTestCase {
    @Autowired
    private PersonFormController form;
    private Person person;
    private MockHttpServletRequest request;

    @Test
    public void testEdit() throws Exception {
        log.debug("testing edit...");
        request = newGet("/personform");
        request.addParameter("id", "1");

        person = form.showForm(request);
        assertNotNull(person);
    }

    @Test
    public void testSave() throws Exception {
        request = newGet("/personform");
        request.addParameter("id", "1");

        person = form.showForm(request);
        assertNotNull(person);

        request = newPost("/personform");

        person = form.showForm(request);
        // update required fields

        BindingResult errors = new DataBinder(person).getBindingResult();
        form.onSubmit(person, errors, request, new MockHttpServletResponse());
        assertFalse(errors.hasErrors());
        assertNotNull(request.getSession().getAttribute("successMessages"));
    }

    @Test
    public void testRemove() throws Exception {
        request = newPost("/personform");
        request.addParameter("delete", "");
        person = new Person();
        person.setId(2L);

        BindingResult errors = new DataBinder(person).getBindingResult();
        form.onSubmit(person, errors, request, new MockHttpServletResponse());

        assertNotNull(request.getSession().getAttribute("successMessages"));
    }
}

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:

package org.appfuse.tutorial.webapp.controller;

import org.apache.commons.lang.StringUtils;
import org.appfuse.service.GenericManager;
import org.appfuse.tutorial.model.Person;
import org.appfuse.tutorial.webapp.controller.BaseFormController;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

@Controller
@RequestMapping("/personform*")
public class PersonFormController extends BaseFormController {
    private GenericManager<Person, Long> personManager = null;

    @Autowired
    public void setPersonManager(@Qualifier("personManager") GenericManager<Person, Long> personManager) {
        this.personManager = personManager;
    }

    public PersonFormController() {
        setCancelView("redirect:persons");
        setSuccessView("redirect:persons");
    }

    @ModelAttribute
    @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
    protected Person showForm(HttpServletRequest request)
    throws Exception {
        String id = request.getParameter("id");

        if (!StringUtils.isBlank(id)) {
            return personManager.get(new Long(id));
        }

        return new Person();
    }

    @RequestMapping(method = RequestMethod.POST)
    public String onSubmit(Person person, BindingResult errors, HttpServletRequest request,
                           HttpServletResponse response)
    throws Exception {
        if (request.getParameter("cancel") != null) {
            return getCancelView();
        }

        if (validator != null) { // validator is null during testing
            validator.validate(person, errors);

            if (errors.hasErrors() && request.getParameter("delete") == null) { // don't validate when deleting
                return "personform";
            }
        }

        log.debug("entering 'onSubmit' method...");

        boolean isNew = (person.getId() == null);
        String success = getSuccessView();
        Locale locale = request.getLocale();

        if (request.getParameter("delete") != null) {
            personManager.remove(person.getId());
            saveMessage(request, getText("person.deleted", locale));
        } else {
            personManager.save(person);
            String key = (isNew) ? "person.added" : "person.updated";
            saveMessage(request, getText(key, locale));

            if (!isNew) {
                success = "redirect:personform?id=" + person.getId();
            }
        }

        return success;
    }
}

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.

<table name='person'>
  <column>id</column>
  <column>first_name</column>
  <column>last_name</column>
  <row>
    <value>1</value>
    <value>Matt</value>
    <value>Raible</value>
  </row>
  <row>
    <value>2</value>
    <value>Bob</value>
    <value>Johnson</value>
  </row>
</table>

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 SUCCESSFUL
Total time: 13 seconds

Create personform.jsp to edit a person

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

<%@ include file="/common/taglibs.jsp"%>

<head>
    <title><fmt:message key="personDetail.title"/></title>
    <meta name="heading" content="<fmt:message key='personDetail.heading'/>"/>
</head>

<form:form commandName="person" method="post" action="personform" id="personForm">
<form:errors path="*" cssClass="error" element="div"/>
<form:hidden path="id"/>
<ul>
    <li>
        <appfuse:label styleClass="desc" key="person.firstName"/>
        <form:errors path="firstName" cssClass="fieldError"/>
        <form:input path="firstName" id="firstName" cssClass="text medium" cssErrorClass="text medium error" maxlength="50"/>
    </li>
    <li>
        <appfuse:label styleClass="desc" key="person.lastName"/>
        <form:errors path="lastName" cssClass="fieldError"/>
        <form:input path="lastName" id="lastName" cssClass="text medium" cssErrorClass="text medium error" maxlength="50"/>
    </li>

    <li class="buttonBar bottom">
        <input type="submit" class="button" name="save" value="<fmt:message key="button.save"/>"/>
        <c:if test="${not empty person.id}">
        <input type="submit" class="button" name="delete" onclick="bCancel=true;return confirmDelete('person')"
            value="<fmt:message key="button.delete"/>" />
        </c:if>
        <input type="submit" class="button" name="cancel" value="<fmt:message key="button.cancel"/>" onclick="bCancel=true"/>
    </li>
</ul>
</form:form>

<script type="text/javascript">
    Form.focusFirstElement($('personForm'));
</script>

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
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="error" id="errorMessages">
        <c:forEach var="error" items="${errors}">
            <img src="<c:url value="/images/iconWarning.gif"/>"
                alt="<fmt:message key="icon.warning"/>" class="icon" />
            <c:out value="${error}"/><br />
        </c:forEach>
    </div>
    <c:remove var="errors"/>
</c:if>

<%-- Success Messages --%>
<c:if test="${not empty successMessages}">
    <div class="message" id="successMessages">
        <c:forEach var="msg" items="${successMessages}">
            <img src="<c:url value="/images/iconInformation.gif"/>"
                alt="<fmt:message key="icon.information"/>" class="icon" />
            <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 2.x 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:

<form name="person">
    <field property="firstName" depends="required">
        <arg key="person.firstName"/>
    </field>
    <field property="lastName" depends="required">
        <arg key="person.lastName"/>
    </field>
</form>

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.

<form:form commandName="person" method="post" action="personform" id="personForm" onsubmit="return validatePerson(this)">
<form:errors path="*" cssClass="error" element="div"/>
<form:hidden path="id"/>
<ul>
    <li>
        <appfuse:label styleClass="desc" key="person.firstName"/>
        <form:errors path="firstName" cssClass="fieldError"/>
        <form:input path="firstName" id="firstName" cssClass="text medium" cssErrorClass="text medium error" maxlength="50"/>
    </li>
    <li>
        <appfuse:label styleClass="desc" key="person.lastName"/>
        <form:errors path="lastName" cssClass="fieldError"/>
        <form:input path="lastName" id="lastName" cssClass="text medium" cssErrorClass="text medium error" maxlength="50"/>
    </li>

    <li class="buttonBar bottom">
        <input type="submit" class="button" name="save" value="<fmt:message key="button.save"/>"/>
        <c:if test="${not empty person.id}">
        <input type="submit" class="button" name="delete" onclick="bCancel=true;return confirmDelete('person')"
            value="<fmt:message key="button.delete"/>" />
        </c:if>
        <input type="submit" class="button" name="cancel" value="<fmt:message key="button.cancel"/>" onclick="bCancel=true"/>
    </li>
</ul>
</form:form>

<v:javascript formName="person" cdata="false" dynamicJavascript="true" staticJavascript="false"/>
<script type="text/javascript" src="<c:url value='/scripts/validator.jsp'/>"></script>

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

<!-- runs person-related tests -->
<target name="PersonTests"
    depends="SearchPersons,EditPerson,SavePerson,AddPerson,DeletePerson"
    description="Call and executes all person test cases (targets)">
    <echo>Successfully ran all Person UI tests!</echo>
</target>

<!-- Verify the persons list screen displays without errors -->
<target name="SearchPersons" description="Tests search for and displaying all persons">
    <webtest name="searchPersons">
        &config;
        <steps>
            &login;
            <invoke description="click View Person link" url="/persons"/>
            <verifytitle description="we should see the personList title"
                text=".*${personList.title}.*" regex="true"/>
        </steps>
    </webtest>
</target>

<!-- Verify the edit person screen displays without errors -->
<target name="EditPerson" description="Tests editing an existing Person's information">
    <webtest name="editPerson">
        &config;
        <steps>
            &login;
            <invoke description="View Person List" url="/persons"/>
            <clicklink label="1" description="Click edit link"/>
            <verifytitle description="we should see the personDetail title"
                text=".*${personDetail.title}.*" regex="true"/>
        </steps>
    </webtest>
</target>

<!-- Edit a person and then save -->
<target name="SavePerson" description="Tests editing and saving a person">
    <webtest name="savePerson">
        &config;
        <steps>
            &login;
            <invoke description="click Edit Person link" url="/personform?id=1"/>
            <verifytitle description="we should see the personDetail title"
                text=".*${personDetail.title}.*" regex="true"/>

            <clickbutton label="${button.save}" description="Click Save"/>
            <verifytitle description="Page re-appears if save successful"
                text=".*${personDetail.title}.*" regex="true"/>
            <verifytext description="verify success message" text="${person.updated}"/>
        </steps>
    </webtest>
</target>

<!-- Add a new Person -->
<target name="AddPerson" description="Adds a new Person">
    <webtest name="addPerson">
        &config;
        <steps>
            &login;
            <invoke description="click Add Button" url="/personform"/>
            <verifytitle description="we should see the personDetail title"
                text=".*${personDetail.title}.*" regex="true"/>

            <setinputfield description="set firstName" name="firstName" value="Jack"/>
            <setinputfield description="set lastName" name="lastName" value="Raible"/>
                
            <clickbutton label="${button.save}" description="Click button 'Save'"/>
            <verifytitle description="Person List appears if save successful"
                text=".*${personList.title}.*" regex="true"/>
            <verifytext description="verify success message" text="${person.added}"/>
        </steps>
    </webtest>
</target>

<!-- Delete existing person -->
<target name="DeletePerson" description="Deletes existing Person">
    <webtest name="deletePerson">
        &config;
        <steps>
            &login;
            <invoke description="click Edit Person link" url="/personform?id=2"/>
            <prepareDialogResponse description="Confirm delete" dialogType="confirm" response="true"/>
            <clickbutton label="${button.delete}" description="Click button 'Delete'"/>
            <verifyNoDialogResponses/>
            <verifytitle description="display Person List" text=".*${personList.title}.*" regex="true"/>
            <verifytext description="verify success message" text="${person.deleted}"/>
        </steps>
    </webtest>
</target>

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.

<target name="run-all-tests" 
    depends="Login,Logout,PasswordHint,Signup,UserTests,StaticPages,WebServices,DWR,FileUpload,PersonTests"
    description="Call and executes all test cases (targets)"/>

After adding this, you should be able to run mvn verify and have these tests execute. If this command results in "BUILD SUCCESSFUL" - 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/mainMenu.jsp.

<li>
    <a href="<c:url value="/persons"/>"><fmt:message key="menu.viewPeople"/></a>
</li>

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:

<Menu name="PersonMenu" title="personList.title" page="/persons"/>

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:

<%@ include file="/common/taglibs.jsp"%>

<menu:useMenuDisplayer name="Velocity" config="cssHorizontalMenu.vm" permissions="rolesAdapter">
<ul id="primary-nav" class="menuList">
    <li class="pad">&nbsp;</li>
    <c:if test="${empty pageContext.request.remoteUser}">
        <li><a href="<c:url value="/login"/>" class="current"><fmt:message key="login.title"/></a></li>
    </c:if>
    <menu:displayMenu name="MainMenu"/>
    <menu:displayMenu name="UserMenu"/>
    <menu:displayMenu name="PersonMenu"/>
    <menu:displayMenu name="AdminMenu"/>
    <menu:displayMenu name="Logout"/>
</ul>
</menu:useMenuDisplayer>

Now if you run mvn jetty:run and go to http://localhost:8080/mainMenu, 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.

Happy Day!
BUILD SUCCESSFUL
Total time: 48 seconds

  1. May 04, 2008

    Andreas Kollegger says:

    Currently (AppFuse 2.0.1) experiencing a problem running multiple tests with hsq...

    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:

    [gather-service] WARN [main] SettingsFactory.buildSettings(117) | Could not obtain connection metadata
    org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory 
    (The database is already in use by another process:
    org.hsqldb.persist.NIOLockFile@5913a797[file =/Users/akollegger/Developer/GATHER/workspace/gather-service/gather_service.lck, 
    exists=true, locked=false, valid=false, fl =null]: java.lang.Exception: checkHeartbeat(): 
    lock file [/Users/akollegger/Developer/GATHER/workspace/gather-service/gather_service.lck] 
    is presumably locked by another process.)
    	at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1225)
            ...
    

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

    mvn jetty:run-war -Dmaven.test.skip=true
Adaptavist Theme Builder (4.0.0-M8) Powered by Atlassian Confluence 3.1, the Enterprise Wiki.