Personal Finance Manager Web Application Continued (Part Seven)

Content

Web Application Continued...

Summary

On the previous page the PFM web application was created as a Maven project, the dependencies for Spring and Velocity were added and the code written for presenting a list view of all the Account Oweners. As we have no account owners in the database the next task is to create a form for adding them. This is what will be tackled next.

Add Account Owner View

(Epic 4a Web Application Views - Story ii Add Owners Create New View)

Add Account Owners & Edit Details (Upate) are very similar, therefore we will create a single Spring controller to handle both cases. Create this controller in the 'com.inivitiv.tutorials.pfm.web.controller' package as 'EditOwnerFormController.java'

package com.inivitiv.tutorials.pfm.web.controller;

import javax.servlet.ServletException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.RequestParam;

import com.inivitiv.tutorials.pfm.domain.Owner;
import com.inivitiv.tutorials.pfm.persist.PFMGenericDao;
import com.inivitiv.tutorials.pfm.web.validate.OwnerValidator;

@Controller
@RequestMapping("/editowner.htm")
public class EditOwnerFormController {

    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    @Autowired
    private PFMGenericDao ownerDao;
   @Autowired
   private OwnerValidator ownerValidator;

   @RequestMapping(method=RequestMethod.POST)
   public String onSubmit(@ModelAttribute("owner")Owner owner,
	    BindingResult result)
           throws ServletException {

       ownerValidator.validate(owner, result);
       if(result.hasErrors()){
           return "editowner";
       }
       logger.info("Saving owner: " + owner);
       ownerDao.saveOrUpdate(owner);

       logger.info("returning from EditOwnerForm view to " 
	       + "//viewowner.htm?Id=" + owner.getId());

       return "redirect:/list.htm";
   }
    
    @RequestMapping(method = RequestMethod.GET)
    public Owner intializeForm(@RequestParam(value = "Id")Long id) {
	
	Owner owner = null;
	if(id == null){
	    owner = new Owner();
	}else {
	    owner = ownerDao.findByPrimaryKey(id);
	}
        return owner;
    }

    public PFMGenericDao getOwnerDao() {
        return ownerDao;
    }

    public void setOwnerDao(PFMGenericDao ownerDao) {
        this.ownerDao = ownerDao;
    }

    public OwnerValidator getOwnerValidator() {
        return ownerValidator;
    }


    public void setOwnerValidator(OwnerValidator ownerValidator) {
        this.ownerValidator = ownerValidator;
    }
}
The method 'initializeForm(@RequestParam(value = "Id")Long id)' recieves the Owner.id as a request parameter. If this is null it creates a new Owner instance, if not it retreives the Owner instance from the database. This Onwer instance is then bound to the form view when it is displayed. Either showing blank fields for a new Account Owner, or populating the fields with the database instance ready for editing.

The submit method 'onSubmit(@ModelAttribute("owner")Owner owner, BindingResult result)' validates the form using the class 'OwnerValidator' which we need to create next. If the validation passes then the Owner instance is saved to the PFM database. Otherwise the formis diplayed with the errors giving the user the opportunuty to corrcct them.

Owner Validator

Cteate the 'Validator.java' class in the 'com.inivitiv.tutorials.pfm.web.validate' package.
package com.inivitiv.tutorials.pfm.web.validate;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.inivitiv.tutorials.pfm.domain.Owner;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class OwnerValidator implements Validator {

    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());
    
    @Override
    public boolean supports(Class clazz) {
	
	return Owner.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors errors) {
	Owner owner = (Owner) obj;
	
	ValidationUtils.rejectIfEmpty(errors, "firstName", 
		"error.owner.firstname.not-specified");
	ValidationUtils.rejectIfEmpty(errors, "middleName", 
		"error.owner.middlename.not-specified");
	ValidationUtils.rejectIfEmpty(errors, "surname", 
		"error.owner.surname.not-specified");
    }
}

Add/Edit Owner Controller Unit Test

Within each test there is some commont set up and tear down code, so we will first create a class that encapsulates this.

To test the AddEditOwnersController we will create a JUnit test, it will also test the Validator as well. Create the following unit test.

package com.inivitiv.tutorials.pfm.web.controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;

import com.inivitiv.tutorials.pfm.domain.Owner;
import com.inivitiv.tutorials.pfm.web.validate.OwnerValidator;
/**
 * Tests the EditOwnerFormController class.
 * @author petersnr
 *
 */
public class AddEditOwnersControllerTest extends PFMControllerTestsInit {
    /**
     * Logger.
     */
    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * Tests initialize method.
     * @throws Exception
     *             The exception thrown if an error occurs
     */
    @Test
    public final void testInitialize() throws Exception {

        EditOwnerFormController controller = new EditOwnerFormController();
        controller.setOwnerDao(ownerDaoImpl);

        OwnerValidator validator = new OwnerValidator();
        controller.setOwnerValidator(validator);

        // Test new Owner by initializing with a null
        Owner owner = controller.intializeForm(null);
        assertNotNull(owner);
        // Owner Id should be null for insert of new owner to work
        assertNull(owner.getId());

        // Test edit owner by create an owner, saving then call
        // initialize form with Id.
        owner = new Owner("Unit", "Test", "Owner");
        ownerDaoImpl.saveOrUpdate(owner);
        Long id = owner.getId();

        // Reset owner.
        owner = null;
        // Controller retrieves owner object using id.
        owner = controller.intializeForm(id);
        // Check correct owner is returned.
        assertEquals(id, owner.getId());
        assertEquals("Unit", owner.getFirstName());
        assertEquals("Test", owner.getMiddleName());
        assertEquals("Owner", owner.getSurname());

    }

    /**
     * Tests handleRequest method.

     * @throws Exception
     *             The exception thrown if an error occurs
     */
    @Test
    public final void testHandleRequestView() throws Exception {
        // Test validator.
        EditOwnerFormController controller = new EditOwnerFormController();
        controller.setOwnerDao(ownerDaoImpl);
        OwnerValidator validator = new OwnerValidator();
        controller.setOwnerValidator(validator);

        // Invalid Owner, result should contain errors.
        Owner owner = new Owner();
        BindingResult result =
                new BeanPropertyBindingResult(owner, "editowner");
        controller.onSubmit(owner, result);
        assertTrue(result.hasErrors());

        // Test with a valid owner, result should not contain errors.
        owner = new Owner("Unit", "Test", "Owner");
        result = new BeanPropertyBindingResult(owner, "editowner");
        String view = controller.onSubmit(owner, result);
        assertFalse((result.hasErrors()));

        // If saved correctly an owner.id will be set.
        assertNotNull(owner.getId());

        // Check correct view is returned
        assertEquals(view, "redirect:/viewowner.htm?Id=" + owner.getId());
    }
}

Owner Validator Unit Test

Create the OwnerValidatorTest.java file in the 'src/test/java' directory in package 'com.inivitiv.tutorials.pfm.web.validate' package as below...

package com.inivitiv.tutorials.pfm.web.validate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Properties;

import org.junit.Before;
import org.junit.Test;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ValidationUtils;

import com.inivitiv.tutorials.pfm.domain.Owner;

/**
 * Tests the Owner Validator.
 * @author petersnr
 *
 */
public class OwnerValidatorTest {
    /**
     * The OwnerValidator instance being tested.
     */
    private OwnerValidator validator;
    @Before
    public void setUp() throws Exception {
        validator = new OwnerValidator();
    }
    /**
     * Tests the supports method.
     */
    @Test
    public void testSupports() {

        Owner owner = new Owner("Unit", "Test", "User");

        boolean supports = validator.supports(owner.getClass());
        assertTrue(supports);
        Integer myInt = new Integer(5);
        supports = validator.supports(myInt.getClass());
        assertFalse(supports);
    }

    @Test
    public void testValidateOwnerConstructor() {

        Owner owner = new Owner("Unit", "Test", "User");
        BindException errors = new BindException(owner, "firstName");
        ValidationUtils.invokeValidator(validator, owner, errors);
        // Should not contain errors.
        assertFalse(errors.hasErrors());
        errors = new BindException(owner, "middleName");
        ValidationUtils.invokeValidator(validator, owner, errors);
        // Should not contain errors.
        assertFalse(errors.hasErrors());
        errors = new BindException(owner, "surname");
        ValidationUtils.invokeValidator(validator, owner, errors);
        // Should not contain errors.
        assertFalse(errors.hasErrors());
    }
    @Test
    public void firstNameIsNull(){
        Owner owner = new Owner("Unit", "Test", "User");
        owner.setFirstName(null);
        BindException errors = new BindException(owner, "firstName");
        ValidationUtils.invokeValidator(validator, owner, errors);
        assertTrue(errors.hasErrors());
        assertEquals(1, errors.getFieldErrorCount("firstName"));

        FieldError fe = errors.getFieldError("firstName");
        assertEquals("error.owner.firstname.not-specified", fe.getCode());
    }
    @Test
    public void middleNameIsNull(){
        Owner owner = new Owner("Unit", "Test", "User");
        owner.setMiddleName(null);
        BindException errors = new BindException(owner, "middleName");
        ValidationUtils.invokeValidator(validator, owner, errors);
        assertTrue(errors.hasErrors());
        assertEquals(1, errors.getFieldErrorCount("middleName"));

        FieldError fe = errors.getFieldError("middleName");
        assertEquals("error.owner.middlename.not-specified", fe.getCode());
    }
    @Test
    public void surnameIsNull(){
        Owner owner = new Owner("Unit", "Test", "User");
        owner.setSurname(null);
        BindException errors = new BindException(owner, "surname");
        ValidationUtils.invokeValidator(validator, owner, errors);
        assertTrue(errors.hasErrors());
        assertEquals(1, errors.getFieldErrorCount("surname"));

        FieldError fe = errors.getFieldError("surname");
        assertEquals("error.owner.surname.not-specified", fe.getCode());

    }

}
Run the JUnit tests by executing mvn test. They should all pass, if not check for any errors and fix them.

Add/Edit Velocity View

We are now in a good position to create the Add/Edit Owners Velocity template, so create the file 'editowner.vm' in the folder 'src/main/webapp/WEB-INF/velocity' as below.

#parse("WEB-INF/velocity/header.vm")
#if(! $owner.id)
<h2>#springMessage("addowner.heading")</h2>
#else
<h2>#springMessage("editowner.heading")</h2>
#end
$br
<form method="post">
<table>
<tr><th>First Name</th><th>Middle Name</th><th>Surname</th></tr>
    <tr>
        <td>#springFormInput("owner.firstName" "")</td>
        <td>#springFormInput("owner.middleName" "")</td>
        <td>#springFormInput("owner.surname" "")</td>
    </tr>
</table>
  #if(${status.error})
    <b>Please fix all errors!</b>
    #foreach($f in $status.errors.fieldErrors)
        #springBind("owner.${f.field}")
        #foreach($e in $status.errorMessages)
            <p>${e}</p>
        #end
    #end
  #end
  <br>
  <input type="submit" alignment="center" value="#springMessage('btn.submit')">
  <button type="cancel">#springMessage("btn.cancel")</button>
</form>
#parse("WEB-INF/velocity/footer.vm")
Provide a link to this page from the 'list.vm' Velocity view, the link exists but does not point to the page, so update to point to the editowners.htm mapping as shown below. Notice that the Id= is left empty, it will ensure our controller allocats a new unintialized Owner object.
<a href="#springUrl("/editowner.htm?Id=")">#springMessage("addowner.link.text")</a>
Just as we externalized the user interface messages in the listowners.vm template we do the same in the template above, therefore update the messages.properties file to contain the new values, your messages.properties should now look like this.
title=Personal Finance Manager
listowner.heading=Account Owners Listing
listowner.link.text=Owner Listing
addowner.link.text=Add New Account Owner
addowner.heading=Add Account Owner
editowner.heading=Edit Account Owner Details
btn.cancel=Cancel
btn.submit=Submit
error.owner.firstname.not-specified=First Name Required
error.owner.middlename.not-specified=Middle Name Required
error.owner.surname.not-specified=Surname Required
The user interface preparation is now complete for the Add/Edit Owners view, next we will create a mapping for Spring to use this view.

Add/Edit Owners Spring Mapping

As mentioned before Spring uses our pfm-webapp-servlet.xml file to look up the application mappings and bean declarations. So to map to our new view we need a new bean mapping. For convenience the complete file is listed below with the new entries.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

  <!-- the application context definition for pfm-webapp DispatcherServlet -->
  
  <!-- i18n support for resource bundles -->
  <bean id="messageSource" 
        class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="messages"/>
  </bean>
  
  <!-- Datasource definition for a HSQL file driven database -->
  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" 
    destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:file:data/pfm"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
  </bean>
  <!-- Let Spring manage the Hibernate sessions as it does a good job 
        The model beans are annotated so use the AnnotationSessionFactory Bean
        and declare the classes that are annotated
  -->
  <bean id="mySessionFactory" 
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <!-- annotated source files -->
    <property name="annotatedClasses">
      <list>
        <value>com.inivitiv.tutorials.pfm.domain.Owner</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.generate_statistics">true</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
        <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.current_session_context_class">thread</prop>
     </props>
    </property>      
  </bean>
  <!-- Velocity templating engine will decouple our Controller beans from the view layer -->
  <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    <property name="prefix">
      <value>/WEB-INF/velocity/</value>
    </property>
    <property name="suffix">
        <value>.vm</value>
    </property>
    <property name="exposeSpringMacroHelpers">
        <value>true</value>
    </property>
  </bean>
  <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <property name="resourceLoaderPath">
       <value>/</value>
    </property>
  </bean>
  <!-- PFM Web Application Bean Declarations -->
  <bean id="ownerDao" class="com.inivitiv.tutorials.pfm.persist.OwnerDaoImpl">
        <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>
  <!-- mapping for List Owners View -->  
  <bean name="/list.htm" class="com.inivitiv.tutorials.pfm.web.controller.ListOwnersController">
    <property name="ownerDao" ref="ownerDao"/>
  </bean>

  <bean id="ownerValidator" class="com.inivitiv.tutorials.pfm.web.validate.OwnerValidator"/>
  <!-- mapping for Add/Edit Owner View -->
  <bean name="/editowner.htm" class="com.inivitiv.tutorials.pfm.web.controller.EditOwnerFormController">
      <property name="ownerDao" ref="ownerDao"/>
      <property name="ownerValidator" ref="ownerValidator"/>
  </bean>

</beans>

The Add/Edit owner view is completed, the list view contains a link to this view and the Spring mapping is completed. Now you can start up the PFM web application and add owners to the application. Start up the web application using the maven command.
mvn jetty:run
Then navigate to the list view using URL http://localhost:8080/pfm-webapp/list.htm and click on the 'Add New Account Owner' link. The Edit/Add Owner view will be display as shown below.

add-account-owner-view.png

Enter the Account Owner details and click on 'Submit', the new Account Owner is added to the database and you are redirected to the 'List View' that now displays the Account Owner you entered.

Try to add another Account owner but this time do not fill in any fields and 'Submit' the form, the form is re-displayed with the errors displayed showing that the form validation is functioning correctly.

add-owner-errors-view.png

Delete Account Owners View

Epic 4a Web Application Views - v. Delete Account Owner View

Now would be a good time to add the delete account owners view, then we can start to maintain the owners. Again we will carry out these operations.

  1. Create a Spring controller to handle the delete of the owner
  2. Create a unit test for the controller
  3. A view is not required for delete, we will just add a link to the list.vm Velocity template so an Account Owner can be selected to delete.
  4. Add a mapping within the pfm-webapp-servlet.xml for the new controller

Create the DeleteOwnerController Class

This Spring controller will take the Account Owner Id as a parameter and use the OwnerDaoImpl instance to carry out the delete. Create the class DeleteOwnerController.java in the 'com.inivitiv.tutorials.pfm.web.controller' package.

package com.inivitiv.tutorials.pfm.web.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.inivitiv.tutorials.pfm.domain.Owner;
import com.inivitiv.tutorials.pfm.persist.PFMGenericDao;

/**
 * Deletes an Account Owner from the Application.
 * @author petersnr
 *
 */
@Controller
public class DeleteOwnerController {
    /**
     * The logger.
     */
    private final Log logger = LogFactory.getLog(getClass());
    /**
     * OwnerDAO for DB actions.
     */
    @Autowired
    private PFMGenericDao ownerDao;
    /**
     * Handles the web app delete Owner request.
     * @param id The Owner.id of the owner to delete
     * @return The view to return to
     */
    @RequestMapping("/delete.htm")
    public final String handleRequest(@RequestParam(value = "Id")
                final Long id) {

        logger.info("Deleting Owner with Id " + id);
        Owner owner = ownerDao.findByPrimaryKey(id);
        ownerDao.delete(owner);

        return "redirect:/list.htm";
    }
    /**
     * Returns the OwnerDAO instance.
     * @return A OwnerDao instance
     */
    public final PFMGenericDao getOwnerDao() {
        return ownerDao;
    }
    /**
     * Sets the OwnerDao instance.
     * @param ownerDao The OwnerDao to set
     */
    public final void setOwnerDao(final PFMGenericDao ownerDao) {
        this.ownerDao = ownerDao;
    }
}

Create the DeleteOwnerController Unit Test

Create this Unit test in the directory 'src/test/java' under the package 'com.inivitiv.tutorials.pfm.web.controller' as filename 'DeleteOwnersControllerTest.java'

package com.inivitiv.tutorials.pfm.web.controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;

import com.inivitiv.tutorials.pfm.domain.Owner;
/**
 * Delete owner Spring Controller.
 * @author petersnr
 *
 */
public class DeleteOwnersControllerTest extends PFMControllerTestsInit {
    /**
     * Logger.
     */
    private final Log logger = LogFactory.getLog(getClass());
    
    /**
     * Tests handleRequest method.
     * 
     * @throws Exception
     *             The exception thrown if an error occurs
     */
    @Test
    public void testHandleRequestView() throws Exception {
        DeleteOwnerController controller = new DeleteOwnerController();
        controller.setOwnerDao(ownerDaoImpl);
        Owner owner = new Owner("Unit", "Test", "Delete");
        Long id = ownerDaoImpl.saveOrUpdate(owner);
        String view = controller.handleRequest(id);
        owner = ownerDaoImpl.findByPrimaryKey(id);
        assertNull(owner);
        // Check model contains key for array of users.
        // Check model returns correct view
        logger.info("Testing correct view is set for DeleteOwnerController()");
        assertEquals("redirect:/list.htm", view);
    }
}
Run the unit tests and ensure all pass, fix any errors.

Update the List View for Delete of Account Owners

We now have everything in place so that we can now delete Account Owners, the list.vm Velocity template just needs a link so that Account Owners can be selected for deletion. The deletion request will also use a small snippet of JavaScript to present a prompt for confirmation. Update the list.vm view so that it contains the links in the table view as listed below. The items in bold are the changes made. A new column is added to the table and this column will now contain a link that carries out the delete, the JavaScript provides the prompt we need as specified in the User Story.

#parse("WEB-INF/velocity/header.vm")
<h2>#springMessage("listowner.heading")</h2>
#if( ${owners.size()} > 0 )
<p>
<table>
<tr><th>ID</th><th>First Name</th><th>Middle Name</th><th>Surname</th><th>&nbsp;</th></tr>
#foreach(${owner} in ${owners})
    <tr>
        <td>${owner.id}</td>
        <td>${owner.firstName}</td>
        <td>${owner.middleName}</td>
        <td>${owner.surname}</td>

        <td><a href="delete.htm?Id=${owner.id}" 
        onclick="return confirm('Are you sure you want to delete owner with id: ${owner.id}?')">Delete</a></td>

    </tr>
#end
</table>
</p>
#else
<h4>No Account Owners found in Personal Finance Manager</h4>
#end
<a href="#springUrl("/editowner.htm?Id=")">#springMessage("addowner.link.text")</a>
#parse("WEB-INF/velocity/footer.vm")

Add the Delete Account Owners Mapping to pfm-webapp-servlet.xml

Finally we add the mapping for the delete to the Spring servlet XML file. Add this bean mapping to the file 'src/main/webapp/WEB-INF/pfm-webapp-servlet.xml just after the mapping for the '/editowner.htm' bean.

  <!-- Mapping for deleting an Account Owner -->
  <bean name="/delete.htm" class="com.inivitiv.tutorials.pfm.web.controller.DeleteOwnerController">
    <property name="ownerDao" ref="ownerDao"/>
  </bean>
Now you are ready to try out the PFM Web Application to add and delete Account Owners. Start up the web app with the maven command.
mvn jetty:run
Navigate to http://localhost:8080/pfm-webapp/list.htm your view will now contain a link in the table to delete Account Owners, try to add and delete owners.

delete-owner-confirm.png

A screenshot of the delete owner screen after an Account Owner delete link was selected.

Account Owner Details View

Epic 4a Web Application Views - iii Account Owner Details View

We will now create a read only view to show the account owners details, it will also contain a link so that the details can be edited, again we will create a Spring controller (and test), a view, and a Spring mapping.

The ViewOwnerController

Create the following controller in the 'com.inivitiv.tutorials.pfm.web.controller' package as file 'ViewOwnerController.java'.

package com.inivitiv.tutorials.pfm.web.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
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 com.inivitiv.tutorials.pfm.domain.Owner;
import com.inivitiv.tutorials.pfm.persist.PFMGenericDao;
/**
 * Spring View Owner instance controller.
 * @author petersnr
 */
@Controller
@RequestMapping("/viewowner.htm")
public class ViewOwnerController {

    /** Logger for this class and subclasses. */
    private final Log logger = LogFactory.getLog(getClass());
    /** OwnerDao for DB actions. */
    @Autowired
    private PFMGenericDao ownerDao;


    /**
     * Handles the controllers get request.
     * @param id Owner Id to retrieve
     * @return  An Owner instance
     */
    @RequestMapping(method = RequestMethod.GET)
    public final Owner intializeView(@RequestParam(value = "Id")final Long id) {
        logger.info("Retrieving Owner with id: " + id);
        Owner owner = ownerDao.findByPrimaryKey(id);

        return owner;
    }

    /**
     * Get the OwnerDao.
     * @return The OwnerDao instance
     */
    public final PFMGenericDao getOwnerDao() {
        return ownerDao;
    }
    /**
     * Set the OwnerDao instance.
     * @param ownerDaoNew The OwnerDao to set
     */
    public final void setOwnerDao(
            final PFMGenericDao ownerDaoNew) {
        this.ownerDao = ownerDaoNew;
    }

}

ViewOwnerController Unit Test

And the Unit test for the controller in the 'src/test/java/' folder in package 'com.inivitiv.tutorials.pfm.web.controller' as below...

package com.inivitiv.tutorials.pfm.web.controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;

import com.inivitiv.tutorials.pfm.domain.Owner;
/**
 * Tests the Spring ViewOwnerController.
 * @author petersnr
 *
 */
public class ViewOwnerControllerTest extends PFMControllerTestsInit {
    /**
     * Logger.
     */
    private final Log logger = LogFactory.getLog(getClass());
    /**
     * Tests handleRequest method.
     *
     * @throws Exception
     *             The exception thrown if an error occurs
     */
    @Test
    public void testIntializeView() throws Exception {
        ViewOwnerController controller = new ViewOwnerController();
        controller.setOwnerDao(ownerDaoImpl);
        Owner owner = new Owner("View", "Owner", "Unittest");
        Long id = ownerDaoImpl.saveOrUpdate(owner);
        assertNotNull(id);
        owner = controller.intializeView(id);
        // Check model contains key for array of users.
        logger.info("Testing  ViewOwnerController() returns correct owner.");
        assertEquals(id, owner.getId());

    }
}

View Owner Details Velocity Template

For the read only view we will create the following template...

#parse("WEB-INF/velocity/header.vm")
<h2>#springMessage("viewowner.heading")</h2>
$br
<table>
<tr><th>First Name</th><th>Middle Name</th><th>Surname</th><th></th></tr>
    <tr>
        <td>${owner.firstName}</td>
        <td>${owner.middleName}</td>
        <td>${owner.surname}</td>
        <td><a href="editowner.htm?Id=${owner.id}">Edit</a></td>
    </tr>
</table>
<br/>
<a href="#springUrl("/list.htm")">#springMessage("listowner.link.text")</a>
#parse("WEB-INF/velocity/footer.vm")
Notice that in the fourth column a link exists to edit the owners details, this will use the same controller for adding owners so there is no further work to do for the 'Edit Account Owners User Story'.

We also need to provide a link from the list of Account Owners to the 'View Details' view. Upate the list.vm Velocity template so it contains the link as shown below in the first column of the table.

<td><a href="viewowner.htm?Id=${owner.id}">${owner.id}</a></td>

The new view contains some new text that will be read from the messages.properties file, add the new entries so that the file matches the entries shown below.

title=Personal Finance Manager
listowner.heading=Account Owners Listing
listowner.link.text=Owner Listing
addowner.link.text=Add New Account Owner
addowner.heading=Add Account Owner
editowner.heading=Edit Account Owner Details
viewowner.heading=View Account Owner
btn.cancel=Cancel
btn.submit=Submit
error.owner.firstname.not-specified=First Name Required
error.owner.middlename.not-specified=Middle Name Required
error.owner.surname.not-specified=Surname Required

Add the View Owner Details Mapping to pfm-webapp-servlet.xml

Finally we add the mapping to the view details screen by adding this bean mapping definition to the Spring servlet file pfm-webapp-servlet.xml...

  <bean name="/viewowner.htm" class="com.inivitiv.tutorials.pfm.web.controller.ViewOwnerController">
    <property name="ownerDao" ref="ownerDao"/>
  </bean>

Finally run the Unit tests to ensure no errors exist, then start up the web application using mvn jetty:run again and navigate to http://localhost:8080/pfm-webapp/list.htm. Now you will see a link for each Owner Id, once clicked the 'View Account Owners' view will be displayed with the details as read only. Click on the 'Edit' link and the view is changed allowing you to upate the Account Owners details.

The 'List Owners View' with link to view owner details read only view on the Account Owners ID field....

listing-view-complete.png

The 'View Owner Details Read Only View' with a link to the 'Edit Owners View'...

view-owners-ro.png

The 'Edit Owner Details View'...

edit-owner-details.png

This completes all the User Stories for Sprint Two. Now we will continue with our Personal Finance Manager application by adding Owner Accounts to the application. On the next page we will define Sprint Three User Stories and tasks.

Date Entered2014-06-09