Featured FREE Whitepapers

What's New Here?

spring-logo

Spring – Adding Spring MVC – part 2

In the previous part we’ve implemented controllers for managers and employees. Now that we know our way around, we’ll do little (but just little) more complicated stuff – controllers for tasks & timesheets. So let’s start with org.timesheet.web.TaskController. First create a class and this time we will be accessing richer domain, so we’ll need to autowire three DAOS – for tasks, employees and managers. @Controller @RequestMapping('/tasks') public class TaskController {private TaskDao taskDao; private EmployeeDao employeeDao; private ManagerDao managerDao;@Autowired public void setTaskDao(TaskDao taskDao) { this.taskDao = taskDao; }@Autowired public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; }@Autowired public void setManagerDao(ManagerDao managerDao) { this.managerDao = managerDao; }public EmployeeDao getEmployeeDao() { return employeeDao; }public TaskDao getTaskDao() { return taskDao; }public ManagerDao getManagerDao() { return managerDao; } } Let’s handle GET request on /tasks: /** * Retrieves tasks, puts them in the model and returns corresponding view * @param model Model to put tasks to * @return tasks/list */ @RequestMapping(method = RequestMethod.GET) public String showTasks(Model model) { model.addAttribute('tasks', taskDao.list());return 'tasks/list'; } We will place JSPs in tasks subfolder. First is list.jsp for showing all tasks. It does not only iterate through all tasks, but on each task it iterates through employees: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><!-- resolve variables --> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task>'--%><html> <head> <title>Tasks</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h1>List of tasks</h1> <a href='tasks?new'>Add new task</a> <table cellspacing='5' class='main-table wide'> <tr> <th style='width: 35%;'>Description</th> <th>Manager</th> <th>Employees</th> <th>Completed</th> <th style='width: 20%;'>Details</th> <th>Delete</th> </tr> <c:forEach items='${tasks}' var='task'> <tr> <td>${task.description}</td> <td> <a href='managers/${task.manager.id}'>${task.manager.name}</a> </td> <td> <c:forEach items='${task.assignedEmployees}' var='emp'> <a href='employees/${emp.id}'>${emp.name}</a> </c:forEach> </td> <td> <div class='delete'> <c:choose> <c:when test='${task.completed}'> Done </c:when> <c:when test='${!task.completed}'> In progress </c:when> </c:choose> </div> </td> <td> <a href='tasks/${task.id}'>Go to page</a> </td> <td> <sf:form action='tasks/${task.id}' method='delete' cssClass='delete'> <input type='submit' value='' class='delete-button' /> </sf:form> </td> </tr> </c:forEach> </table><br /> <a href='welcome'>Go back</a> </body> </html> Deleting task as usual: /** * Deletes task with specified ID * @param id Task's ID * @return redirects to tasks if everything was ok * @throws TaskDeleteException When task cannot be deleted */ @RequestMapping(value = '/{id}', method = RequestMethod.DELETE) public String deleteTask(@PathVariable('id') long id) throws TaskDeleteException {Task toDelete = taskDao.find(id); boolean wasDeleted = taskDao.removeTask(toDelete);if (!wasDeleted) { throw new TaskDeleteException(toDelete); }// everything OK, see remaining tasks return 'redirect:/tasks'; }TaskDeleteException: package org.timesheet.web.exceptions;import org.timesheet.domain.Task;/** * When task cannot be deleted. */ public class TaskDeleteException extends Exception {private Task task;public TaskDeleteException(Task task) { this.task = task; }public Task getTask() { return task; } } Method for handling this exception: /** * Handles TaskDeleteException * @param e Thrown exception with task that couldn't be deleted * @return binds task to model and returns tasks/delete-error */ @ExceptionHandler(TaskDeleteException.class) public ModelAndView handleDeleteException(TaskDeleteException e) { ModelMap model = new ModelMap(); model.put('task', e.getTask()); return new ModelAndView('tasks/delete-error', model); } JSP page jsp/tasks/delete-error.jsp for showing deletion error: <%--@elvariable id='task' type='org.timesheet.domain.Task'--%><html> <head> <title>Cannot delete task</title> </head> <body> Oops! Resource <a href='${task.id}'>${task.description}</a> can not be deleted.<p> Make sure there are no timesheets assigned on task. </p><br /><br /><br /> <a href='../welcome'>Back to main page.</a> </body> </html> Showing task’s detail will be accessed with URI /tasks/{id}. We’ll put in the model both task and unassigned employees that can be added to the task. It’ll be handled like so: /** * Returns task with specified ID * @param id Tasks's ID * @param model Model to put task to * @return tasks/view */ @RequestMapping(value = '/{id}', method = RequestMethod.GET) public String getTask(@PathVariable('id') long id, Model model) { Task task = taskDao.find(id); model.addAttribute('task', task);// add all remaining employees List<Employee> employees = employeeDao.list(); Set<Employee> unassignedEmployees = new HashSet<Employee>();for (Employee employee : employees) { if (!task.getAssignedEmployees().contains(employee)) { unassignedEmployees.add(employee); } }model.addAttribute('unassigned', unassignedEmployees);return 'tasks/view'; } Now something slightly more complicated. We would like to show user detail page of the task. On this task we’d like to add/remove employees assigned on it. First, let’s think of URL. Tasks have assigned employees, so our URL for accessing employee on task will be like this: /tasks/{id}/employees/{employeeId} To remove employee, we will simply access this resource with DELETE method, so let’s add method to controller: /** * Removes assigned employee from task * @param taskId Task's ID * @param employeeId Assigned employee's ID */ @RequestMapping(value = '/{id}/employees/{employeeId}', method = RequestMethod.DELETE) @ResponseStatus(HttpStatus.NO_CONTENT) public void removeEmployee( @PathVariable('id') long taskId, @PathVariable('employeeId') long employeeId) {Employee employee = employeeDao.find(employeeId); Task task = taskDao.find(taskId);task.removeEmployee(employee); taskDao.update(task); } On the view page (we’ll see that just in moment), we will simply alter DOM model using jQuery and remove assigned employee from list. Let’s pretend that nothing can go wrong (we have NO_CONTENT response) so employee will always be successfully removed from DB. So we can simply alter that DOM model. For adding employee, we will have selection list (or combo box) of unassigned employees. When employee is removed we will append this to selection of available employees (he is available again). When employee will be added, we will alter Task with DAO and redirect back to same task (everything will be updated). Here’s code for assigning employee to task: /** * Assigns employee to tak * @param taskId Task's ID * @param employeeId Employee's ID (to assign) * @return redirects back to altered task: tasks/taskId */ @RequestMapping(value = '/{id}/employees/{employeeId}', method = RequestMethod.PUT) public String addEmployee( @PathVariable('id') long taskId, @PathVariable('employeeId') long employeeId) {Employee employee = employeeDao.find(employeeId); Task task = taskDao.find(taskId);task.addEmployee(employee); taskDao.update(task); return 'redirect:/tasks/' + taskId; } And finally, tasks/view.jsp for details of Task. As I mentioned, there is lot of DOM altering so this code might seem little more difficult than usually. <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%><%--@elvariable id='task' type='org.timesheet.domain.Task'--%> <%--@elvariable id='unassigned' type='java.util.List<org.timesheet.domain.Employee>'--%><html> <head> <title>Task page</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Task info</h2> <div id='list'> <ul> <li> <label for='description'>Description:</label> <input name='description' id='description' value='${task.description}' disabled='${task.completed ? 'disabled' : ''}' /> </li> <li> <label for='manager'>Manager:</label> <input name='manager' id='manager' value='${task.manager.name}' disabled='true' /> </li> <li> <label for='employees'>Employees:</label> <table id='employees' class='task-table'> <c:forEach items='${task.assignedEmployees}' var='emp'> <tr> <sf:form action='${task.id}/employees/${emp.id}' method='delete'> <td> <a href='../employees/${emp.id}' id='href-${emp.id}'>${emp.name}</a> </td> <td> <input type='submit' value='Remove' id='remove-${emp.id}' /> <script src='/timesheet-app/resources/jquery-1.7.1.js'></script> <script type='text/javascript'> $('#remove-${emp.id}').on('click', function() { $('#remove-${emp.id}').addClass('hidden'); $('#href-${emp.id}').remove();// add to list of unassigned var opt = document.createElement('option'); opt.setAttribute('value', '${emp.id}'); opt.textContent = '${emp.name}'; $('#selected-emp').append(opt); }); </script> </td> </sf:form> </tr> </c:forEach> </table> </li> <li> <label for='unassigned'>Unassgined:</label> <table id='unassigned' class='task-table'> <tr> <sf:form method='put' id='add-form'> <td> <select id='selected-emp'> <c:forEach items='${unassigned}' var='uemp'> <option value='${uemp.id}'> ${uemp.name} </option> </c:forEach> </select> </td> <td> <input type='submit' value='Add' id='add-employee' /> <script src='/timesheet-app/resources/jquery-1.7.1.js'></script> <script type='text/javascript'> $('#add-employee').on('click', function() { $('#selected-emp').selected().remove(); }); </script> </td> </sf:form> </tr> </table> </li> </ul> </div><br /><br /> <a href='../tasks'>Go Back</a><script src='/timesheet-app/resources/jquery-1.7.1.js'></script> <script type='text/javascript'> (function() { // prepare default form action setAddAction();// handler for changing action $('#selected-emp').on('change', function() { setAddAction(); });function setAddAction() { var id = $('#selected-emp').val(); $('#add-form').attr('action', '${task.id}/employees/' + id); } })(); </script> </body> </html> As you can observe from the code, we’re again using only HTML + JavaScript. Only thing that is JSP specific is bringing data from model to the page. OK, now we must be able to create new Task. Let’s prepare our controller for serving form for adding task that will be accessed from /tasks?new: /** * Creates form for new task. * @param model Model to bind to HTML form * @return tasks/new */ @RequestMapping(params = 'new', method = RequestMethod.GET) public String createTaskForm(Model model) { model.addAttribute('task', new Task());// list of managers to choose from List<Manager> managers = managerDao.list(); model.addAttribute('managers', managers);return 'tasks/new'; } Task consists of name, manager and assigned employees. For scope of this tutorial, I decided not to implement the last one. We will simply generate some employees. If you will ever want to be able to pick employees from some sort of selection list and assign them to task, then please note, that this should be done asynchronously. For that purposes you can map special methods to controller and do AJAX posts for example with jQuery with $.post. I think that would be little too much for this tutorial, but if you’re interested how to use AJAX with Spring, check out this blog post on ajax simplifications in Spring 3. When we were creating Employees and Managers, we only used primitive types for properties. Now we would like to assign actual Manager instance to task. So we will have to tell Spring how it should convert value from select list (manager’s id) to actual instance. For this we will use custom PropertyEditorSupport facility. Add new org.timesheet.web.editors package and create new class ManagerEditor with following code: public class ManagerEditor extends PropertyEditorSupport {private ManagerDao managerDao;public ManagerEditor(ManagerDao managerDao) { this.managerDao = managerDao; }@Override public void setAsText(String text) throws IllegalArgumentException { long id = Long.parseLong(text); Manager manager = managerDao.find(id); setValue(manager); } } ManagerEditor will have passed DAO in it’s constructor. It will lookup actual manager by it’s ID and call parent’s setValue. Spring should now know there is such an editor, so we must register it in our controller. We only need method that has WebDataBinder as parameter and we need to annotate it with @InitBinder annotation like so: @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Manager.class, new ManagerEditor(managerDao)); } And that’s it, Spring now knows how to assign manager to our task directly from form. Finally code for saving Task. As I said earlier, we will generate some employees to task just before saving it: /** * Saves new task to the database * @param task Task to save * @return redirects to tasks */ @RequestMapping(method = RequestMethod.POST) public String addTask(Task task) { // generate employees List<Employee> employees = reduce(employeeDao.list());task.setAssignedEmployees(employees); taskDao.add(task);return 'redirect:/tasks'; } There is reduce method, which is simple helper method for reducing employees in memory. This is not terribly effective, we could do that rather with more sophisticated query, but for now it’ll do just fine. Also feel free to roll your own reduce logic if you want: /** * Reduces list of employees to some smaller amount. * Simulates user interaction. * @param employees Employees to reduced * @return New list of some employees from original employees list */ private List<Employee> reduce(List<Employee> employees) { List<Employee> reduced = new ArrayList<Employee>(); Random random = new Random(); int amount = random.nextInt(employees.size()) + 1;// max. five employees amount = amount > 5 ? 5 : amount;for (int i = 0; i < amount; i++) { int randomIdx = random.nextInt(employees.size()); Employee employee = employees.get(randomIdx); reduced.add(employee); employees.remove(employee); }return reduced; } Let’s see tasks/new.jsp page now: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%><%--@elvariable id='task' type='org.timesheet.domain.Task'--%> <%--@elvariable id='managers' type='java.util.List<org.timesheet.domain.Manager'--%><html> <head> <title>Add new task</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Add new Task</h2> <div id='list'> <sf:form method='post' action='tasks' commandName='task'> <ul> <li> <label for='description'>Description:</label> <input name='description' id='description' value='${task.description}' /> </li> <li> <label for='manager-select'>Manager:</label> <sf:select path='manager' id='manager-select'> <sf:options items='${managers}' itemLabel='name' itemValue='id' /> </sf:select> </li> <li> Employees will be generated ... </li> <li> <input type='submit' value='Save'> </li> </ul> </sf:form> </div><br /><br /> <a href='tasks'>Go Back</a></body> </html> And of course test for controller: package org.timesheet.web;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.web.servlet.ModelAndView; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao; import org.timesheet.service.dao.TaskDao; import org.timesheet.web.exceptions.TaskDeleteException;import java.util.Collection; import java.util.List;import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;@ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class TaskControllerTest extends DomainAwareBase {private Model model; // used for controller @Autowired private TaskDao taskDao;@Autowired private ManagerDao managerDao;@Autowired private EmployeeDao employeeDao; @Autowired private TaskController controller; @Before public void setUp() { model = new ExtendedModelMap(); } @After public void cleanUp() { List<Task> tasks = taskDao.list(); for (Task task : tasks) { taskDao.remove(task); } }@Test public void testShowTasks() { // prepare some data Task task = sampleTask(); // use controller String view = controller.showTasks(model); assertEquals('tasks/list', view);List<Task> listFromDao = taskDao.list(); Collection<?> listFromModel = (Collection<?>) model.asMap ().get('tasks');assertTrue(listFromModel.contains(task)); assertTrue(listFromDao.containsAll(listFromModel)); } @Test public void testDeleteTaskOk() throws TaskDeleteException { Task task = sampleTask(); long id = task.getId();// delete & assert String view = controller.deleteTask(id); assertEquals('redirect:/tasks', view); assertNull(taskDao.find(id)); } @Test(expected = TaskDeleteException.class) public void testDeleteTaskThrowsException() throws TaskDeleteException { Task task = sampleTask(); long id = task.getId(); // mock DAO for this call TaskDao mockedDao = mock(TaskDao.class); when(mockedDao.removeTask(task)).thenReturn(false);TaskDao originalDao = controller.getTaskDao(); try { // delete & expect exception controller.setTaskDao(mockedDao); controller.deleteTask(id); } finally { controller.setTaskDao(originalDao); } } @Test public void testHandleDeleteException() { Task task = sampleTask(); TaskDeleteException e = new TaskDeleteException(task); ModelAndView modelAndView = controller.handleDeleteException(e);assertEquals('tasks/delete-error', modelAndView.getViewName()); assertTrue(modelAndView.getModelMap().containsValue(task)); } @Test public void testGetTask() { Task task = sampleTask(); long id = task.getId();// get & assert String view = controller.getTask(id, model); assertEquals('tasks/view', view); assertEquals(task, model.asMap().get('task')); } @Test public void testRemoveEmployee() { Task task = sampleTask(); long id = task.getAssignedEmployees().get(0).getId(); controller.removeEmployee(task.getId(), id);// task was updated inside controller in other transaction -> refresh task = taskDao.find(task.getId());// get employee & assert Employee employee = employeeDao.find(id); assertFalse(task.getAssignedEmployees().contains(employee)); } @Test public void testAddEmployee() { Task task = sampleTask(); Employee cassidy = new Employee('Butch Cassidy', 'Cowboys'); employeeDao.add(cassidy); controller.addEmployee(task.getId(), cassidy.getId());// task was updated inside controller in other transaction -> refresh task = taskDao.find(task.getId());// get employee & assert Employee employee = employeeDao.find(cassidy.getId()); assertTrue(task.getAssignedEmployees().contains(employee)); } @Test public void testAddTask() { Task task = sampleTask(); // save via controller String view = controller.addTask(task); assertEquals('redirect:/tasks', view); // task is in DB assertEquals(task, taskDao.find(task.getId())); }private Task sampleTask() { Manager manager = new Manager('Jesse James'); managerDao.add(manager);Employee terrence = new Employee('Terrence', 'Cowboys'); Employee kid = new Employee('Sundance Kid', 'Cowboys'); employeeDao.add(terrence); employeeDao.add(kid);Task task = new Task('Wild West', manager, terrence, kid); taskDao.add(task); return task; } } That’s it for Tasks. Now let’s create controllers for timesheets. Add basic boilerplate for controller and autowired DAOs that we’ll require: @Controller @RequestMapping('/timesheets') public class TimesheetController {private TimesheetDao timesheetDao; private TaskDao taskDao; private EmployeeDao employeeDao;@Autowired public void setTimesheetDao(TimesheetDao timesheetDao) { this.timesheetDao = timesheetDao; }@Autowired public void setTaskDao(TaskDao taskDao) { this.taskDao = taskDao; }@Autowired public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; }public TimesheetDao getTimesheetDao() { return timesheetDao; }public TaskDao getTaskDao() { return taskDao; }public EmployeeDao getEmployeeDao() { return employeeDao; } } Method for handling GET request on timesheets: /** * Retrieves timesheets, puts them in the model and returns corresponding view * @param model Model to put timesheets to * @return timesheets/list */ @RequestMapping(method = RequestMethod.GET) public String showTimesheets(Model model) { List<Timesheet> timesheets = timesheetDao.list(); model.addAttribute('timesheets', timesheets);return 'timesheets/list'; } JSPs will be placed in timesheets subfolder. Add list.jsp page, that will basically iterate through Timesheet’s properties and roll deleting form: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><!-- resolve variables --> <%--@elvariable id='timesheets' type='java.util.List<org.timesheet.domain.Timesheet>'--%><html> <head> <title>Timesheets</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h1>List of timesheets</h1> <a href='timesheets?new'>Add new timesheet</a> <table cellspacing='5' class='main-table wide'> <tr> <th style='width: 30%'>Employee</th> <th style='width: 50%'>Task</th> <th>Hours</th> <th>Details</th> <th>Delete</th> </tr> <c:forEach items='${timesheets}' var='ts'> <tr> <td> <a href='employees/${ts.who.id}'>${ts.who.name}</a> </td> <td> <a href='tasks/${ts.task.id}'>${ts.task.description}</a> </td> <td>${ts.hours}</td> <td> <a href='timesheets/${ts.id}'>Go to page</a> </td> <td> <sf:form action='timesheets/${ts.id}' method='delete' cssClass='delete'> <input type='submit' class='delete-button'> </sf:form> </td> </tr> </c:forEach> </table><br /> <a href='welcome'>Go back</a> </body> </html> Deleting Timesheet is easier than deleting task, because we won’t break any constraint in database, so we can simply use default remove method on DAO: /** * Deletes timeshet with specified ID * @param id Timesheet's ID * @return redirects to timesheets */ @RequestMapping(value = '/{id}', method = RequestMethod.DELETE) public String deleteTimesheet(@PathVariable('id') long id) { Timesheet toDelete = timesheetDao.find(id); timesheetDao.remove(toDelete);return 'redirect:/timesheets'; } We will access individual Timesheet resource by adding it’s ID to URI as usual, so we’ll handle /timesheets/{id}. But there are objects assigned to timesheet – Task instance and Employee instance. We don’t want form to null them out. Therefore we will introduce lightweight command backing object for form. We will update only hours and then set those new hours on real Timesheet instance: /** * Returns timesheet with specified ID * @param id Timesheet's ID * @param model Model to put timesheet to * @return timesheets/view */ @RequestMapping(value = '/{id}', method = RequestMethod.GET) public String getTimesheet(@PathVariable('id') long id, Model model) { Timesheet timesheet = timesheetDao.find(id); TimesheetCommand tsCommand = new TimesheetCommand(timesheet); model.addAttribute('tsCommand', tsCommand);return 'timesheets/view'; } And here’s code for TimesheetCommand which is now under new package org.timesheet.web.commands: package org.timesheet.web.commands;import org.hibernate.validator.constraints.Range; import org.timesheet.domain.Timesheet;import javax.validation.constraints.NotNull;public class TimesheetCommand {@NotNull @Range(min = 1, message = 'Hours must be 1 or greater') private Integer hours; private Timesheet timesheet;// default c-tor for bean instantiation public TimesheetCommand() {}public TimesheetCommand(Timesheet timesheet) { hours = timesheet.getHours(); this.timesheet = timesheet; }public Integer getHours() { return hours; }public void setHours(Integer hours) { this.hours = hours; }public Timesheet getTimesheet() { return timesheet; }public void setTimesheet(Timesheet timesheet) { this.timesheet = timesheet; }@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; }TimesheetCommand that = (TimesheetCommand) o;if (hours != null ? !hours.equals(that.hours) : that.hours != null) { return false; } if (timesheet != null ? !timesheet.equals(that.timesheet) : that.timesheet != null) { return false; }return true; }@Override public int hashCode() { int result = hours != null ? hours.hashCode() : 0; result = 31 * result + (timesheet != null ? timesheet.hashCode() : 0); return result; } } Pretty straightforward, but what are those @NotNull and @Range annotations? Well, we certailny don’t want user to enter negative or zero number for amount of hours, so we will use this neat JSR 303 Bean Validation API. To make it work, simply add dependency to hibernate validator to your pom.xml: <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.2.0.Final</version> </dependency> Once Hibernate Validator is in our classpath, default validator will automatically be picked. To make it work though, we must enable annotation driven MVC, so add following line to timesheet-servlet.xml bean config file: <mvc:annotation-driven /> We’ll see usage of a valid model a few lines later. Under timesheets folder we’ll now create view.jsp page that’ll contain info about single timesheet: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%><%--@elvariable id='tsCommand' type='org.timesheet.web.commands.TimesheetCommand'--%><html> <head> <title>Timesheet page</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Timesheet info</h2> <div id='list'> <sf:form method='post' modelAttribute='tsCommand'> <sf:errors path='*' cssClass='errors' element='div' /> <ul> <li> <label for='employeeName'>Assigned employee:</label> <a id='employee' href='../employees/${tsCommand.timesheet.who.id}'> ${tsCommand.timesheet.who.name} </a> </li> <li> <label for='task'>Task:</label> <a id='task' href='../tasks/${tsCommand.timesheet.task.id}'> ${tsCommand.timesheet.task.description} </a> </li> <li> <label for='hours'>Hours:</label> <input name='hours' id='hours' value='${tsCommand.hours}' /> </li> <li> <input type='submit' value='Save' /> </li> </ul> </sf:form> </div><br /><br /> <a href='../timesheets'>Go Back</a> </body> </html> In this view page we have submit button that’ll trigger POST request on /timesheets/{id} and pass updated model (TimesheetCommand instance in that). So let’s handle this. We will use @Valid annotation, which is part of JSR 303 Bean Validation API which marks object to validate. Also note, that TimesheetCommand must be annotated with @ModelAttribute annotation, because this command is bound to web view. Validation errors are stored in BindingResult object: /** * Updates timesheet with given ID * @param id ID of timesheet to lookup from DB * @param tsCommand Lightweight command object with changed hours * @return redirects to timesheets */ @RequestMapping(value = '/{id}', method = RequestMethod.POST) public String updateTimesheet(@PathVariable('id') long id, @Valid @ModelAttribute('tsCommand') TimesheetCommand tsCommand, BindingResult result) {Timesheet timesheet = timesheetDao.find(id); if (result.hasErrors()) { tsCommand.setTimesheet(timesheet); return 'timesheets/view'; }// no errors, update timesheet timesheet.setHours(tsCommand.getHours()); timesheetDao.update(timesheet);return 'redirect:/timesheets'; } For adding, we will have to pick from select menus of existing Tasks and Employees, so we’ll pass list of those when serving new form: /** * Creates form for new timesheet * @param model Model to bind to HTML form * @return timesheets/new */ @RequestMapping(params = 'new', method = RequestMethod.GET) public String createTimesheetForm(Model model) { model.addAttribute('timesheet', new Timesheet()); model.addAttribute('tasks', taskDao.list()); model.addAttribute('employees', employeeDao.list()); return 'timesheets/new'; }For showing select lists of Employees and Tasks we again need to create editors for them. We saw this approach earlier, so as before let’s add 2 new editors to our project that use corresponding DAOs: package org.timesheet.web.editors;import org.timesheet.domain.Employee; import org.timesheet.service.dao.EmployeeDao;import java.beans.PropertyEditorSupport;/** * Will convert ID from combobox to employee's instance. */ public class EmployeeEditor extends PropertyEditorSupport {private EmployeeDao employeeDao;public EmployeeEditor(EmployeeDao employeeDao) { this.employeeDao = employeeDao; }@Override public void setAsText(String text) throws IllegalArgumentException { long id = Long.parseLong(text); Employee employee = employeeDao.find(id); setValue(employee); } } package org.timesheet.web.editors;import org.timesheet.domain.Task; import org.timesheet.service.dao.TaskDao;import java.beans.PropertyEditorSupport;public class TaskEditor extends PropertyEditorSupport {private TaskDao taskDao;public TaskEditor(TaskDao taskDao) { this.taskDao = taskDao; }@Override public void setAsText(String text) throws IllegalArgumentException { long id = Long.parseLong(text); Task task = taskDao.find(id); setValue(task); } } We’ll register these editors in TimesheetController initBinder method: @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Employee.class, new EmployeeEditor(employeeDao)); binder.registerCustomEditor(Task.class, new TaskEditor(taskDao)); } Now we can safely add new.jsp under timesheets folder, because select lists will correctly be populated with data passed in the model: <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='employees' type='java.util.List<org.timesheet.domain.Employee'--%> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task'--%><html> <head> <title>Add new timesheet</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Add new Timesheet</h2> <div id='list'> <sf:form method='post' action='timesheets' commandName='timesheet'> <ul> <li> <label for='employees'>Pick employee:</label> <sf:select path='who' id='employees'> <sf:options items='${employees}' itemLabel='name' itemValue='id' /> </sf:select> </li> <li> <label for='tasks'>Pick task:</label> <sf:select path='task' id='tasks'> <sf:options items='${tasks}' itemLabel='description' itemValue='id' /> </sf:select> </li> <li> <label for='hours'>Hours:</label> <sf:input path='hours' /> </li> <li> <input type='submit' value='Save' /> </li> </ul> </sf:form> </div><br /><br /> <a href='timesheets'>Go Back</a> </body> </html> Submit button submits POST request on /timesheets path, so we’ll handle this with pretty straightforward controller method: /** * Saves new Timesheet to the database * @param timesheet Timesheet to save * @return redirects to timesheets */ @RequestMapping(method = RequestMethod.POST) public String addTimesheet(Timesheet timesheet) { timesheetDao.add(timesheet);return 'redirect:/timesheets'; } So all timesheet functionalities should be working now, just make sure by using application for a while. Of course, we will also write unit test for TimesheetController now. In test methods testUpdateTimesheetValid and testUpdateTimesheetInValid we are not validating object manually, but we’re mocking validator instead: package org.timesheet.web;import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.domain.Timesheet; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao; import org.timesheet.service.dao.TaskDao; import org.timesheet.service.dao.TimesheetDao; import org.timesheet.web.commands.TimesheetCommand;import java.util.Collection; import java.util.List;import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;@ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class TimesheetControllerTest extends DomainAwareBase { @Autowired private TimesheetDao timesheetDao;@Autowired private EmployeeDao employeeDao;@Autowired private ManagerDao managerDao;@Autowired private TaskDao taskDao;@Autowired private TimesheetController controller; private Model model; // used for controller@Before public void setUp() { model = new ExtendedModelMap(); }@Test public void testShowTimesheets() { // prepare some data Timesheet timesheet = sampleTimesheet();// use controller String view = controller.showTimesheets(model); assertEquals('timesheets/list', view);List<Timesheet> listFromDao = timesheetDao.list(); Collection<?> listFromModel = (Collection<?>) model.asMap().get('timesheets');assertTrue(listFromModel.contains(timesheet)); assertTrue(listFromDao.containsAll(listFromModel)); } @Test public void testDeleteTimesheet() { // prepare ID to delete Timesheet timesheet = sampleTimesheet(); timesheetDao.add(timesheet); long id = timesheet.getId();// delete & assert String view = controller.deleteTimesheet(id); assertEquals('redirect:/timesheets', view); assertNull(timesheetDao.find(id)); }@Test public void testGetTimesheet() { // prepare timesheet Timesheet timesheet = sampleTimesheet(); timesheetDao.add(timesheet); long id = timesheet.getId(); TimesheetCommand tsCommand = new TimesheetCommand(timesheet);// get & assert String view = controller.getTimesheet(id, model); assertEquals('timesheets/view', view); assertEquals(tsCommand, model.asMap().get('tsCommand')); }@Test public void testUpdateTimesheetValid() { // prepare ID to delete Timesheet timesheet = sampleTimesheet(); timesheetDao.add(timesheet); long id = timesheet.getId(); TimesheetCommand tsCommand = new TimesheetCommand(timesheet);// user alters Timesheet hours in HTML form with valid value tsCommand.setHours(1337); BindingResult result = mock(BindingResult.class); when(result.hasErrors()).thenReturn(false);// update & assert String view = controller.updateTimesheet(id, tsCommand, result); assertEquals('redirect:/timesheets', view); assertTrue(1337 == timesheetDao.find(id).getHours()); }@Test public void testUpdateTimesheetInValid() { // prepare ID to delete Timesheet timesheet = sampleTimesheet(); timesheetDao.add(timesheet); long id = timesheet.getId();TimesheetCommand tsCommand = new TimesheetCommand(timesheet); Integer originalHours = tsCommand.getHours();// user alters Timesheet hours in HTML form with valid value tsCommand.setHours(-1); BindingResult result = mock(BindingResult.class); when(result.hasErrors()).thenReturn(true);// update & assert String view = controller.updateTimesheet(id, tsCommand, result); assertEquals('timesheets/view', view); assertEquals(originalHours, timesheetDao.find(id).getHours()); }@Test public void testAddTimesheet() { // prepare timesheet Timesheet timesheet = sampleTimesheet();// save but via controller String view = controller.addTimesheet(timesheet); assertEquals('redirect:/timesheets', view);// timesheet is stored in DB assertEquals(timesheet, timesheetDao.find(timesheet.getId())); }private Timesheet sampleTimesheet() { Employee marty = new Employee('Martin Brodeur', 'NHL'); employeeDao.add(marty);Manager jeremy = new Manager('Jeremy'); managerDao.add(jeremy);Task winStanleyCup = new Task('NHL finals', jeremy, marty); taskDao.add(winStanleyCup);Timesheet stanelyCupSheet = new Timesheet(marty, winStanleyCup, 100); timesheetDao.add(stanelyCupSheet);return stanelyCupSheet; } } Last controller we have to do is for our special business service – TimesheetService. We already have implemented and tested it’s logic. Controller will simply merge this functionalities to one menu page and we’ll handle each with controller. So let’s add some boilerplate controller definition and DAO wiring at first: @Controller @RequestMapping('/timesheet-service') public class TimesheetServiceController {private TimesheetService service; private EmployeeDao employeeDao; private ManagerDao managerDao;@Autowired public void setService(TimesheetService service) { this.service = service; }@Autowired public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; }@Autowired public void setManagerDao(ManagerDao managerDao) { this.managerDao = managerDao; }} When user enters /timesheet-service with GET request, we will server him menu with populated data: /** * Shows menu of timesheet service: * that contains busiest task and employees and managers to * look for their assigned tasks. * @param model Model to put data to * @return timesheet-service/list */ @RequestMapping(method = RequestMethod.GET) public String showMenu(Model model) { model.addAttribute('busiestTask', service.busiestTask()); model.addAttribute('employees', employeeDao.list()); model.addAttribute('managers', managerDao.list());return 'timesheet-service/menu'; } Again, to make stuff work from select lists we will register editors (we’ll just reuse editors that we created recently): @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Employee.class, new EmployeeEditor(employeeDao)); binder.registerCustomEditor(Manager.class, new ManagerEditor(managerDao)); } Now we will be providing a service. We will have RESTful URLs once again, but actual resources won’t be directly mapped to domain models as before, but results of some internal service. So getting tasks for manager with id 123 will result to GET request timesheets/manager-tasks/123. Same for tasks for employee. We will form actual URLs with jQuery using listeners for select lists. Add timesheet-service folder and add there menu.jsp page with following content: <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='busiestTask' type='org.timesheet.domain.Task'--%> <%--@elvariable id='managers' type='java.util.List<org.timesheet.domain.Manager>'--%> <%--@elvariable id='employees' type='java.util.List<org.timesheet.domain.Employee>'--%><html> <head> <title>Timesheet Service</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h1>Timesheet services</h1> <div id='list'> <h3>Busiest task</h3> <ul> <li> <a href='/timesheet-app/tasks/${busiestTask.id}' id='busiest-task'>${busiestTask.description}</a> </li> </ul><h3>Tasks for manager</h3> <sf:form method='get' id='manager-form'> <ul> <li> <select id='select-managers'> <c:forEach items='${managers}' var='man'> <option value='${man.id}'>${man.name}</option> </c:forEach> </select> </li> <li> <input type='submit' value='Search' /> </li> </ul> </sf:form><h3>Tasks for employee</h3> <sf:form method='get' id='employee-form'> <ul> <li> <select id='select-employees'> <c:forEach items='${employees}' var='emp'> <option value='${emp.id}'>${emp.name}</option> </c:forEach> </select> </li> <li> <input type='submit' value='Search'> </li> </ul> </sf:form> </div><br /><br /> <a href='/timesheet-app/welcome'>Go Back</a><script src='/timesheet-app/resources/jquery-1.7.1.js'></script> <script type='text/javascript'> (function() { // set default actions setAddAction('#select-managers', '#manager-form', 'manager-tasks'); setAddAction('#select-employees', '#employee-form', 'employee-tasks');// handler for chaning action $('#select-managers').on('change', function() { setAddAction('#select-managers', '#manager-form', 'manager-tasks'); }); $('#select-employees').on('change', function() { setAddAction('#select-employees', '#employee-form', 'employee-tasks'); });function setAddAction(selectName, formName, action) { var id = $(selectName).val(); $(formName).attr('action', '/timesheet-app/timesheet-service/' + action + '/' + id); } })(); </script> </body> </html> Getting tasks for given manager: /** * Returns tasks for given manager * @param id ID of manager * @param model Model to put tasks and manager * @return timesheet-service/manager-tasks */ @RequestMapping(value = '/manager-tasks/{id}', method = RequestMethod.GET) public String showManagerTasks(@PathVariable('id') long id, Model model) { Manager manager = managerDao.find(id); List<Task> tasks = service.tasksForManager(manager);model.addAttribute('manager', manager); model.addAttribute('tasks', tasks);return 'timesheet-service/manager-tasks'; } And as a result page timesheet-service/manager-tasks.jsp will be rendered: <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='manager' type='org.timesheet.domain.Manager'--%> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task>'--%><html> <head> <title>Tasks for manager</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h3> Current manager: <a href='/timesheet-app/managers/${manager.id}'>${manager.name}</a> </h3> <div id='list'> <c:forEach items='${tasks}' var='task'> <li> <a href='/timesheet-app/tasks/${task.id}'>${task.description}</a> </li> </c:forEach> </div><br /><br /> <a href='../'>Go Back</a> </body> </html> We’ll do pretty much the same for employee: /** * Returns tasks for given employee * @param id ID of employee * @param model Model to put tasks and employee * @return timesheet-service/employee-tasks */ @RequestMapping(value = '/employee-tasks/{id}', method = RequestMethod.GET) public String showEmployeeTasks(@PathVariable('id') long id, Model model) { Employee employee = employeeDao.find(id); List<Task> tasks = service.tasksForEmployee(employee); model.addAttribute('employee', employee); model.addAttribute('tasks', tasks); return 'timesheet-service/employee-tasks'; } And jsp view employee-tasks.jsp: <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %><%--@elvariable id='employee' type='org.timesheet.domain.Employee'--%> <%--@elvariable id='tasks' type='java.util.List<org.timesheet.domain.Task>'--%><html> <head> <title>Tasks for employee</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h3> Current employee: <a href='/timesheet-app/employees/${employee.id}'>${employee.name}</a> </h3> <div id='list'> <c:forEach items='${tasks}' var='task'> <li> <a href='/timesheet-app/tasks/${task.id}'>${task.description}</a> </li> </c:forEach> </div><br /><br /> <a href='../'>Go Back</a> </body> </html> So lets make sure everything is well integrated and add unit test for this new contoller: package org.timesheet.web;import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.jdbc.SimpleJdbcTestUtils; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.service.TimesheetService; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao;import static org.junit.Assert.assertEquals;/** * This test relies on fact that DAOs and Services are tested individually. * Only compares, if controller returns the same as individual services. */ @ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class TimesheetServiceControllerTest extends DomainAwareBase {@Autowired private TimesheetServiceController controller;@Autowired private TimesheetService timesheetService;@Autowired private EmployeeDao employeeDao;@Autowired private ManagerDao managerDao;@Autowired private SimpleJdbcTemplate jdbcTemplate;private Model model; private final String createScript = 'src/main/resources/sql/create-data.sql';@Before public void setUp() { model = new ExtendedModelMap(); SimpleJdbcTestUtils.executeSqlScript(jdbcTemplate, new FileSystemResource(createScript), false); }@Test public void testShowMenu() { String view = controller.showMenu(model); assertEquals('timesheet-service/menu', view); assertEquals(timesheetService.busiestTask(), model.asMap().get('busiestTask'));// this should be done only on small data sample // might cause serious performance cost for complete assertEquals(employeeDao.list(), model.asMap().get('employees')); assertEquals(managerDao.list(), model.asMap().get('managers')); }@Test public void testShowManagerTasks() { // prepare some ID Manager manager = managerDao.list().get(0); long id = manager.getId();String view = controller.showManagerTasks(id, model); assertEquals('timesheet-service/manager-tasks', view); assertEquals(manager, model.asMap().get('manager')); assertEquals(timesheetService.tasksForManager(manager), model.asMap().get('tasks')); }@Test public void testShowEmployeeTasks() { // prepare some ID Employee employee = employeeDao.list().get(0); long id = employee.getId();String view = controller.showEmployeeTasks(id, model); assertEquals('timesheet-service/employee-tasks', view); assertEquals(employee, model.asMap().get('employee')); assertEquals(timesheetService.tasksForEmployee(employee), model.asMap().get('tasks')); } } Project structure after this part (all new stuff is visible):Final request mappings:Reference: Part 5 – Adding Spring MVC part 2 from our JCG partner Michal Vrtiak at the vrtoonjava blog....
spring-logo

Spring – Adding Spring MVC – part 1

Welcome to the fourth part of this tutorial. In this part, we will write controllers and views using Spring MVC and think about our REST model. First thing that we must do, is make a web application from what we have so far. We will add web/WEB-INF folder to our project root. Inside WEB-INF create jsp folder. We will put our JSPs in that place. Inside that folder we will put deployment descriptor web.xml file with following contents: <?xml version='1.0' encoding='UTF-8'?> <web-app xmlns='http://java.sun.com/xml/ns/javaee' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd'version='3.0'><display-name>timesheet-app</display-name><context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:persistence-beans.xml </param-value> </context-param><listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener><servlet> <servlet-name>timesheet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet><servlet-mapping> <servlet-name>timesheet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app> Note that we are using servlet called timesheet. This is a dispatcher servlet. The following picture illustrates how Spring’s dispatcher servlet works (it’s called Front controller on picture below):Request is handled by dispatcher servlet Dispatcher servlet decides to which controller it should deliver request (by request mapping, we’ll see that later) and then it delegates request Controller creates model and delivers it back to dispatcher servlet Dispatcher servlet resolves logical name of the view, binds model there and renders the viewThe last step is quite mysterious. How does dispatcher servlet resolve logical name of the view? It uses something called ViewResolver. But we are not going to create our own by hand, instead we will just create another configuration file, define a bean with ViewResolver and have it injected by Spring. In WEB-INF create another Spring configuration file. By convention it must be named timesheet-servlet.xml, because we named our DispatcherServlet “timesheet” and this is file name, where Spring will be looking for config by default. Also create package org.timesheet.web. This is where we will be putting our controllers (which are again only annotated POJOs). Here’s the timesheet-servlet.xml <?xml version='1.0' encoding='UTF-8'?> <beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:context='http://www.springframework.org/schema/context' xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd'><context:component-scan base-package='org.timesheet.web' /><bean class='org.springframework.web.servlet.view.InternalResourceViewResolver'> <property name='prefix' value='/WEB-INF/jsp/' /> <property name='suffix' value='.jsp' /> </bean> </beans> We defined prefix and suffix for resolving logical names. It is really simple. We calculate name like this: full name = prefix + logical name + suffix Therefore with our InternalResourceViewResolver, logical name “index” would be resolved to “/WEB-INF/jsp/index.jsp”. For views we will be using JSP technology with JSTL (tag library), so we need to add another dependencies to our pom.xml file: <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> </dependency> Now, we’d like to handle GET on /timesheet-app/welcome. So we need to write view and controller (for Model we’ll use one from Spring facilities). Let’s start with controller: package org.timesheet.web;import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod;import java.util.Date;@Controller @RequestMapping('/welcome') public class WelcomeController {@RequestMapping(method = RequestMethod.GET) public String showMenu(Model model) { model.addAttribute('today', new Date()); return 'index'; }} So when someone accesses url welcome (in our case http://localhost:8080/timesheet-app/welcome), this controller will handle request. We are also using Model and binding there value with name “today”. This is how we get value to view page. Note that root of my application is /timesheet-app. It’s called application context. You can change that of course, but all remaining codes asume that you’re application context is set like that. If you’re deploying WAR it’s based on WAR’s name. From showMenu method we’re returning “index” – which will be resolved to WEB-INF/jsp/index.jsp so let’s create such a page and put there some basic content: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %><html> <head> <title>Welcome to Timesheet app!</title> </head> <body> <h1>Welcome to the Timesheet App!</h1><ul> <li><a href='managers'>List managers</a></li> <li><a href='employees'>List employees</a></li> <li><a href='tasks'>List tasks</a></li> <li><a href='timesheets'>List timesheets</a></li> </ul><h2>Also check out <a href='timesheet-service'>extra services!</a></h2> Today is: <fmt:formatDate value='${today}' pattern='dd-MM-yyyy' /> </body> </html> OK to recap, we added web.xml file, timesheet-servlet.xml Spring configuration file, controller class and jsp page. Let’s try to run this on some web container. I’ll be using Tomcat7, but if you feel more comfortable with another web container or even application server – feel free to switch. Now there are plenty of ways how to run Tomcat and how to deploy the application. You can:Use embedded Tomcat with Maven plugin Run Tomcat directly from IntelliJ Run Tomcat directly from Eclipse/STSWhichever you choose, be sure that you can access URL mentioned above before continuing. Honestly, deployment in Java is least fun for me, so don’t give up if you feel frustrated, it might not work for the first time. But with tutorials above, you probably won’t have any problems. Also don’t forget about setting right application context. Before we write more controllers, let’s prepare some data. When Spring creates welcomeController bean, we’d like to have some data. So for now, let’s just write dummy generator that will just create some entites. Later in tutorial, we will see some more realistic solution. Put helpers package under web package were controllers are place there EntityGenerator class: package org.timesheet.web.helpers;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.domain.Timesheet; import org.timesheet.service.GenericDao; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao; import org.timesheet.service.dao.TaskDao; import org.timesheet.service.dao.TimesheetDao;import java.util.List;/** * Small util helper for generating entities to simulate real system. */ @Service public final class EntityGenerator {@Autowired private EmployeeDao employeeDao;@Autowired private ManagerDao managerDao;@Autowired private TaskDao taskDao;@Autowired private TimesheetDao timesheetDao;public void generateDomain() { Employee steve = new Employee('Steve', 'Design'); Employee bill = new Employee('Bill', 'Marketing'); Employee linus = new Employee('Linus', 'Programming'); // free employees (no tasks/timesheets) Employee john = new Employee('John', 'Beatles'); Employee george = new Employee('George', 'Beatles'); Employee ringo = new Employee('Ringo', 'Beatles'); Employee paul = new Employee('Paul', 'Beatles');Manager eric = new Manager('Eric'); Manager larry = new Manager('Larry'); // free managers Manager simon = new Manager('Simon'); Manager garfunkel = new Manager('Garfunkel');addAll(employeeDao, steve, bill, linus, john, george, ringo, paul); addAll(managerDao, eric, larry, simon, garfunkel);Task springTask = new Task('Migration to Spring 3.1', eric, steve, linus); Task tomcatTask = new Task('Optimizing Tomcat', eric, bill); Task centosTask = new Task('Deploying to CentOS', larry, linus);addAll(taskDao, springTask, tomcatTask, centosTask);Timesheet linusOnSpring = new Timesheet(linus, springTask, 42); Timesheet billOnTomcat = new Timesheet(bill, tomcatTask, 30);addAll(timesheetDao, linusOnSpring, billOnTomcat); } public void deleteDomain() { List<Timesheet> timesheets = timesheetDao.list(); for (Timesheet timesheet : timesheets) { timesheetDao.remove(timesheet); }List<Task> tasks = taskDao.list(); for (Task task : tasks) { taskDao.remove(task); }List<Manager> managers = managerDao.list(); for (Manager manager : managers) { managerDao.remove(manager); }List<Employee> employees = employeeDao.list(); for (Employee employee : employees) { employeeDao.remove(employee); } } private <T> void addAll(GenericDao<T, Long> dao, T... entites) { for (T o : entites) { dao.add(o); } } } Now let’s use the code for WelcomeController. We’ll inject there generator and place special method annotated with @PostConstruct annotation. That’s JSR-250 annotation for bean lifecycle and Spring supports it. It means, that this method will be invoked right after Spring IoC container instantiates welcomeController bean. package org.timesheet.web;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.timesheet.web.helpers.EntityGenerator;import javax.annotation.PostConstruct; import java.util.Date;@Controller @RequestMapping('/welcome') public class WelcomeController {@Autowired private EntityGenerator entityGenerator;@RequestMapping(method = RequestMethod.GET) public String showMenu(Model model) { model.addAttribute('today', new Date()); return 'index'; }@PostConstruct public void prepareFakeDomain() { entityGenerator.deleteDomain(); entityGenerator.generateDomain(); } } OK let’s write some controllers for domain logic now! We’ll start by writing Employee’s controller. First, create class EmployeeController under org.timesheet.web package. Mark class as web controller and to handle “/employees” requests: @Controller @RequestMapping('/employees') public class EmployeeController { ... For processing persistent data (Employees in this case) we need DAO and have it autowired by Spring’s IoC container, so let’s do just that: private EmployeeDao employeeDao;@Autowired public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } Now we want to handle HTTP GET method. When user accesses http://localhost:8080/timesheet-app/employees with a web browser, controller must handlet GET request. It’s only contacting DAO and collecting all employees and putting them into the model. @RequestMapping(method = RequestMethod.GET) public String showEmployees(Model model) { List<Employee> employees = employeeDao.list(); model.addAttribute('employees', employees);return 'employees/list'; } Under jsp folder create employees folder where we will put all corresponding JSPs of employees. Probably you already noticed, that page with list of employees will be resolved to /WEB-INF/jsp/employees/list.jsp. So create such a page. We’ll see the contents later, if you want you can put there random text for now to see if it works. In JSP page we will show a link next to employee for his personal page, which will look like http://localhost:8080/timesheet-app/employees/{id} where ID is employee’s id. This is RESTful URL because it’s resource oriented, and we’re directly identifying resource. RESTless URL would be something like http://localhost:8080/timesheet-app/employees.html?id=123. That’s action oriented and doesn’t identify resource. Let’s add another method to controller, that handles this URL: @RequestMapping(value = '/{id}', method = RequestMethod.GET) public String getEmployee(@PathVariable('id') long id, Model model) { Employee employee = employeeDao.find(id); model.addAttribute('employee', employee);return 'employees/view'; } Again, create view.jsp page under /jsp/employees folder. From this page, we’d also like to alter employee. We’re simply access same URL but with different web method – POST. That means, we’re giving data from bounded model to update. This method handles employee updating: @RequestMapping(value = '/{id}', method = RequestMethod.POST) public String updateEmployee(@PathVariable('id') long id, Employee employee) { employee.setId(id); employeeDao.update(employee);return 'redirect:/employees'; } In this case, we were accessing employees/{id} with GET or POST method. But what if we want to delete employee? We’ll access same URL but with different method – DELETE. We will use additional business logic in EmployeeDao. If anything goes wrong, we’ll throw exception containing employee that cannot be deleted. So le’ts add controller method for this case: /** * Deletes employee with specified ID * @param id Employee's ID * @return redirects to employees if everything was ok * @throws EmployeeDeleteException When employee cannot be deleted */ @RequestMapping(value = '/{id}', method = RequestMethod.DELETE) public String deleteEmployee(@PathVariable('id') long id) throws EmployeeDeleteException {Employee toDelete = employeeDao.find(id); boolean wasDeleted = employeeDao.removeEmployee(toDelete);if (!wasDeleted) { throw new EmployeeDeleteException(toDelete); }// everything OK, see remaining employees return 'redirect:/employees'; } Notice that we’re returning redirect from that method. The redirect: prefix signals that the request should be redirected to the path that it precedes. Create package org.timesheet.web.exceptions and place following EmployeeDeleteException there: package org.timesheet.web.exceptions;import org.timesheet.domain.Employee;/** * When employee cannot be deleted. */ public class EmployeeDeleteException extends Exception {private Employee employee;public EmployeeDeleteException(Employee employee) { this.employee = employee; }public Employee getEmployee() { return employee; } } Arguably, this exception could directly be thrown from DAO. Now how do we handle it? Spring has special annotation called @ExceptionHandler. We’ll place it in our controller and when specified exception is thrown, method annotated with ExceptionHandler will handle it and resolve correct view: /** * Handles EmployeeDeleteException * @param e Thrown exception with employee that couldn't be deleted * @return binds employee to model and returns employees/delete-error */ @ExceptionHandler(EmployeeDeleteException.class) public ModelAndView handleDeleteException(EmployeeDeleteException e) { ModelMap model = new ModelMap(); model.put('employee', e.getEmployee()); return new ModelAndView('employees/delete-error', model); } Okay, time for JSPs. We will be using some resources (like *.css or *.js) so in your web application root create resources folder. WEB-INF is NOT root, it’s the folder above. So resources and WEB-INF now should be in same level in directory tree. We’ve configured our dispatcher servlet to handle every request (with / url pattern), but we don’t want to have static resources handled by it atm. We’ll resolve that by simply putting mapping for default servlet in our web.xml file: <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping> Under those resource create styles.css file. We’ll put CSS for whole app there right now even though we will use lotsa that stuff later. table, th { margin: 10px; padding: 5px; width: 300px; }.main-table { border: 2px solid green; border-collapse: collapse; }.wide { width: 600px; }.main-table th { background-color: green; color: white; }.main-table td { border: 1px solid green; }th { text-align: left; }h1 { margin: 10px; }a { margin: 10px; }label { display: block; text-align: left; }#list { padding-left: 10px; position: relative; }#list ul { padding: 0; }#list li { list-style: none; margin-bottom: 1em; }.hidden { display: none; }.delete { margin: 0; text-align: center; }.delete-button { border: none; background: url('/timesheet-app/resources/delete.png') no-repeat top left; color: transparent; cursor: pointer; padding: 2px 8px; }.task-table { width: 150px; border: 1px solid #dcdcdc; }.errors { color: #000; background-color: #ffEEEE; border: 3px solid #ff0000; padding: 8px; margin: 16px; } Let’s now create employess/list.jsp page: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><html> <head> <title>Employees</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h1>List of employees</h1> <a href='employees?new'>Add new employee</a> <table cellspacing='5' class='main-table'> <tr> <th>Name</th> <th>Department</th> <th>Details</th> <th>Delete</th> </tr> <c:forEach items='#{employees}' var='emp'> <tr> <td>${emp.name}</td> <td>${emp.department}</td> <td> <a href='employees/${emp.id}'>Go to page</a> </td> <td> <sf:form action='employees/${emp.id}' method='delete' cssClass='delete'> <input type='submit' class='delete-button' value='' /> </sf:form> </td> </tr> </c:forEach> </table><br /> <a href='welcome'>Go back</a> </body> </html> From that page, we’re linking our css under resources (with full name including application context). There’s also link linking to employee’s detail page (view.jsp) that’s resolved from employee’s id. The most interesting part is usage of sf taglib. To stay Web 1.0 friendly, we unfortunatelly cannot directly use DELETE. Up to HTML4 and XHTML1, html forms can only use GET and POST. Workaround is to use hidden field that marks, if the POST actually should be used as DELETE. That’s precisely what Spring does for us for free – just using sf:form prefix. So we’re tunneling DELETE via HTTP POST, but it will be dispatched correctly. To make this work, we must put in our web.xml special Spring’s filter for that: <filter> <filter-name>httpMethodFilter</filter-name> <filter-class> org.springframework.web.filter.HiddenHttpMethodFilter </filter-class> </filter> <filter-mapping> <filter-name>httpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> Even though that JSP is java specific technology that is actually compiled to servlet, we can use it pretty much like any HTML page. We’ve added some CSS and now we add most popular javascript library – jQuery. Go to https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js and download jquery.js file and drop it to your resources folder. We will allow users to update resource with POST, so we will use jQuery for some DOM manipulations – just for the sake of fancyness. You can use pretty much anything you can in common HTML pages. Let’s now create /employees/view.jsp – that’s kinda detail page for employee. <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><html> <head> <title>Employee page</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Employee info</h2> <div id='list'> <sf:form method='post'> <ul> <li> <label for='name'>Name:</label> <input name='name' id='name' value='${employee.name}' disabled='true'/> </li> <li> <label for='department'>Department:</label> <input name='department' id='department' value='${employee.department}' disabled='true' /> </li> <li> <input type='button' value='Unlock' id='unlock' /> <input type='submit' value='Save' id='save' class='hidden' /> </li> </ul> </sf:form> </div><br /><br /> <a href='../employees'>Go Back</a><script src='/timesheet-app/resources/jquery-1.7.1.js'></script> <script> (function() { $('#unlock').on('click', function() { $('#unlock').addClass('hidden');// enable stuff $('#name').removeAttr('disabled'); $('#department').removeAttr('disabled'); $('#save').removeClass('hidden'); }); })(); </script> </body> </html> Inside the page we refer to jQuery file and we have self-invoking annonymous function – after button with id “unlock” is clicked, we hide it, bring forward the submit button and unlock fields so employee can be updated. After Save button is pressed, we’re redirected back to list of employees and this one has been updated. The last functionality that we’re gonna get done for CRUD on Employee is adding. We will handle that by accessing employees with GET and extra prameter that we’ll call new. So URL for adding employee will be: http://localhost:8080/timesheet-app/employees?new Let’s modify our controller for this: @RequestMapping(params = 'new', method = RequestMethod.GET) public String createEmployeeForm(Model model) { model.addAttribute('employee', new Employee()); return 'employees/new'; } That will serve new JSP page – /WEB-INF/jsp/employees/new.jsp: <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %> <html> <head> <title>Add new employee</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Add new Employee</h2> <div id='list'> <sf:form method='post' action='employees'> <ul> <li> <label for='name'>Name:</label> <input name='name' id='name' value='${employee.name}'/> </li> <li> <label for='department'>Department:</label> <input name='department' id='department' value='${employee.department}' /> </li> <li> <input type='submit' value='Save' id='save' /> </li> </ul> </sf:form> </div><br /><br /> <a href='employees'>Go Back</a> </body> </html> The page is pretty similar to view.jsp. In real world applications we’d use somerhing like Apache Tiles for reducing redundant code, but now let’s not worry about that. Note that we submit form with “employees” action. Back to our controller, let’s handle employees with POST http method: @RequestMapping(method = RequestMethod.POST) public String addEmployee(Employee employee) { employeeDao.add(employee);return 'redirect:/employees'; } And let’s not forget about error JSP page, when we cannot delete employee, jsp/employees/delete-error.jsp: <html> <head> <title>Cannot delete employee</title> </head> <body> Oops! Resource <a href='${employee.id}'>${employee.name}</a> can not be deleted.<p> Make sure employee doesn't have assigned any task or active timesheet. </p><br /><br /><br /> <a href='../welcome'>Back to main page.</a> </body> </html> That’s it, we have whole CRUD functionality for our employees. Let’s recap the basic steps what we just did:Added EmployeeController class Create resources folder in web root for static content Added mapping for default servlet in web.xml Added styles.css to resources folder Configured POST-DELETE tunneling with filter in web.xml Downloaded jQuery.js and added to our resources folder Added employess/list.jsp page Added employess/view.jsp page Added employess/new.jsp page Added employees/delete-error.jsp pageNow, here’s complete code for the controller: package org.timesheet.web;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import org.timesheet.domain.Employee; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.web.exceptions.EmployeeDeleteException;import java.util.List;/** * Controller for handling Employees. */ @Controller @RequestMapping('/employees') public class EmployeeController {private EmployeeDao employeeDao;@Autowired public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; }public EmployeeDao getEmployeeDao() { return employeeDao; }/** * Retrieves employees, puts them in the model and returns corresponding view * @param model Model to put employees to * @return employees/list */ @RequestMapping(method = RequestMethod.GET) public String showEmployees(Model model) { List<Employee> employees = employeeDao.list(); model.addAttribute('employees', employees);return 'employees/list'; }/** * Deletes employee with specified ID * @param id Employee's ID * @return redirects to employees if everything was ok * @throws EmployeeDeleteException When employee cannot be deleted */ @RequestMapping(value = '/{id}', method = RequestMethod.DELETE) public String deleteEmployee(@PathVariable('id') long id) throws EmployeeDeleteException {Employee toDelete = employeeDao.find(id); boolean wasDeleted = employeeDao.removeEmployee(toDelete);if (!wasDeleted) { throw new EmployeeDeleteException(toDelete); }// everything OK, see remaining employees return 'redirect:/employees'; }/** * Handles EmployeeDeleteException * @param e Thrown exception with employee that couldn't be deleted * @return binds employee to model and returns employees/delete-error */ @ExceptionHandler(EmployeeDeleteException.class) public ModelAndView handleDeleteException(EmployeeDeleteException e) { ModelMap model = new ModelMap(); model.put('employee', e.getEmployee()); return new ModelAndView('employees/delete-error', model); }/** * Returns employee with specified ID * @param id Employee's ID * @param model Model to put employee to * @return employees/view */ @RequestMapping(value = '/{id}', method = RequestMethod.GET) public String getEmployee(@PathVariable('id') long id, Model model) { Employee employee = employeeDao.find(id); model.addAttribute('employee', employee);return 'employees/view'; }/** * Updates employee with specified ID * @param id Employee's ID * @param employee Employee to update (bounded from HTML form) * @return redirects to employees */ @RequestMapping(value = '/{id}', method = RequestMethod.POST) public String updateEmployee(@PathVariable('id') long id, Employee employee) { employee.setId(id); employeeDao.update(employee);return 'redirect:/employees'; }/** * Creates form for new employee * @param model Model to bind to HTML form * @return employees/new */ @RequestMapping(params = 'new', method = RequestMethod.GET) public String createEmployeeForm(Model model) { model.addAttribute('employee', new Employee()); return 'employees/new'; }/** * Saves new employee to the database * @param employee Employee to save * @return redirects to employees */ @RequestMapping(method = RequestMethod.POST) public String addEmployee(Employee employee) { employeeDao.add(employee);return 'redirect:/employees'; } } If you’re using SpringSource Tool Suite, you can check mappings directly in IDE. Add to your project “Spring Project Nature”, in Properties->Spring->Bean Support configure your Spring’s config file. Then right click project and press Spring Tools->Show Request Mappings and you should see something like this:Last thing about employees that remains is writing JUnit test. Since we have our timesheet-servlet.xml in WEB-INF, we can’t access its beans in JUnit test. What we’ll do is remove following line from timesheet-servlet.xml: <context:component-scan base-package='org.timesheet.web' />We now create new Spring’s bean config in src/main/resources and call it controllers.xml. The only thing we care about is to put autoscanning for controllers here, so content is pretty simple: <?xml version='1.0' encoding='UTF-8'?> <beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:context='http://www.springframework.org/schema/context' xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd'><context:component-scan base-package='org.timesheet.web' /></beans> To make context aware of those spring beans, alter context-param in web.xml like this: <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:persistence-beans.xml classpath:controllers.xml </param-value> </context-param> Also, we now must import beans from controllers.xml to timesheet-servlet.xml, so instead of removed line <context:component-scan … from timesheet-servlet.xml, we add following: <import resource='classpath:controllers.xml' /> That’ll allow us to have controllers autowired to tests. Okay, so in the test source folder create package org.timesheet.web and let’s put there EmployeeControllerTest. It’s pretty straightforward and we only test controller as POJO and how does it affect persistence layer (verifying via DAO). We made one exception however. In method testDeleteEmployeeThrowsException, we will explicitelly tell DAO to return false when trying to delete employee. This will save us complicated object creation and injection of additional DAOs. We will use popular mocking framework for this – Mockito. Add dependency to your pom.xml: <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.0</version> </dependency> Test for EmployeeController: package org.timesheet.web;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.web.servlet.ModelAndView; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.web.exceptions.EmployeeDeleteException;import java.util.Collection; import java.util.List;import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;@ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class EmployeeControllerTest extends DomainAwareBase { @Autowired private EmployeeDao employeeDao;@Autowired private EmployeeController controller; private Model model; // used for controller @Before public void setUp() { model = new ExtendedModelMap(); }@After public void cleanUp() { List<Employee> employees = employeeDao.list(); for (Employee employee : employees) { employeeDao.remove(employee); } }@Test public void testShowEmployees() { // prepare some data Employee employee = new Employee('Lucky', 'Strike'); employeeDao.add(employee);// use controller String view = controller.showEmployees(model); assertEquals('employees/list', view);List<Employee> listFromDao = employeeDao.list(); Collection<?> listFromModel = (Collection<?>) model.asMap().get('employees');assertTrue(listFromModel.contains(employee)); assertTrue(listFromDao.containsAll(listFromModel)); } @Test public void testDeleteEmployeeOk() throws EmployeeDeleteException { // prepare ID to delete Employee john = new Employee('John Lennon', 'Singing'); employeeDao.add(john); long id = john.getId();// delete & assert String view = controller.deleteEmployee(id); assertEquals('redirect:/employees', view); assertNull(employeeDao.find(id)); }@Test(expected = EmployeeDeleteException.class) public void testDeleteEmployeeThrowsException() throws EmployeeDeleteException { // prepare ID to delete Employee john = new Employee('John Lennon', 'Singing'); employeeDao.add(john); long id = john.getId();// mock DAO for this call EmployeeDao mockedDao = mock(EmployeeDao.class); when(mockedDao.removeEmployee(john)).thenReturn(false);EmployeeDao originalDao = controller.getEmployeeDao(); try { // delete & expect exception controller.setEmployeeDao(mockedDao); controller.deleteEmployee(id); } finally { controller.setEmployeeDao(originalDao); } }@Test public void testHandleDeleteException() { Employee john = new Employee('John Lennon', 'Singing'); EmployeeDeleteException e = new EmployeeDeleteException(john); ModelAndView modelAndView = controller.handleDeleteException(e);assertEquals('employees/delete-error', modelAndView.getViewName()); assertTrue(modelAndView.getModelMap().containsValue(john)); } @Test public void testGetEmployee() { // prepare employee Employee george = new Employee('George Harrison', 'Singing'); employeeDao.add(george); long id = george.getId(); // get & assert String view = controller.getEmployee(id, model); assertEquals('employees/view', view); assertEquals(george, model.asMap().get('employee')); }@Test public void testUpdateEmployee() { // prepare employee Employee ringo = new Employee('Ringo Starr', 'Singing'); employeeDao.add(ringo); long id = ringo.getId();// user alters Employee in HTML form ringo.setDepartment('Drums');// update & assert String view = controller.updateEmployee(id, ringo); assertEquals('redirect:/employees', view); assertEquals('Drums', employeeDao.find(id).getDepartment()); }@Test public void testAddEmployee() { // prepare employee Employee paul = new Employee('Paul McCartney', 'Singing'); // save but via controller String view = controller.addEmployee(paul); assertEquals('redirect:/employees', view);// employee is stored in DB assertEquals(paul, employeeDao.find(paul.getId())); } }Notice how do we use mocked dao for setting it in try/finally block. It’s just for that one call to ensure that correct exception is thrown. If you’ve never seen mocking, I definitelly suggest learning more about this technique. There are plenty of mocking frameworks. The one we picked – Mockito – comes with really neat syntax that heavily uses java static imports. Now, Managers are pretty similar to Employees, so without any big problems, let’s add pretty similar stuff for managers: First, create managers folder in WEB-INF/jsp. Now let’s write controller and inject corresponding DAO: @Controller @RequestMapping('/managers') public class ManagerController {private ManagerDao managerDao;@Autowired public void setManagerDao(ManagerDao managerDao) { this.managerDao = managerDao; }public ManagerDao getManagerDao() { return managerDao; } } Add method for listing managers: /** * Retrieves managers, puts them in the model and returns corresponding view * @param model Model to put employees to * @return managers/list */ @RequestMapping(method = RequestMethod.GET) public String showManagers(Model model) { List<Manager> employees = managerDao.list(); model.addAttribute('managers', employees);return 'managers/list'; } Add list.jsp to jsp/managers: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> <%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %> <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><html> <head> <title>Managers</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h1>List of managers</h1> <a href='managers?new'>Add new manager</a> <table cellspacing='5' class='main-table'> <tr> <th>Name</th> <th>Details</th> <th>Delete</th> </tr> <c:forEach items='#{managers}' var='man'> <tr> <td>${man.name}</td> <td> <a href='managers/${man.id}'>Go to page</a> </td> <td> <sf:form action='managers/${man.id}' method='delete' cssClass='delete'> <input type='submit' value='' class='delete-button' /> </sf:form> </td> </tr> </c:forEach> </table><br /> <a href='welcome'>Go back</a> </body> </html> Add method for deleting managers: /** * Deletes manager with specified ID * @param id Manager's ID * @return redirects to managers if everything was OK * @throws ManagerDeleteException When manager cannot be deleted */ @RequestMapping(value = '/{id}', method = RequestMethod.DELETE) public String deleteManager(@PathVariable('id') long id) throws ManagerDeleteException {Manager toDelete = managerDao.find(id); boolean wasDeleted = managerDao.removeManager(toDelete);if (!wasDeleted) { throw new ManagerDeleteException(toDelete); }// everything OK, see remaining managers return 'redirect:/managers'; } Exception when deleting fails: package org.timesheet.web.exceptions;import org.timesheet.domain.Manager;/** * When manager cannot be deleted */ public class ManagerDeleteException extends Exception {private Manager manager;public ManagerDeleteException(Manager manager) { this.manager = manager; }public Manager getManager() { return manager; } } Method for handling this exception: /** * Handles ManagerDeleteException * @param e Thrown exception with manager that couldn't be deleted * @return binds manager to model and returns managers/delete-error */ @ExceptionHandler(ManagerDeleteException.class) public ModelAndView handleDeleteException(ManagerDeleteException e) { ModelMap model = new ModelMap(); model.put('manager', e.getManager()); return new ModelAndView('managers/delete-error', model); } Add method for getting manager’s page: /** * Returns manager with specified ID * @param id Managers's ID * @param model Model to put manager to * @return managers/view */ @RequestMapping(value = '/{id}', method = RequestMethod.GET) public String getManager(@PathVariable('id') long id, Model model) { Manager manager = managerDao.find(id); model.addAttribute('manager', manager);return 'managers/view'; } Add manager’s page view.jsp under jsp/managers: <%@ page contentType='text/html;charset=UTF-8' language='java' %> <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form'%><html> <head> <title>Manager page</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Manager info</h2> <div id='list'> <sf:form method='post'> <ul> <li> <label for='name'>Name:</label> <input name='name' id='name' value='${manager.name}' disabled='true'/> </li> <li> <input type='button' value='Unlock' id='unlock' /> <input type='submit' value='Save' id='save' class='hidden' /> </li> </ul> </sf:form> </div><br /><br /> <a href='../managers'>Go Back</a><script src='/timesheet-app/resources/jquery-1.7.1.js'></script> <script> (function() { $('#unlock').on('click', function() { $('#unlock').addClass('hidden');// enable stuff $('#name').removeAttr('disabled'); $('#save').removeClass('hidden'); }); })(); </script> </body> </html> JSP page for handling error on deleting: <html> <head> <title>Cannot delete manager</title> </head> <body> Oops! Resource <a href='${manager.id}'>${manager.name}</a> can not be deleted.<p> Make sure manager doesn't have assigned any task or active timesheet. </p><br /><br /><br /> <a href='../welcome'>Back to main page.</a> </body> </html> Add method for updating manager: /** * Updates manager with specified ID * @param id Manager's ID * @param manager Manager to update (bounded from HTML form) * @return redirects to managers */ @RequestMapping(value = '/{id}', method = RequestMethod.POST) public String updateManager(@PathVariable('id') long id, Manager manager) { manager.setId(id); managerDao.update(manager);return 'redirect:/managers'; } Add method for returning new manager’s form: /** * Creates form for new manager * @param model Model to bind to HTML form * @return manager/new */ @RequestMapping(params = 'new', method = RequestMethod.GET) public String createManagerForm(Model model) { model.addAttribute('manager', new Manager()); return 'managers/new'; } Add page for new manager new.jsp under jsp/managers: <%@ taglib prefix='sf' uri='http://www.springframework.org/tags/form' %> <%@ page contentType='text/html;charset=UTF-8' language='java' %> <html> <head> <title>Add new manager</title> <link rel='stylesheet' href='/timesheet-app/resources/style.css' type='text/css'> </head> <body> <h2>Add new Manager</h2> <div id='list'> <sf:form method='post' action='managers'> <ul> <li> <label for='name'>Name:</label> <input name='name' id='name' value='${manager.name}'/> </li> <li> <input type='submit' value='Save' id='save' /> </li> </ul> </sf:form> </div><br /><br /> <a href='managers'>Go Back</a> </body> </html> And finally, add method for adding manager: /** * Saves new manager to the database * @param manager Manager to save * @return redirects to managers */ @RequestMapping(method = RequestMethod.POST) public String addManager(Manager manager) { managerDao.add(manager);return 'redirect:/managers'; } Ok last piece of code for this part is test case for ManagerController: package org.timesheet.web;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.web.servlet.ModelAndView; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Manager; import org.timesheet.service.dao.ManagerDao; import org.timesheet.web.exceptions.ManagerDeleteException;import java.util.Collection; import java.util.List;import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;@ContextConfiguration(locations = {'/persistence-beans.xml', '/controllers.xml'}) public class ManagerControllerTest extends DomainAwareBase { @Autowired private ManagerDao managerDao;@Autowired private ManagerController controller; private Model model; // used for controller @Before public void setUp() { model = new ExtendedModelMap(); }@After public void cleanUp() { List<Manager> managers = managerDao.list(); for (Manager manager : managers) { managerDao.remove(manager); } }@Test public void testShowManagers() { // prepare some data Manager manager = new Manager('Bob Dylan'); managerDao.add(manager);// use controller String view = controller.showManagers(model); assertEquals('managers/list', view);List<Manager> listFromDao = managerDao.list(); Collection<?> listFromModel = (Collection<?>) model.asMap().get('managers');assertTrue(listFromModel.contains(manager)); assertTrue(listFromDao.containsAll(listFromModel)); } @Test public void testDeleteManagerOk() throws ManagerDeleteException { // prepare ID to delete Manager john = new Manager('John Lennon'); managerDao.add(john); long id = john.getId();// delete & assert String view = controller.deleteManager(id); assertEquals('redirect:/managers', view); assertNull(managerDao.find(id)); }@Test(expected = ManagerDeleteException.class) public void testDeleteManagerThrowsException() throws ManagerDeleteException { // prepare ID to delete Manager john = new Manager('John Lennon'); managerDao.add(john); long id = john.getId();// mock DAO for this call ManagerDao mockedDao = mock(ManagerDao.class); when(mockedDao.removeManager(john)).thenReturn(false);ManagerDao originalDao = controller.getManagerDao(); try { // delete & expect exception controller.setManagerDao(mockedDao); controller.deleteManager(id); } finally { controller.setManagerDao(originalDao); } }@Test public void testHandleDeleteException() { Manager john = new Manager('John Lennon'); ManagerDeleteException e = new ManagerDeleteException(john); ModelAndView modelAndView = controller.handleDeleteException(e);assertEquals('managers/delete-error', modelAndView.getViewName()); assertTrue(modelAndView.getModelMap().containsValue(john)); }@Test public void testGetManager() { // prepare manager Manager george = new Manager('George Harrison'); managerDao.add(george); long id = george.getId(); // get & assert String view = controller.getManager(id, model); assertEquals('managers/view', view); assertEquals(george, model.asMap().get('manager')); }@Test public void testUpdateManager() { // prepare manager Manager ringo = new Manager('Ringo Starr'); managerDao.add(ringo); long id = ringo.getId();// user alters manager in HTML form ringo.setName('Rango Starr');// update & assert String view = controller.updateManager(id, ringo); assertEquals('redirect:/managers', view); assertEquals('Rango Starr', managerDao.find(id).getName()); }@Test public void testAddManager() { // prepare manager Manager paul = new Manager('Paul McCartney'); // save but via controller String view = controller.addManager(paul); assertEquals('redirect:/managers', view);// manager is stored in DB assertEquals(paul, managerDao.find(paul.getId())); } } Request mappings looks like this now:So in this part, we learned what is Spring MVC, how to use our entities as models, how to write controllers in POJO style, how RESTful design looks like, how to create views with JSPs and how to setup application for using CSS and JavaScript. We wrote controllers for Employees and Managers. In the next part we’ll continue with writing controllers for Tasks and Timesheets. Make sure everything works well so far before you proceed to the next part. Here’s src folder (only new stuff is expanded. Don’t worry about .iml files, they’re for IntelliJ):Here’s web folder:Reference: Part 4 – Adding Spring MVC – part 1 from our JCG partner Michal Vrtiak at the vrtoonjava blog....
spring-logo

Spring – DAO and Service layer

Welcome to the third part of Spring tutorial. In this part, we will continue in writing our Timesheet application and this time we’ll implement DAO layer, business services and write some tests. In the previous part, we’ve defined GenericDao interface that tells us, what operations we will need to perform upon entities. Now we need to provide implementation. We will write class that performs these operations generically with Hibernate’s facilities (using SessionFactory). Therefore, any provided DAO automatically inherits these basic operations. We’ll talk about this later. package org.timesheet.service.impl;import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.timesheet.service.GenericDao;import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.util.List;/** * Basic DAO operations dependent with Hibernate's specific classes * @see SessionFactory */ @Transactional(propagation= Propagation.REQUIRED, readOnly=false) public class HibernateDao<E, K extends Serializable> implements GenericDao<E, K> {private SessionFactory sessionFactory; protected Class<? extends E> daoType;public HibernateDao() { daoType = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; }@Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }protected Session currentSession() { return sessionFactory.getCurrentSession(); }@Override public void add(E entity) { currentSession().save(entity); }@Override public void update(E entity) { currentSession().saveOrUpdate(entity); }@Override public void remove(E entity) { currentSession().delete(entity); }@Override public E find(K key) { return (E) currentSession().get(daoType, key); }@Override public List<E> list() { return currentSession().createCriteria(daoType).list(); } } I want you to note couple of things about this code:We’re using @Transcational annotation at the top of the class. That basically means, that DAO methods will run within transcations. To make it work, we need to alter our persistence-beans.xml file and declare there transaction manager, which will be handling the transactions. Just add following lines (new bean definition): <bean id='transactionManager' class='org.springframework.orm.hibernate3.HibernateTransactionManager'> <property name='sessionFactory' ref='sessionFactory' /> </bean>We’re autowiring (@Autowired) SessionFactory using setter injection. As you should know, there are more kinds of injection (field, setter, constructor). Field injection looks best with Spring, because annotation resides directly at field, not on constructor or setter method. On the other hand, field injection is most useless one, because we cannot manually set other dependencies to private fields (for example in unit test). I prefer constructor injection whenever I can, because I don’t have to use mutator (setter) for the dependency. Objects are therefore constructed in more safe way. In this specific case we will use setter injection, because we’re designing this class for extension. If we’d pick constructor injection, all extending classes would have to have constructor matching one from the superclass. If you want to learn more about this, I recommend this brilliant book written by Dhanji R. Prasanna. Also note, that first line of constructor is doing some reflective magic. That’s because Java doesn’t have generics at runtime, only at compile time, so it prevents us from writing something like E.class. Therefore we used this ugly hack.Now we have some basic template for DAO operations. In the real systems, there usually is DAO for each entity. That’s because sometimes those inherited CRUD operations are not enough, and you need some additional business operations. We will define interfaces (sets of operations for each DAO) that are typesafe and we will only depend on those later in controllers. We will implement them with Hibernate and have them autowired. Create new package org.timesheet.service.dao and add there following interfaces – DAOs for each entity: package org.timesheet.service.dao;import org.timesheet.domain.Employee; import org.timesheet.service.GenericDao;/** * DAO of employee. */ public interface EmployeeDao extends GenericDao<Employee, Long> {/** * Tries to remove employee from the system. * @param employee Employee to remove * @return {@code true} if employee is not assigned to any task * or timesheet. Else {@code false}. */ boolean removeEmployee(Employee employee);} package org.timesheet.service.dao;import org.timesheet.domain.Manager; import org.timesheet.service.GenericDao;/** * DAO of Manager. */ public interface ManagerDao extends GenericDao<Manager, Long> { /** * Tries to remove manager from the system. * @param manager Manager to remove * @return {@code true} if manager is not assigned to any task. * Else {@code false}. */ boolean removeManager(Manager manager); } package org.timesheet.service.dao;import org.timesheet.domain.Task; import org.timesheet.service.GenericDao;/** * DAO of Task. */ public interface TaskDao extends GenericDao<Task, Long> {/** * Tries to remove task from the system. * @param task Task to remove * @return {@code true} if there is no timesheet created on task. * Else {@code false}. */ boolean removeTask(Task task);} package org.timesheet.service.dao;import org.timesheet.domain.Timesheet; import org.timesheet.service.GenericDao;/** * DAO of Timesheet. */ public interface TimesheetDao extends GenericDao<Timesheet, Long> { // no additional business operations atm } Time for implementation. We will just extend HibernateDao and implement coresponding interface. We need those concrete classes, because this will be injected to the corresponding fields (which are declared by interface). Maybe you’ve heared something about this approach – it’s called programming to interfaces and it is something you definitelly want to embrace. package org.timesheet.service.impl;import org.hibernate.Query; import org.springframework.stereotype.Repository; import org.timesheet.domain.Employee; import org.timesheet.service.dao.EmployeeDao;@Repository('employeeDao') public class EmployeeDaoImpl extends HibernateDao<Employee, Long> implements EmployeeDao {@Override public boolean removeEmployee(Employee employee) { Query employeeTaskQuery = currentSession().createQuery( 'from Task t where :id in elements(t.assignedEmployees)'); employeeTaskQuery.setParameter('id', employee.getId());// employee mustn't be assigned on no task if (!employeeTaskQuery.list().isEmpty()) { return false; }Query employeeTimesheetQuery = currentSession().createQuery( 'from Timesheet t where t.who.id = :id'); employeeTimesheetQuery.setParameter('id', employee.getId());// employee mustn't be assigned to any timesheet if (!employeeTimesheetQuery.list().isEmpty()) { return false; }// ok, remove as usual remove(employee); return true;} } package org.timesheet.service.impl;import org.hibernate.Query; import org.springframework.stereotype.Repository; import org.timesheet.domain.Manager; import org.timesheet.service.dao.ManagerDao;@Repository('managerDao') public class ManagerDaoImpl extends HibernateDao<Manager, Long> implements ManagerDao {@Override public boolean removeManager(Manager manager) { Query managerQuery = currentSession().createQuery( 'from Task t where t.manager.id = :id'); managerQuery.setParameter('id', manager.getId());// manager mustn't be assigned on no task if (!managerQuery.list().isEmpty()) { return false; }// ok, remove as usual remove(manager); return true; } } package org.timesheet.service.impl;import org.hibernate.Criteria; import org.hibernate.Query; import org.springframework.stereotype.Repository; import org.timesheet.domain.Task; import org.timesheet.domain.Timesheet; import org.timesheet.service.dao.TaskDao;import java.util.ArrayList; import java.util.HashSet; import java.util.List;@Repository('taskDao') public class TaskDaoImpl extends HibernateDao<Task, Long> implements TaskDao {@Override public boolean removeTask(Task task) { Query taskQuery = currentSession().createQuery( 'from Timesheet t where t.task.id = :id'); taskQuery.setParameter('id', task.getId());// task mustn't be assigned to no timesheet if (!taskQuery.list().isEmpty()) { return false; }// ok, remove as usual remove(task); return true; }@Override public List<Task> list() { return currentSession().createCriteria(Task.class) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) .list(); } } package org.timesheet.service.impl;import org.hibernate.Criteria; import org.springframework.stereotype.Repository; import org.timesheet.domain.Timesheet; import org.timesheet.service.dao.TimesheetDao;import java.util.List;@Repository('timesheetDao') public class TimesheetDaoImpl extends HibernateDao<Timesheet, Long> implements TimesheetDao {@Override public List<Timesheet> list() { return currentSession().createCriteria(Timesheet.class) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) .list(); } } I want you to note that we have Spring’s @Repository annotation on every DAO class. That’s because we won’t create them by hand, but have them injected and managed by Spring’s IoC container. By the way – this is the newer annotation oriented approach. No XML configuration, Spring will figure it out for us We can use plenty of annotations that register classes as beans:@Component – autoscan component (Spring bean) @Repository – component in persistence layer (usually DAO) @Service – component in service layer @Controller – controller in MVC architectureAnother interseting thing is that we pass string value to @Repository annotation. I’ll quote Spring’s javadoc here, since it’s clearest explanation: “The value may indicate a suggestion for a logical component name, to be turned into a Spring bean in case of an autodetected component.” Now – time for testing! Create new package: /src/test/java/org/timesheet/service/dao and put tests there. We will be using some external SQL scripts for verifying database status. Under src/main/resources create folder sql. Now let’s add two scripts; cleanup.sql and create-data.sql. We’ll use now only cleanup.sql script, create-data.sql will be used later. create-data.sql -- delete old data delete from task_employee; delete from timesheet; delete from task; delete from employee; delete from manager;-- add few employees insert into employee values(1, 'management', 'Steve Jobs'); insert into employee values(2, 'management', 'Bill Gates'); insert into employee values(3, 'engineering', 'Steve Wozniak'); insert into employee values(4, 'engineering', 'Paul Allen');-- add few managers insert into manager values(1, 'Eric Schmidt'); insert into manager values(2, 'Steve Ballmer');-- add some tasks insert into task values(1, 0, 'task 1', 1); insert into task values(2, 0, 'task 2', 2);-- connect tasks to some employees insert into task_employee values (1, 1); insert into task_employee values (1, 3); insert into task_employee values (1, 4); insert into task_employee values (2, 2); insert into task_employee values (2, 1);-- create some timesheets on tasks insert into timesheet values(1, 5, -- hours 1, -- first task 1 -- employee steve jobs );insert into timesheet values(2, 8, -- hours 2, -- second task 3 -- employee bill gates ); cleanup.sql delete from task_employee; delete from timesheet; delete from task; delete from employee; delete from manager; You don’t have to use my data; feel free to create some on your own. Just make sure they make somehow sense to you. Before we write test we need new Spring bean. It’s called jdbcTemplate and it’s well known facility for working with JDBC in Spring. It’s basically wrapper upon plain JDBC that simplifies lot of things. Thanks to this we can run script with simple call as you’ll see later. For now, add this bean to your persistence-beans.xml Spring Config file: <bean id='jdbcTemplate' class='org.springframework.jdbc.core.simple.SimpleJdbcTemplate'> <constructor-arg type='javax.sql.DataSource' ref='dataSource'/> </bean>I won’t be paying any special attention to every test, so let’s only briefly talk about what to test and how. We’re testing our DAOs and we need to make sure that basic CRUD operations work properly. We’re cleaning all the data after each test method and if necessary, we’re creating them before the test method runs. Basic idea of our tests is like so:If something was added, check if can find that If something was removed, check that we can’t find that anymore Add couple of items to database, count them and verify they’ve been added Update item, save it. Find it and check that it has been changedI like to think of those tests like integration tests more like unit tests. In huge domain similar tests would require (unlike plain unit tests) quite large amount of time to run. This time we will create special base class called org.timesheet.DomainAwareBase. This extends AbstractJUnit4SpringContextTests so we can have our DAOs autowired, but it also deletes all data from database before any test method is executed using deleteScript. package org.timesheet;import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; import org.springframework.test.jdbc.SimpleJdbcTestUtils;/** * Base makes sure that before any test empty database is available. */ @ContextConfiguration(locations = {'/persistence-beans.xml'}) public abstract class DomainAwareBase extends AbstractJUnit4SpringContextTests {private final String deleteScript = 'src/main/resources/sql/cleanup.sql';@Autowired private SimpleJdbcTemplate jdbcTemplate;@Before public void deleteAllDomainEntities() { SimpleJdbcTestUtils.executeSqlScript(jdbcTemplate, new FileSystemResource(deleteScript), false); } } About autowiring and tooling: For me, tooling is specially important when autowiring beans. It’s little hard to navigate through code withou any additional support. For example if you’re using ultimate edition of IntelliJ IDEA you can navigate directly from field to autowired dependency because IntelliJ will add little marker.Or you can also see autowired depencies together with those declared in XML in dependencies view.Let’s see the code for tests now: package org.timesheet.service.dao;import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.domain.Timesheet;import java.util.Arrays; import java.util.List;import static org.junit.Assert.*;@ContextConfiguration(locations = '/persistence-beans.xml') public class EmployeeDaoTest extends DomainAwareBase {@Autowired private EmployeeDao employeeDao;@Autowired private ManagerDao managerDao;@Autowired private TaskDao taskDao;@Autowired private TimesheetDao timesheetDao;@Test public void testAdd() { int size = employeeDao.list().size(); employeeDao.add(new Employee('test-employee', 'hackzorz'));// list should have one more employee now assertTrue (size < employeeDao.list().size()); } @Test public void testUpdate() { Employee employee = new Employee('test-employee', 'hackzorz'); employeeDao.add(employee); employee.setName('updated');employeeDao.update(employee); Employee found = employeeDao.find(employee.getId()); assertEquals('updated', found.getName()); }@Test public void testFind() { Employee employee = new Employee('test-employee', 'hackzorz'); employeeDao.add(employee);Employee found = employeeDao.find(employee.getId()); assertEquals(found, employee); }@Test public void testList() { assertEquals(0, employeeDao.list().size()); List<Employee> employees = Arrays.asList( new Employee('test-1', 'testers'), new Employee('test-2', 'testers'), new Employee('test-3', 'testers')); for (Employee employee : employees) { employeeDao.add(employee); }List<Employee> found = employeeDao.list(); assertEquals(3, found.size()); for (Employee employee : found) { assertTrue(employees.contains(employee)); } }@Test public void testRemove() { Employee employee = new Employee('test-employee', 'hackzorz'); employeeDao.add(employee);// successfully added assertEquals(employee, employeeDao.find(employee.getId()));// try to remove employeeDao.remove(employee); assertNull(employeeDao.find(employee.getId())); }@Test public void testRemoveEmployee() { Manager manager = new Manager('task-manager'); managerDao.add(manager);Employee employee = new Employee('Jaromir', 'Hockey'); employeeDao.add(employee);Task task = new Task('test-task', manager, employee); taskDao.add(task);Timesheet timesheet = new Timesheet(employee, task, 100); timesheetDao.add(timesheet);// try to remove -> shouldn't work assertFalse(employeeDao.removeEmployee(employee));// remove stuff timesheetDao.remove(timesheet); taskDao.remove(task);// should work -> employee is now free assertTrue(employeeDao.removeEmployee(employee)); }} package org.timesheet.service.dao;import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task;import java.util.Arrays; import java.util.List;import static org.junit.Assert.*;@ContextConfiguration(locations = '/persistence-beans.xml') public class ManagerDaoTest extends DomainAwareBase { @Autowired private ManagerDao managerDao;@Autowired private EmployeeDao employeeDao;@Autowired private TaskDao taskDao; @Test public void testAdd() { int size = managerDao.list().size(); managerDao.add(new Manager('test-manager'));assertTrue (size < managerDao.list().size()); }@Test public void testUpdate() { Manager manager = new Manager('test-manager'); managerDao.add(manager); manager.setName('updated');managerDao.update(manager); Manager found = managerDao.find(manager.getId()); assertEquals('updated', found.getName()); }@Test public void testFind() { Manager manager = new Manager('test-manager'); managerDao.add(manager);Manager found = managerDao.find(manager.getId()); assertEquals(found, manager); } @Test public void testList() { assertEquals(0, managerDao.list().size()); List<Manager> managers = Arrays.asList( new Manager('test-1'), new Manager('test-2'), new Manager('test-3') ); for (Manager manager : managers) { managerDao.add(manager); }List<Manager> found = managerDao.list(); assertEquals(3, found.size()); for (Manager manager : found) { assertTrue(managers.contains(manager)); } } @Test public void testRemove() { Manager manager = new Manager('test-manager'); managerDao.add(manager); // successfully added assertEquals(manager, managerDao.find(manager.getId())); // try to remove managerDao.remove(manager); assertNull(managerDao.find(manager.getId())); }@Test public void testRemoveManager() { Manager manager = new Manager('task-manager'); managerDao.add(manager);Employee employee = new Employee('Jaromir', 'Hockey'); employeeDao.add(employee);Task task = new Task('test-task', manager, employee); taskDao.add(task);// try to remove -> shouldn't work assertFalse(managerDao.removeManager(manager));// remove task taskDao.remove(task);// should work -> no more tasks for manager assertTrue(managerDao.removeManager(manager)); } } package org.timesheet.service.dao;import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task;import java.util.Arrays; import java.util.List;import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue;@ContextConfiguration(locations = '/persistence-beans.xml') public class TaskDaoTest extends DomainAwareBase { @Autowired private TaskDao taskDao;@Autowired private ManagerDao managerDao;@Autowired private EmployeeDao employeeDao;@Test public void testAdd() { int size = taskDao.list().size();Task task = newSpringTask(); taskDao.add(task);assertTrue(size < taskDao.list().size()); }@Test public void testUpdate() { Task task = newSpringTask(); taskDao.add(task); // update task task.setDescription('Learn Spring 3.1'); taskDao.update(task);Task found = taskDao.find(task.getId()); assertEquals('Learn Spring 3.1', found.getDescription()); }@Test public void testFind() { Task task = newSpringTask(); taskDao.add(task); assertEquals(task, taskDao.find(task.getId())); } @Test public void testList() { assertEquals(0, taskDao.list().size()); Task templateTask = newSpringTask(); List<Task> tasks = Arrays.asList( newTaskFromTemplate(templateTask, '1'), newTaskFromTemplate(templateTask, '2'), newTaskFromTemplate(templateTask, '3') ); for (Task task : tasks) { taskDao.add(task); }List<Task> found = taskDao.list(); assertEquals(3, found.size()); for (Task task : found) { assertTrue(tasks.contains(task)); } } @Test public void testRemove() { Task task = newSpringTask(); taskDao.add(task); // successfully added assertEquals(task, taskDao.find(task.getId())); // try to remove taskDao.remove(task); assertNull(taskDao.find(task.getId())); }/** * @return Dummy task for testing */ private Task newSpringTask() { Manager bob = new Manager('Bob'); managerDao.add(bob);Employee steve = new Employee('Steve', 'Business'); Employee woz = new Employee('Woz', 'Engineering'); employeeDao.add(steve); employeeDao.add(woz);return new Task('Learn Spring', bob, steve, woz); }/** * Creates dummy task fo testing as copy of existing task and * adds aditional information to every field. * @param templateTask Task to copy * @param randomInfo Info to append everywhere * @return Random task for testing */ private Task newTaskFromTemplate(Task templateTask, String randomInfo) { String description = templateTask.getDescription() + randomInfo; Manager manager = new Manager( templateTask.getManager().getName()); managerDao.add(manager);List<Employee> templateEmployees = templateTask.getAssignedEmployees(); Employee[] employees = new Employee[templateEmployees.size()];int idx = 0; for (Employee templateEmployee : templateEmployees) { Employee employee = new Employee( templateEmployee.getName() + randomInfo, templateEmployee.getDepartment() + randomInfo); employees[idx++] = employee; employeeDao.add(employee); }return new Task(description, manager, employees); } } package org.timesheet.service.dao;import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.timesheet.DomainAwareBase; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.domain.Timesheet;import java.util.Arrays; import java.util.List;import static org.junit.Assert.*;@ContextConfiguration(locations = '/persistence-beans.xml') public class TimesheetDaoTest extends DomainAwareBase {@Autowired private TimesheetDao timesheetDao;// daos needed for integration test of timesheetDao @Autowired private TaskDao taskDao;@Autowired private EmployeeDao employeeDao;@Autowired private ManagerDao managerDao;// common fields for timesheet creation private Task task; private Employee employee;@Override public void deleteAllDomainEntities() { super.deleteAllDomainEntities(); setUp(); }public void setUp() { employee = new Employee('Steve', 'Engineering'); employeeDao.add(employee);Manager manager = new Manager('Bob'); managerDao.add(manager);task = new Task('Learn Spring', manager, employee); taskDao.add(task); }@Test public void testAdd() { int size = timesheetDao.list().size();Timesheet timesheet = newTimesheet(); timesheetDao.add(timesheet);assertTrue (size < timesheetDao.list().size()); }@Test public void testUpdate() { Timesheet timesheet = newTimesheet(); timesheetDao.add(timesheet);// update timesheet timesheet.setHours(6); taskDao.update(timesheet.getTask()); timesheetDao.update(timesheet);Timesheet found = timesheetDao.find(timesheet.getId()); assertTrue(6 == found.getHours()); }@Test public void testFind() { Timesheet timesheet = newTimesheet(); timesheetDao.add(timesheet);assertEquals(timesheet, timesheetDao.find(timesheet.getId())); }@Test public void testList() { assertEquals(0, timesheetDao.list().size()); Timesheet templateTimesheet = newTimesheet(); List<Timesheet> timesheets = Arrays.asList( newTimesheetFromTemplate(templateTimesheet, 4), newTimesheetFromTemplate(templateTimesheet, 7), newTimesheetFromTemplate(templateTimesheet, 10) ); for (Timesheet timesheet : timesheets) { timesheetDao.add(timesheet); }List<Timesheet> found = timesheetDao.list(); assertEquals(3, found.size()); for (Timesheet timesheet : found) { assertTrue (timesheets.contains(timesheet)); } }@Test public void testRemove() { Timesheet timesheet = newTimesheet(); timesheetDao.add(timesheet); // successfully added assertEquals(timesheet, timesheetDao.find(timesheet.getId())); // try to remoce timesheetDao.remove(timesheet); assertNull (timesheetDao.find(timesheet.getId())); }/** * @return Dummy timesheet for testing */ private Timesheet newTimesheet() { return new Timesheet(employee, task, 5); }private Timesheet newTimesheetFromTemplate(Timesheet template, Integer hours) { return new Timesheet( template.getWho(), template.getTask(), hours ); } } You can run your tests as individual classes or alltogether from your IDE, or you can run them as “test” goal from Maven like so (switch to project directory): $ mvn test Tests are pretty much similar so if you can understand at least one of them, you’re probably fine just to copy-paste them to your own project. If you care to spend little more time, feel free to write them on your own and do little experimentation to get Hibernate know a little better. As for DAOs, we’re pretty much done. One thing left though – our TimesheetService interface. That’s the set of business operations that we’re intersted in, so let’s implement it using Hibernate. We’ll put TimesheetServiceImpl class under org.timesheet.service.impl package: package org.timesheet.service.impl;import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.service.TimesheetService; import org.timesheet.service.dao.TaskDao;import java.util.ArrayList; import java.util.List; import java.util.Random;@Transactional(propagation= Propagation.REQUIRED, readOnly=false) @Service('timesheetService') public class TimesheetServiceImpl implements TimesheetService {// dependencies private SessionFactory sessionFactory; private TaskDao taskDao;private Random random = new Random();@Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }@Autowired public void setTaskDao(TaskDao taskDao) { this.taskDao = taskDao; }public SessionFactory getSessionFactory() { return sessionFactory; }public TaskDao getTaskDao() { return taskDao; }private Session currentSession() { return sessionFactory.getCurrentSession(); }@Override public Task busiestTask() { List<Task> tasks = taskDao.list(); if (tasks.isEmpty()) { return null; } Task busiest = tasks.get(0); for (Task task : tasks) { if (task.getAssignedEmployees().size() > busiest.getAssignedEmployees().size()) { busiest = task; } } return busiest; }@Override public List<Task> tasksForEmployee(Employee employee) { List<Task> allTasks = taskDao.list(); List<Task> tasksForEmployee = new ArrayList<Task>(); for (Task task : allTasks) { if (task.getAssignedEmployees().contains(employee)) { tasksForEmployee.add(task); } }return tasksForEmployee; }@Override public List<Task> tasksForManager(Manager manager) { Query query = currentSession() .createQuery('from Task t where t.manager.id = :id'); query.setParameter('id', manager.getId()); return query.list(); } }Note that we use @Service annotation this time (we’ve talked about these before). Also we’re injecting some DAOs with setter injection. Some business method aren’t implemented most effectively, but we’re demonstrating that we can either mix generic DAO logic or create our own queries using HQL. We could have picked Criteria API, it doesn’t really matter now. The biggest downside about HQL is that it’s plain strings so it’s not refactoring friendly – unless you use proper tooling. For example, IntelliJ has autocompletion even for plain strings. It just figures out that you’re writing HQL. Pretty useful is also HQL console, IntelliJ has one and there’s plugin for Eclipse. IntelliJ highlighting & autocompletion for HQL:Now we should test this service. This time we don’t want to create instances of entities in Java, we’ll use external SQL scripts we created before – for setting up and cleaning the data. Let’s put test class TimesheetServiceTest in package org.timesheet.service in src/test/java folder. In the following code, note how we’re using jdbcTemplate bean: package org.timesheet.service;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; import org.springframework.test.jdbc.SimpleJdbcTestUtils; import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task; import org.timesheet.service.dao.EmployeeDao; import org.timesheet.service.dao.ManagerDao;import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue;@ContextConfiguration(locations = '/persistence-beans.xml') public class TimesheetServiceTest extends AbstractJUnit4SpringContextTests { @Autowired private TimesheetService timesheetService;// resources for accessing data during the testing @Autowired private SimpleJdbcTemplate jdbcTemplate; @Autowired private EmployeeDao employeeDao; @Autowired private ManagerDao managerDao;private final String createScript = 'src/main/resources/sql/create-data.sql'; private final String deleteScript = 'src/main/resources/sql/cleanup.sql';@Before public void insertData() { SimpleJdbcTestUtils.executeSqlScript(jdbcTemplate, new FileSystemResource(createScript), false); }@After public void cleanUp() { SimpleJdbcTestUtils.executeSqlScript(jdbcTemplate, new FileSystemResource(deleteScript), false); }@Test public void testBusiestTask() { Task task = timesheetService.busiestTask(); assertTrue(1 == task.getId()); } @Test public void testTasksForEmployees() { Employee steve = employeeDao.find(1L); Employee bill = employeeDao.find(2L); assertEquals(2, timesheetService.tasksForEmployee(steve).size()); assertEquals(1, timesheetService.tasksForEmployee(bill).size()); } @Test public void testTasksForManagers() { Manager eric = managerDao.find(1L); assertEquals(1, timesheetService.tasksForManager(eric).size()); } }Allright, that’s it. We’ve implemented DAO and service layer. This included quite lot of code, so before you continue make sure that you’re project structure looks like this:Reference: Part 3 – DAO and Service layer from our JCG partner Michal Vrtiak at the vrtoonjava blog....
spring-logo

Spring – Persistence layer – writing entities and configuring Hibernate

Welcome to the second part of this tutorial. Don’t freak out when you see how long this article is – I promise you it’s mostly easy POJOs and some generated code. Before we start, we need to update our Maven dependencies, because we will be using Hibernate and Spring now. Add following dependencies to your pom.xml: <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.6.8.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!-- spring framework --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.0.RELEASE</version> </dependency> If you’re new to Maven you might be wondering now – how do you know these? Where can I get them? Well, just go to http://mvnrepository.com/ and type what your ale looking for. You will get complete code for maven dependencies. If you have ever tried to assemble Spring or Hibernate application yourself without using Maven, you probably know how painful it was. With Maven things are so much easier. Also note, that we have included dependency to MySQL’s connector. If you’ve decided to use other database, don’t forget to change this. With Hibernate we have 2 options how to turn our POJOs to entites. Either we use XML and create mapping files, or we will put some meta information to our code (java annotations). Some people are afraid of those and consider this as coupling with framework. It is true that you will need javax.persistence annotations at your classpath, but we won’t be implementing interfaces or extending framework classes. We will just add some meta information to our code and POJOs will still be simply POJOs with some extra information. We will turn our POJOs to entities now. We will need to do following changes:Add default no-args constructor for Hibernate Create getters and setters for fields add equals and hashCode methods. Add persistence annotations. Note that we also use @Table annotation to distinguish between Java and SQL naming conventions. Add id fields. These will be primary keys in our relational database.This is quite lot of boilerplate code, so let your IDE help you. Most modern IDEs will generate construcors, getters, setters, equals and hashCode for you. package org.timesheet.domain;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table;@Entity @Table(name = 'employee') public class Employee {@Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String name; private String department; public Employee() { }public Employee(String name, String department) { this.name = name; this.department = department; } public String getName() { return name; } public String getDepartment() { return department; } public Long getId() { return id; } public void setId(Long id) { this.id = id; }public void setName(String name) { this.name = name; }public void setDepartment(String department) { this.department = department; }@Override public String toString() { return 'Employee [id=' + id + ', name=' + name + ', department=' + department + ']'; }@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((department == null) ? 0 : department.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Employee)) { return false; } Employee other = (Employee) obj; if (department == null) { if (other.department != null) { return false; } } else if (!department.equals(other.department)) { return false; } if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } } package org.timesheet.domain;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table;@Entity @Table(name = 'manager') public class Manager { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String name; public Manager() { }public Manager(String name) { this.name = name; } public String getName() { return name; }public Long getId() { return id; }public void setId(Long id) { this.id = id; }public void setName(String name) { this.name = name; }@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Manager)) { return false; } Manager other = (Manager) obj; if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; }@Override public String toString() { return 'Manager [id=' + id + ', name=' + name + ']'; } } package org.timesheet.domain;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table;@Entity @Table(name='timesheet') public class Timesheet {@Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @OneToOne @JoinColumn(name = 'employee_id') private Employee who; @OneToOne @JoinColumn(name = 'task_id') private Task task; private Integer hours; public Timesheet() { } public Timesheet(Employee who, Task task, Integer hours) { this.who = who; this.task = task; this.hours = hours; }public Employee getWho() { return who; }public Task getTask() { return task; } public Integer getHours() { return hours; } public Long getId() { return id; }public void setId(Long id) { this.id = id; }public void setWho(Employee who) { this.who = who; }public void setTask(Task task) { this.task = task; }public void setHours(Integer hours) { this.hours = hours; }/** * Manager can alter hours before closing task * @param hours New amount of hours */ public void alterHours(Integer hours) { this.hours = hours; }@Override public String toString() { return 'Timesheet [id=' + id + ', who=' + who + ', task=' + task + ', hours=' + hours + ']'; }@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((hours == null) ? 0 : hours.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((task == null) ? 0 : task.hashCode()); result = prime * result + ((who == null) ? 0 : who.hashCode()); return result; }@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Timesheet)) { return false; } Timesheet other = (Timesheet) obj; if (hours == null) { if (other.hours != null) { return false; } } else if (!hours.equals(other.hours)) { return false; } if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (task == null) { if (other.task != null) { return false; } } else if (!task.equals(other.task)) { return false; } if (who == null) { if (other.who != null) { return false; } } else if (!who.equals(other.who)) { return false; } return true; } } And finally, here’s Task entity when we needed to use also @ManyToMany mapping. That’s because one employee can work on multiple tasks and one task can have assigned multiple employees. We’ve defined how our m:n will look like, using @JoinTable and @JoinColumn annotations. package org.timesheet.domain;import javax.persistence.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List;@Entity @Table(name = 'task') public class Task {@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;@ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = 'task_employee', joinColumns = {@JoinColumn(name = 'task_id')}, inverseJoinColumns = {@JoinColumn(name = 'employee_id')} ) private List<Employee> assignedEmployees = new ArrayList<Employee>();@OneToOne @JoinColumn(name = 'manager_id') private Manager manager;private String description; boolean completed;public Task() { }public Task(String description, Manager manager, Employee... employees) { this.description = description; this.manager = manager; assignedEmployees.addAll(Arrays.asList(employees)); completed = false; }public Manager getManager() { return manager; }public List<Employee> getAssignedEmployees() { return assignedEmployees; }public void addEmployee(Employee e) { assignedEmployees.add(e); }public void removeEmployee(Employee e) { assignedEmployees.remove(e); }public Long getId() { return id; }public void setId(Long id) { this.id = id; }public boolean isCompleted() { return completed; }public void setCompleted(boolean completed) { this.completed = completed; }public void setAssignedEmployees(List<Employee> assignedEmployees) { this.assignedEmployees = assignedEmployees; }public void setManager(Manager manager) { this.manager = manager; }public String getDescription() { return description; }public void setDescription(String description) { this.description = description; }@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; }Task task = (Task) o;if (completed != task.completed) { return false; } if (description != null ? !description.equals(task.description) : task.description != null) { return false; } if (id != null ? !id.equals(task.id) : task.id != null) { return false; } if (manager != null ? !manager.equals(task.manager) : task.manager != null) { return false; }return true; }@Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (manager != null ? manager.hashCode() : 0); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + (completed ? 1 : 0); return result; }@Override public String toString() { return 'Task{' + 'id=' + id + ', assignedEmployees=' + assignedEmployees + ', manager=' + manager + ', description='' + description + '\'' + ', completed=' + completed + '}'; } } So we didn’t really do anything special to model. If you fancy some UML look at the following picture, relations are same as before.Okay we have entites, now let’s create database. Pick some tool for database management (even plain terminal is fine) and create timesheet database like so (by default mysql will install to /usr/local/mysql/bin/mysql at Mac OS X): $ mysql -u root mysql > create database timesheet; If you’ve ever configured Hibernate before you probably know, that you need quite many files and boilerplate code when dealing for example with SessionFactory. These things are much simpler with Spring. We will now create our first Spring Bean Configuration File – it’s file where we register beans for Spring container. If I had to explain what’s this file to someone who doesn’t know what Spring is at all – it’s kind of magic bag where Spring container can find objects. Modern IDEs will help you get all the XML namespaces right, for example you can see pictures from STS wizard. NetBeans has something similar and IntelliJ resolves namespaces on the fly. Name configuration file persistence-beans.xml and we will put it under src/main/resources folder.So setting up Hibernate, transactions, annotation config and so on is as simple as creating few beans in XML file. Alternatively, we can use Java Config for Spring, but XML configs are still used much more, so we’ll stick to those. I don’t want to discourage you from using Java Config though! XML config is much much more popular at this moment, but I can’t guarantee that for the next few years. I’ve commented every bean to make sure you understand what we did there before proceeding. If you want to get some visual grasp of connections between beans you can again use some tooling – in STS it’s called Bean Graph, in IntelliJ it’s Dependencies. You can see sample of dependencies on the picture below.<?xml version='1.0' encoding='UTF-8'?> <beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:context='http://www.springframework.org/schema/context' xmlns:tx='http://www.springframework.org/schema/tx' xsi:schemaLocation='http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.1.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.1.xsd'><!-- we can use annotations --> <context:annotation-config /> <!-- package to look for annotated classes --> <context:component-scan base-package='org.timesheet.service.impl' /> <!-- we will manage transactions with annotations --> <tx:annotation-driven /><!-- data source for our database --> <bean id='dataSource' class='org.springframework.jdbc.datasource.DriverManagerDataSource'> <property name='driverClassName' value='com.mysql.jdbc.jdbc2.optional.MysqlDataSource' /> <property name='url' value='jdbc:mysql://localhost/timesheet' /> <property name='username' value='root' /> <property name='password' value='' /> </bean> <!-- configure hibernate session factory --> <bean id='sessionFactory' class='org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean'> <property name='dataSource' ref='dataSource' /> <property name='annotatedClasses' > <list> <value>org.timesheet.domain.Employee</value> <value>org.timesheet.domain.Manager</value> <value>org.timesheet.domain.Task</value> <value>org.timesheet.domain.Timesheet</value> </list> </property> <property name='hibernateProperties'> <props> <prop key='dialect'>org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key='hibernate.show_sql'>true</prop> <prop key='hibernate.hbm2ddl.auto'>update</prop> </props> </property> </bean> </beans>Okay, that was quite lot of configuration, he? What’s not so good is that we’ve placed names of our entities to XML as plain text, so it isn’t refactoring friendly. But I think for this tutorial it’s acceptable Let’s write integration test for Hibernate so we know that everything is set up properly. package org.timesheet.integration;import static org.junit.Assert.*;import org.hibernate.SessionFactory; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;@ContextConfiguration(locations = '/persistence-beans.xml') public class HibernateConfigurationTest extends AbstractJUnit4SpringContextTests { @Autowired private SessionFactory sessionFactory;@Test public void testHibernateConfiguration() { // Spring IOC container instantiated and prepared sessionFactory assertNotNull (sessionFactory); }} I want you to note 2 things here. First, we extend AbstractJUnit4SpringContextTests class. We tell it where it should look for actual XML config with spring beans. Otherwise we would have to create Spring container by ourselves, which means more boilerplate code. Second, we use @Autowired annotation. That means we don’t create instance of SessionFactory by hand using new operator, but we will have it Autowired (Injected) by Spring container! That’s one of the most important purposes of Spring container – depend on interfaces and have implementations injected instead of creating them by hand. Everything should work now and I think that’s enough for this part. If you like you can check plain SQL, and see tables are here, do it like so: mysql> use timesheet; mysql> show tables; +---------------------+ | Tables_in_timesheet | +---------------------+ | employee | | manager | | task | | task_employee | | timesheet | +---------------------+ 5 rows in set (0.00 sec)Reference: Part 2 – Persistence layer – writing entities and configuring Hibernate from our JCG partner Michal Vrtiak at the vrtoonjava blog....
spring-logo

Spring – Designing the domain model and the service layer

We are going to build application for timesheet management. So let’s think about some uses cases and entities first. Let me write them in the few bullets:Task is assigned to employee by manager. One task can be assigned to many employees. Employee fills the amount of hours that he worked on certain task to the system. Manager/Employee views reports on timesheets (timesheets can be altered).Let’s revisit those points a little and let’s try to transform this plain human language to some relations and entities that programmer can spot.Entities: Manager, Employee, Timesheet, TaskOkay, we should now have better grasp about the domain, so let’s create maven project and implement classes. With Maven you get nice and clean project structure. All you need is installed Maven and having pom.xml in your project. You can either do that “by hand” and building application via terminal (in this case just create regular project and add pom.xml file). I prefer using some additional tooling. IntelliJ IDEA, NetBeans and Springsource Tool Suite have out of the box Maven support. If you’re using plain Eclipse, check m2eclipse plugin. In either way, here is some basic Maven configuration for our project: <project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd'> <modelVersion>4.0.0</modelVersion> <groupId>org.timesheet</groupId> <artifactId>org.timesheet</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Timesheet Management On Spring</name> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project> Now let’s implement domain model. Create package org.timesheet.domain and define following classes. package org.timesheet.domain;public class Employee {private String name; private String department;public Employee(String name, String department) { this.name = name; this.department = department; } public String getName() { return name; } public String getDepartment() { return department; } }package org.timesheet.domain;public class Manager {private String name;public Manager(String name) { this.name = name; } public String getName() { return name; } }package org.timesheet.domain;import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class Task {private List<Employee> assignedEmployees = new ArrayList<Employee>(); private Manager manager; private boolean completed; private String description; public Task(String description, Manager manager, Employee... employees) { this.description = description; this.manager = manager; assignedEmployees.addAll(Arrays.asList(employees)); completed = false; }public Manager getManager() { return manager; } public List<Employee> getAssignedEmployees() { return assignedEmployees; } public void addEmployee(Employee e) { assignedEmployees.add(e); } public void removeEmployee(Employee e) { assignedEmployees.remove(e); } public void completeTask() { completed = true; } }package org.timesheet.domain;public class Timesheet {private Employee who; private Task task; private Integer hours; public Timesheet(Employee who, Task task, Integer hours) { this.who = who; this.task = task; this.hours = hours; }public Employee getWho() { return who; }public Task getTask() { return task; } public Integer getHours() { return hours; } /** * Manager can alter hours before closing task * @param hours New amount of hours */ public void alterHours(Integer hours) { this.hours = hours; }@Override public String toString() { return 'Timesheet [who=' + who + ', task=' + task + ', hours=' + hours + ']'; }} As you can see, Manager and Employee classes don’t have many properties, they’re here just for the sake of having type safe model. In the “real world”, they’d probably have various other properties like surname, birthday, address and so on, maybe even common parent class. Also, we don’t really care about various constraints now. For example, we can only fill integer hours on tasks and so on. Now it’s time to define our service layer – define business operations and establish interface for those. So let’s make package org.timesheet.service. At first, we will create GenericDao interface, where we will define basic CRUD operations for every entity in the system. package org.timesheet.service;import java.util.List;public interface GenericDao<E, K> {void add(E entity); void update(E entity); void remove(E entity); E find(K key); List<E> list(); } For now, let’s not worry about the actual persistence layer – let’s create some dummy implementation and store all the data in memory. We will put it in to the new package – org.timesheet.service.impl. Don’t worry, later we’ll use Hibernate for this. Here is the code for the dummy implementation: package org.timesheet.service.impl;import java.util.ArrayList; import java.util.List;import org.timesheet.service.GenericDao;public class InMemoryDao<E, K> implements GenericDao<E, K> { private List<E> entities = new ArrayList<E>();@Override public void add(E entity) { entities.add(entity); }@Override public void update(E entity) { throw new UnsupportedOperationException('Not supported in dummy in-memory impl!'); }@Override public void remove(E entity) { entities.remove(entity); }@Override public E find(K key) { if (entities.isEmpty()) { return null; } // just return the first one sice we are not using any keys ATM return entities.get(0); }@Override public List<E> list() { return entities; }} Next, we will write our first simple test. We will now add our first dependency into pom.xml file to JUnit library. Because it’s the first one, we also need to wrap it into dependencies element like so: <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> Here’s our first very simple unit test for Employee DAO. We won’t do others now, since we don’t really have anything to test yet. What’s more important though, is how we depend on implementation of DAO in the test (we use new InMemoryDao…). This is bad, because we should only test public API of defined interface. Later in this tutorial, you will see how Spring helps us to solve such problems. package org.timesheet.service;import static org.junit.Assert.*;import java.util.List;import org.junit.Before; import org.junit.Test; import org.timesheet.domain.Employee; import org.timesheet.service.impl.InMemoryDao;public class EmployeeDaoTest { private GenericDao<Employee, Long> employeeDao = new InMemoryDao<Employee, Long>(); @Before public void setUp() { for (int i = 0; i < 5; i++) { Employee e = new Employee('Mike ' + i, 'IT'); employeeDao.add(e); } } @Test public void testAdd() { int oldSize = employeeDao.list().size(); Employee e = new Employee('Bob', 'IT'); employeeDao.add(e); int newSize = employeeDao.list().size(); assertFalse (oldSize == newSize); } @Test public void testRemove() { int oldSize = employeeDao.list().size(); Employee e = employeeDao.find(1L); employeeDao.remove(e); int newSize = employeeDao.list().size(); assertFalse (oldSize == newSize); } @Test public void testUpdate() { //TODO: need real implementation } @Test public void testList() { List<Employee> list = employeeDao.list(); assertNotNull (list); assertFalse (list.isEmpty()); }} If you want, you can also write unit tests for remaining tests for the other DAOs. But since we don’t have proper implementation to test now, we’ll do it later together. Things are not always so easy though. It’s not only about CRUD operations, it’s also about business operations that are not generic enough to be expressed in simple DAOs. So let’s define few business operations and create separate service for them. We’ll call this service TimesheetService. package org.timesheet.service;import org.timesheet.domain.Employee; import org.timesheet.domain.Manager; import org.timesheet.domain.Task;import java.util.List;/** * Business that defines operations on timesheets */ public interface TimesheetService { /** * @return Finds the busiest task (with the most of employees). * Returns {@code null} when tasks are empty. */ Task busiestTask(); /** * Finds all the tasks for the employee. * @param e Employee * @return Tasks */ List<Task> tasksForEmployee(Employee e); /** * Finds all the tasks for the manager. * @param m Manager * @return Tasks */ List<Task> tasksForManager(Manager m); } Okay, so far so good. You have now idea what’s the business domain we will be using in the next examples. You might be wondering now – we haven’t use any Spring yet, why? Remember that Spring’s original purpose is to simplify enterprise java development and encourage POJO development model. So it will be very easy to use Spring with this basic model, so we won’t have our core logic mixed with unnecessary dependencies. On the picture below there is structure of the project we’ve built so far, so make sure you’re good.Reference: Part 1 – Designing the domain model and the service layer from our JCG partner Michal Vrtiak at the vrtoonjava blog....
powermock-logo

Using PowerMock to Mock Constructors

In my opinion, one of the main benefits of dependency injection is that you can inject mock and/or stub objects into your code in order to improve testability, increase test coverage and write better and more meaningful tests. There are those times, however, when you come across some legacy code that doesn’t use dependency injection and held together by composition rather than aggregation. When this happens, you have three options:Ignore the problem and not write any tests. Refactor like mad, changing everything to use dependency injection. Use PowerMock to mock constructorsObviously, option 1 isn’t a serious option, and although I’d recommend refactoring to move everything over to dependency injection, that takes time and you have to be pragmatic. That’s where PowerMock comes in… this blog demonstrates how to use PowerMock to mock a constructor, which means that when your code calls new it doesn’t create a real object, it creates a mock object. To demonstrate this idea, the first thing we need is some classes to test, which are shown below. public class AnyOldClass { public String someMethod() { return "someMethod"; } }public class UsesNewToInstantiateClass {public String createThing() {AnyOldClass myclass = new AnyOldClass();String returnValue = myclass.someMethod(); return returnValue; } }The first class, AnyOldClass, is the class that the code instantiates by calling new. In this example, as the name suggests, it can be anything. The second class, the aptly named UsesNewToInstantiateClass, has one method, createThing(), which when called does a: AnyOldClass myclass = new AnyOldClass();This is all pretty straight forward, so we’ll move quickly on to the PowerMock assisted JUnit test: import static org.easymock.EasyMock.expect; import static org.junit.Assert.assertEquals; import static org.powermock.api.easymock.PowerMock.expectNew; import static org.powermock.api.easymock.PowerMock.replay; import static org.powermock.api.easymock.PowerMock.verify;import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.annotation.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner;@RunWith(PowerMockRunner.class) @PrepareForTest(UsesNewToInstantiateClass.class) public class MockConstructorTest {@Mock private AnyOldClass anyClass;private UsesNewToInstantiateClass instance;@Test public final void testMockConstructor() throws Exception {instance = new UsesNewToInstantiateClass();expectNew(AnyOldClass.class).andReturn(anyClass);final String expected = "MY_OTHER_RESULT"; expect(anyClass.someMethod()).andReturn(expected);replay(AnyOldClass.class, anyClass); String result = instance.createThing(); verify(AnyOldClass.class, anyClass); assertEquals(expected, result); }}Firstly, this class has the usual PowerMock additions of: @RunWith(PowerMockRunner.class) @PrepareForTest(UsesNewToInstantiateClass.class)at the top of the file plus the creation of the anyOldClass mock object. The important line of code to consider is: expectNew(AnyOldClass.class).andReturn(anyClass);This line of code tells PowerMock to expect a call to new AnyOldClass() and return our anyClass mock object. Also of interest are the calls to replay and verify. In the example above, they both have two arguments. The first, AnyOldClass.class relates to the expectNew(…) call above, whilst the second, anyClass refers to the straight forward mock call expect(anyClass.someMethod()).andReturn(expected);. There are those times when you should really let new do what it does: create a new object of the requested type. There is a body of opinion that says you can over-isolate your code when testing and that mocking everything reduces the meaning and value of a test. To me there’s no right answer to this and it’s a matter of choice. It’s fairly obvious that if your code accesses an external resource such as a database, then you’d either refactor and implement DI or use PowerMock. If your code under test doesn’t access any external resources, then it’s more of a judgement call on how much code isolation is too much? This perhaps needs some thought and may be the subject for another blog on anther day… Reference: Using PowerMock to Mock Constructors from our JCG partner Roger Hughes at “Captain Debug’s” Blog....
apache-camel-logo

Camel: Build a message based application

This is a long article that contains three separate topics:Getting started with Apache Camel with Java Improving startup of routes with a CamelRunner Building message based application using CamelBut since I’ve prepared a camel-demo-1.0.0-SNAPSHOT-project.zip that has all these materials included, I thought it would easier to combine them and present it as whole.Getting started with Apache Camel with Java Trying out Camel with few Groovy lines is one thing, but getting a full scale project in Java is another matter. Today, I will show you how to get things started on Apache Camel with Maven based project. You may also use the provided camel-demo as project template to jump start your own Apache Camel project. You would just need to rename the Java package and rename the pom’s group and artifact id’s to match your need.Preparing a Maven based project with Camel dependencies Unzip the camel-demo project source, and you will see the basic directory layout. camel-demo +- bin +- config +- data +- src +- pom.xml +- README.txt What makes this demo a Camel based project is just the declaration in pom.xml. Let’s take a look the file and its dependencies. <?xml version='1.0' encoding='UTF-8'?> <project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'><modelVersion>4.0.0</modelVersion> <groupId>deng.cameldemo</groupId> <artifactId>camel-demo</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <slf4j.version>1.6.6</slf4j.version> <camel.version>2.10.1</camel.version> </properties><build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.3</version> <configuration> <descriptorRefs> <descriptorRef>project</descriptorRef> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies><!-- Unit testing lib --> <dependency> <groupId>junit</groupId> <artifactId>junit-dep</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>1.2.1</version> <scope>test</scope> </dependency><!-- Logging lib --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> <scope>runtime</scope> <optional>true</optional> </dependency><!-- Apache Commons lib --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.0.1</version> </dependency><!-- Apache Camel --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>${camel.version}</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring</artifactId> <version>${camel.version}</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-groovy</artifactId> <version>${camel.version}</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> <version>${camel.version}</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-mina</artifactId> <version>${camel.version}</version> </dependency></dependencies></project> This pom.xml decalares a Java based application and it will produce jar. It requires minimal of JDK 6 or higher. Besides the typical junit and hamcrest for unit testing, I also added slf4j for logging. I have added couple Apache’s commons-lang/io to the project as well. I think these are basic settings that any Java based application should use them. The maven-assembly-plugin I have declared is only for this demo packging purpose, and you may change or remove to suite your own project need. For Camel dependencies, you would need minimal camel-core for routes building. And then you can add any additional components you plan to use in your project. I have added the following for building typical message based application development:The camel-spring – we want to have option to declare Camel routes in xml files as configuration. See camel-demo/config directory for samples. The camel-jackson – we want to process messaging data in our application as JSON format. The camel-mina – we want to send messaging data accross network through TCP socket. The camel-groovy – [optional] we want to be able to add dynamic scripting to route, even inside the xml config. This is great for debug and POC.Note that since we use multiple camel components dependencies, I choose to set a Maven property ${camel.version} so that when we upgrade Camel, it’s easier to maintain the pom.xml file in one place. You should able to cd into the project directory and run mvn compile to verify that the project. It should compile without error.Improving startup of routes with a CamelRunner With the project pom.xml file ready, you can start creating Camel routes to handle your own business logics. Before we get too excited, let’s try out a simple HelloRoute to see how it works and how we can run it first. Here is the route defnition code in src/main/java/deng/cameldemo/HelloRoute.java. package deng.cameldemo;import org.apache.camel.builder.RouteBuilder;public class HelloRoute extends RouteBuilder { @Override public void configure() throws Exception { from('timer://helloTimer?period=3000'). to('log:' + getClass().getName()); } }Take a test ride with the Camel To see above in action, we need to add it into a CamelContext and start the context. For Java standalone program, we would write this setup code in a Main class. The Camel actually comes with a org.apache.camel.main.MainSupport abstract class that you may use to extend your own Main. However, I think it would be even nicer if Camel would provide a CamelRunner that can run like this. $ java CamelRunner deng.cameldemo.HelloRoute Such CamelRunner would be very user friendly and re-usable to have, so that’s what I did. I wrote one like this: package deng.cameldemo;import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultCamelContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;/** * A main program to start Camel and run as a server using RouteBuilder class names or * Spring config files. * * <p>Usage: * * java deng.cameldemo.CamelRunner deng.cameldemo.HelloRoute * * or * * java -Dspring=true deng.cameldemo.CamelRunner /path/to/camel-spring.xml * * @author Zemian Deng */ public class CamelRunner { public static void main(String[] args) throws Exception { CamelRunner runner = new CamelRunner(); runner.run(args); }private static Logger logger = LoggerFactory.getLogger(CamelRunner.class); public void run(String[] args) throws Exception { if (Boolean.parseBoolean(System.getProperty('spring', 'false'))) runWithSpringConfig(args); else runWithCamelRoutes(args);// Wait for user to hit CRTL+C to stop the service synchronized(this) { this.wait(); } }private void runWithSpringConfig(String[] args) { final ConfigurableApplicationContext springContext = new FileSystemXmlApplicationContext(args);// Register proper shutdown. Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { springContext.close(); logger.info('Spring stopped.'); } catch (Exception e) { logger.error('Failed to stop Spring.', e); } } });// Start spring logger.info('Spring started.'); }private void runWithCamelRoutes(String[] args) throws Exception { final CamelContext camelContext = new DefaultCamelContext(); // Register proper shutdown. Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { camelContext.stop(); logger.info('Camel stopped for {}', camelContext); } catch (Exception e) { logger.error('Failed to stop Camel.', e); } } });// Added RouteBuilder from args for (String className : args) { Class<?> cls = Class.forName(className); if (RouteBuilder.class.isAssignableFrom(cls)) { Object obj = cls.newInstance(); RouteBuilder routeBuilder = (RouteBuilder)obj; camelContext.addRoutes(routeBuilder); } else { throw new RuntimeException('Unable to add Camel RouteBuilder ' + className); } }// Start camel camelContext.start(); logger.info('Camel started for {}', camelContext); } } To help you run the main class, I have provided a run-java wrapper script under the project’s bin directory, so that you may quickly test it without having to setup classpath. $ mvn package $ bin/run-java deng.cameldemo.CamelRunner deng.cameldemo.HelloRoute You will see that the program will load the HelloRoute in a DefaultCamelContext and start it as a server. The HelloRoute itself will generate a 3 seconds timer message and send it to a logger, which should be printing onto your console screen. This will continue forever until you hit CTRL+C to end it. NOTE: You only have to invoke mvn package command once, so that it will package up all the dependencies jars in order for run-java to auto-detect them. If you are not going to use maven-assembly-plugin during package phase, then use mvn dependency:copy-dependencies command explicitly will work fine as well.Take a test ride with the Camel, Part 2: running Camel with Spring xml configuration The HelloRoute example above would simply provide route definition that formed by using component URI’s. It will be nice if we can configure the route in a declarative manner so that we may change the route without re-compile a class file. This will be very handy especially if you are not familiar with each component’s options and want to explore and try things out. Well, that’s what the camel-spring is for. Beside giving you an option to load route in xml config file, it also provides a very flexible way to register custom services/processors bean in the Spring IoC container. If you are a keen reader, you will notice in the CamelRunner code above that it has an extra runWithSpringConfig part. So the CamelRunner can actually bootstrap any Spring xml file and start a context as a server. You may use it like this: $ bin/run-java deng.cameldemo.CamelRunner -Dspring=true config/hellocamel-spring.xmlThe config/hellocamel-spring.xml is just an equivalent of our HelloRoute code but in Spring xml form: <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.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd'><camelContext id='helloCamel' xmlns='http://camel.apache.org/schema/spring'> <route> <from uri='timer://jdkTimer?period=3000'/> <to uri='log://deng.cameldemo.HelloCamel'/> </route> </camelContext></beans> This remove the need to compile/re-compile HelloRoute to define the Camel route to run.Building message based application using Camel To present you with a more practical demo, I would show you further on how to setup Camel to process message based application. In many IT shops, it’s common that you would have a server to take message data as input and process them. A practical use case is to take any JSON formated message and transform it into object and process it. To do this in Camel, what you want to build is a route that will take input messages from a TCP port, and then process it in a pipeflow with any business logic you may have. You will run the route as a server, and then client may use any mean to submit the message to the TCP port. Client may even be another thin Camel client app to submit data as well. Let me show you how to get started. Writing the server side code with Camel route The server side would need a route to listen from a TCP port, and this is provided by camel-mina component. The first step is you need a route. package deng.cameldemo;import org.apache.camel.builder.RouteBuilder;public class TcpMsgRoute extends RouteBuilder { @Override public void configure() throws Exception { String port = System.getProperty('port', '12345'); from('mina:tcp://localhost:' + port + '?sync=false'). to('log:' + getClass().getName()); } }Then the next step is … done! No way, you mean that’s all there to it for a server? Too good to be true? Well, let’s try it out $ bin/run-java deng.cameldemo.CamelRunner deng.cameldemo.TcpMsgRoute -Dport=12345 15:21:41 main INFO org.apache.camel.impl.DefaultCamelContext:1391 | Apache Camel 2.10.1 (CamelContext: camel-1) is starting 15:21:41 main INFO org.apache.camel.management.ManagementStrategyFactory:43 | JMX enabled. 15:21:42 main INFO org.apache.camel.impl.converter.DefaultTypeConverter:45 | Loaded 172 type converters 15:21:42 main INFO org.apache.camel.component.mina.MinaConsumer:59 | Binding to server address: localhost/127.0.0.1:12345 using acceptor: org.apache.mina.transport.socket.nio.SocketAcceptor@2ffad8fe 15:21:42 main INFO org.apache.camel.impl.DefaultCamelContext:2045 | Route: route1 started and consuming from: Endpoint[mina://tcp://localhost:12345?sync=true] 15:21:42 main INFO org.apache.camel.management.DefaultManagementLifecycleStrategy:859 | StatisticsLevel at All so enabling load performance statistics 15:21:42 main INFO org.apache.camel.impl.DefaultCamelContext:1426 | Total 1 routes, of which 1 is started. 15:21:42 main INFO org.apache.camel.impl.DefaultCamelContext:1427 | Apache Camel 2.10.1 (CamelContext: camel-1) started in 0.505 seconds 15:21:42 main INFO deng.cameldemo.CamelRunner:93 | Camel started for CamelContext(camel-1) Voila! The server is up and waiting for your users to send messages through port 12345. Not too bad for few lines of code.Writing the client side code with Camel ProducerTemplate Since our server expose a TCP port and take in any text content message, you can create any client that’s capable writing to a TCP socket. In here, I will show you how to use Camel to write a thin client. package deng.cameldemo.client;import java.io.FileReader; import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; import org.apache.camel.impl.DefaultCamelContext; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class TcpMsgSender { public static void main(String[] args) throws Exception { TcpMsgSender runner = new TcpMsgSender(); runner.run(args); }private static Logger logger = LoggerFactory.getLogger(TcpMsgSender.class); public void run(String[] args) throws Exception { String fileName = args.length > 0 ? args[0] : 'data/msg.txt'; String[] hostPort = (args.length > 1 ? args[1] : 'localhost:12345').split(':'); String host = hostPort[0]; String port = hostPort.length > 1 ? hostPort[1] : '12345'; logger.info('Sending tcp message {} to host={}, port={}', new Object[]{ fileName, host, port});String text = IOUtils.toString(new FileReader(fileName)); logger.debug('File size={}', text.length());CamelContext camelContext = new DefaultCamelContext(); ProducerTemplate producer = camelContext.createProducerTemplate(); producer.sendBody('mina:tcp://' + host + ':' + port + '?sync=false', text); logger.info('Message sent.'); } } This TcpMsgSender can send any text file to your server endpoint. Try this out while your server is running: $ bin/run-java deng.cameldemo.client.TcpMsgSender data/test-msg.json localhost:12345 15:22:35 main INFO deng.cameldemo.client.TcpMsgSender:24 | Sending tcp message data/test-msg.json to host=localhost, port=12345 15:22:35 main DEBUG deng.cameldemo.client.TcpMsgSender:27 | File size=47 15:22:35 main INFO org.apache.camel.impl.converter.DefaultTypeConverter:45 | Loaded 172 type converters 15:22:35 main INFO org.apache.camel.management.ManagementStrategyFactory:43 | JMX enabled. 15:22:35 main INFO deng.cameldemo.client.TcpMsgSender:32 | Message sent.You should able to verify from your server console output that it received the msg. The msg I sent is in data/test-msg.json, which contains this simple text: { 'firstName' : 'Zemian', 'lastName' : 'Deng' }Note that our server simply receive plain text and log it. We will discuss how to process the message next.Processing message data in JSON format with Camel and Spring xml config You thought the server code was easy from above, guess again. You can actually replace the TcpMsgRoute with just some simple xml lines! <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.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd'><camelContext id='tcpMsgServer' xmlns='http://camel.apache.org/schema/spring'> <route> <from uri='mina:tcp://localhost:12345?sync=false'/> <to uri='log://deng.cameldemo.TcpMsgServer'/> </route> </camelContext></beans>Save it as config/tcpmsgserver-spring.xml. Then re-run the server, and you should get the same result as above. $ bin/run-java deng.cameldemo.CamelRunner -Dspring=true config/tcpmsgserver-spring.xmlNow let us improve the above xml to further process the JSON message data. We will like to transform the plain text to a Java object then process by a custom bean. To do that, we first would need to add unmarshal component to the route. This is where the camel-jackson comes into play. In our demo, the unmarshalling step would convert the JSON text into a java.util.Map and then pass it to a processor bean named myMsgProcessor. Let’s create a new xml file named config/tcpmsgserver-json-spring.xml as follow. <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.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd'><camelContext id='tcpMsgServer' xmlns='http://camel.apache.org/schema/spring'> <route> <from uri='mina:tcp://localhost:12345?sync=false'/> <to uri='log://deng.cameldemo.TcpMsgServer'/> <unmarshal> <json library='Jackson'/> </unmarshal> <to uri='bean:myMsgProcessor?method=process'/> </route> </camelContext><bean id='myMsgProcessor' class='deng.cameldemo.MyMsgProcessor'> </bean></beans>The myMsgProcessor is an Spring bean that we provide custom logic code to process the data. At this point we have a full Java object to manipulate. The content of the processor can be any POJO with the method name specified in the URI. Here is an example one: package deng.cameldemo;import org.apache.camel.builder.RouteBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map;public class MyMsgProcessor { private static Logger logger = LoggerFactory.getLogger(MyMsgProcessor.class); public void process(Map<String, String> data) { logger.info('We should slice and dice the data: ' + data); } } Try re-run the server with the new xml file above, and you should able to re-invoke the same client command to test it out. Here is a sample output of my server: $ bin/run-java deng.cameldemo.CamelRunner -Dspring=true config/tcpmsgserver-json-spring.xml 17:05:25 main INFO org.springframework.context.support.FileSystemXmlApplicationContext:456 | Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@4200309: startup date [Sat Sep 15 17:05:25 EDT 2012]; root of context hierarchy 17:05:25 main INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 | Loading XML bean definitions from file [/Users/zemian/projects/sandbox/camel-demo/config/tcpmsgserver-json-spring.xml] 17:05:27 main INFO org.springframework.beans.factory.support.DefaultListableBeanFactory:557 | Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@27b75165: defining beans [template,consumerTemplate,tcpMsgServer:beanPostProcessor,tcpMsgServer,myMsgProcessor]; root of factory hierarchy 17:05:27 main INFO org.apache.camel.spring.SpringCamelContext:1391 | Apache Camel 2.10.1 (CamelContext: tcpMsgServer) is starting 17:05:27 main INFO org.apache.camel.management.ManagementStrategyFactory:43 | JMX enabled. 17:05:27 main INFO org.apache.camel.impl.converter.DefaultTypeConverter:45 | Loaded 172 type converters 17:05:28 main INFO org.apache.camel.component.mina.MinaConsumer:59 | Binding to server address: localhost/127.0.0.1:12345 using acceptor: org.apache.mina.transport.socket.nio.SocketAcceptor@5a3cae4a 17:05:28 main INFO org.apache.camel.spring.SpringCamelContext:2045 | Route: route1 started and consuming from: Endpoint[mina://tcp://localhost:12345?sync=false] 17:05:28 main INFO org.apache.camel.management.DefaultManagementLifecycleStrategy:859 | StatisticsLevel at All so enabling load performance statistics 17:05:28 main INFO org.apache.camel.spring.SpringCamelContext:1426 | Total 1 routes, of which 1 is started. 17:05:28 main INFO org.apache.camel.spring.SpringCamelContext:1427 | Apache Camel 2.10.1 (CamelContext: tcpMsgServer) started in 0.695 seconds 17:05:28 main INFO deng.cameldemo.CamelRunner:61 | Spring started. 17:05:35 Camel (tcpMsgServer) thread #3 - MinaThreadPool INFO deng.cameldemo.TcpMsgServer:96 | Exchange[ExchangePattern:InOnly, BodyType:String, Body:{ 'firstName' : 'Zemian', 'lastName' : 'Deng' }] 17:05:35 Camel (tcpMsgServer) thread #3 - MinaThreadPool INFO deng.cameldemo.MyMsgProcessor:11 | We should slice and dice the data: {lastName=Deng, firstName=Zemian} Pay attention that Camel will auto convert the data format in your route! Our client only sends the plain text as JSON format, but when server receives it, it unmarshals it using Jackson library, and then converts it into a java Map object. It then passes the map object into our processor bean. Also, in this demo, I choose to use a generic java.util.Map as processor method argument (which is output of the JSON unmarshal), but you can easily define your own business data type, such as MyCustomerData. This reveals the power of Camel, since you don’t need to push the message in your flow, but only worry about writing your ‘processor’ as a POJO. The Camel will ‘glue’ components together to form a route and carry the message data through the pipeline flow. On the same token, when you write your business logic in one or more processors, it’s a good idea that you limit your POJO logic to be as small unit as possible. When you do this, then you can maximize the reusability of the processors. The bigger POJO you make, with many business logics mixed in, it will also make it difficult to test. So I recommend you when developing these processor beans, try to think them as LEGO pieces — small POJO. You want to let Camel define the route and glue the LEGO pieces togther. Once you get into this habit of thiking, then you can use Camel in a more effectively way to solve many of your domain problems. Well, that’s all for today folks. I hope you enjoyed the Camel ride. Happy coding and don’t forget to share! Reference: Building message based application using Camel from our JCG partner Zemian Deng at the A Programmer’s Journal blog....
apache-myfaces-logo

Configure timeout for CDI conversations

CDI conversation scope is a nice feature when developing JSF applications. Imagine you have large data tables which take a long time to be loaded. You normally don’t want to place the loaded data in session scoped beans by reason of high memory consumption. And you can’t place the loaded data in view scoped beans because you wouldn’t like always to reload data again if user leave and enter the same view. It would be nice only to keep data if user enters the same page within a certain time interval and reload them again if the bean was not accessed within this time interval. This can be achieved by conversation scoped bean with timeout. We will deal with MyFaces CODI (CDI Extensions) and see how to set a custom timeout for beans annotated with @ConversationScoped. The default timeout is 30 min. what is too long for our example. We will configure it for 1 min. The first step is to extend CODI’s ConversationConfig and overwrite the method getConversationTimeoutInMinutes(). Let’s write a class AlternativeConversationConfig. package controller.cdi;import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Specializes; import org.apache.myfaces.extensions.cdi.core.api.config.ConfigEntry; import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.config.ConversationConfig;@ApplicationScoped @Alternative @Specializes public class AlternativeConversationConfig extends ConversationConfig {@ConfigEntry public int getConversationTimeoutInMinutes() { return 1; } }The important thing is the annotation @Specializes which allows to inject AlternativeConversationConfig instead of ConversationConfig at every existing places. The second step is a proper entry in beans.xml to use (activate) our class on all injection points for ConversationConfig. <alternatives> <class>controller.cdi.AlternativeConversationConfig</class> </alternatives>Server’s log output during startup contains these lines now config implementation: controller.cdi.AlternativeConversationConfig$Proxy$_$$_WeldClientProxy config implementation: controller.cdi.AlternativeConversationConfig method: getConversationTimeoutInMinutes value: 1To check if everything is ok, we can write a conversation scoped bean and use it in facelets. import java.io.Serializable; import javax.faces.event.ActionEvent; import javax.inject.Named; import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ConversationScoped;@Named @ConversationScoped public class CdiBeanConversationScoped implements Serializable { private int counter; public int getCounter() { return counter; } public void increment(ActionEvent e) { counter++; } }<h:outputText id='counter' value='Conversation scoped counter: #{cdiBeanConversationScoped.counter}'/> <p:commandButton value='Increment counter' process='@this' update='counter' actionListener='#{cdiBeanConversationScoped.increment}'/>The counter will expire after 1 min. if no access to the bean happens within this time interval. Simple push the button to increment the counter, wait longer than 1 min. and increment it again. You will see that counter was reseted. Reference: Configure timeout for CDI conversations from our JCG partner Oleg Varaksin at the Thoughts on software development blog....
jboss-hibernate-logo

Hibernate: save vs persist and saveOrUpdate

What is difference between save and saveOrUpdate or Difference between save and persist are common interview question in any Hibernate interview, much like Difference between get and load method in Hibernate. Hibernate Session class provides couple of ways to save object into database by methods like save , saveOrUpdate and persist . You can use either save() , saveOrUpdate() or persist() based upon your requirement for persisting object into Database. Along with Spring framework Interview questions, Hibernate questions are also quite popular on J2EE interviews because of its status as leading ORM. It’s good to prepare some questions from Hibernate before appearing in any J2EE interviews. One of them is Difference between save , saveOrUpdate and persist, which we will see in this Hibernate article. Difference between save and saveOrUpdate in Hibernate Main difference between save and saveOrUpdate method is that save() generates a new identifier and INSERT record into database while saveOrUpdate can either INSERT or UPDATE based upon existence of record. Clearly saveOrUpdate is more flexible in terms of use but it involves an extra processing to find out whether record already exists in table or not. In summary save() method saves records into database by INSERT SQL query, Generates a new identifier and return the Serializable identifier back. On the other hand saveOrUpdate() method either INSERT or UPDATE based upon existence of object in database. If persistence object already exists in database then UPDATE SQL will execute and if there is no corresponding object in database than INSERT will run. Difference between save and persist method in Hibernate In last section we saw What are difference between save and saveOrUpdate and now we will see Difference on save vs persist method.First difference between save and persist is there return type. Similar to save method persist also INSERT records into database but return type of persist is void while return type of save is Serializable object. Another difference between persist and save is that both methods make a transient instance persistent. However, persist() method doesn’t guarantee that the identifier value will be assigned to the persistent instance immediately, the assignment might happen at flush time. One more thing which differentiate persist and save method in Hibernate is that is there behaviour on outside of transaction boundaries. persist() method guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries. save() method does not guarantee the same, it returns an identifier, and if an INSERT has to be executed to get the identifier (e.g. ‘identity’ generator), this INSERT happens immediately, no matter if you are inside or outside of a transaction. Fourth difference between save and persist method in Hibernate is related to previous difference on save vs persist. Because of its above behaviour of persist method outside transaction boundary, its useful in long-running conversations with an extended Session context. On the other hand save method is not good in a long-running conversation with an extended Session context.These were some differences between save, saveOrUpdate and persist method of Hibernate. All three method are related to saving Object into database but there behaviour are quite different. Knowledge of save , persist and saveOrUpdate not only helps to decide better use of Hibernate API but also help you to do well in Hibernate interviews. Don’t forget to share! Reference: Difference between save vs persist and saveOrUpdate in Hibernate from our JCG partner Javin Paul at the Javarevisited blog....
software-development-2-logo

Software Development tips and tricks

These are just some tips and tricks I learnt over my career that I’d like to share. This list does not contains silver bullets and it doesn’t pretend to be complete or be an absolute truth. It’s just a list that I hope may be helpful for someone. 1 ) Push the coding to the last This point can be also be read as think before coding. I found myself, lots of times, especially in the beginning of my career, in rush of writing classes, methods, interfaces as soon as I was receiving a spec for a new functionality. This impulse turned out 90% of the times into a PAINFUL situation. The spec was unclear and so unclear was my code; The spec was too ‘code oriented’ and so blindly coding this was producing wrong design/ implementation that needed to be trashed out and completely re-designed; Many other are the disadvantages of writing code straight away after receiving a spec. So, lesson learnt and this is what I’m doing now after receiving a spec/story to develop:I stay away from the keyboard! I print the spec (sorry amazonian jaguars ) and I take my time to read them carefully. Do not think that everything you will read there is exempt of mistakes or cannot be done in better and simpler ways. At the end of the day, people who wrote the specs, know little or nothing about the software, what they know is how to make more money with new functionality. If something is not clear to you, it will not clarify magically by itself if you start to code. Ask questions to other colleagues, to the business, clarify as much as possible and only when you feel confident, when you see the real pitcure of what they really want, then and only then you can switch the monitor on and click on the eclipse icon…2 ) Simple = perfect Make the 2 famous principles, KISS and YAGNI, part of your blood, skin and bones. Strive for simplicity, forget about the trends, focus on the most basic thing that will give you what is needed. Don’t think straight about XML configuration, spring/seam framework, drools, aspects, multithreading solutions, ejbs, or whatever complex/cool technology you know or you heard out there, nobody really cares about the technologies you use. What everyone cares is that your code does what is supposed to do, it is READABLE and it is MAINTAINABLE. Pure java is enough to cover 90% or more of everything you need. Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. Make him happy, write simple, clear straightforward code. I remember I read somewhere that, if you read your code after 1 or 2 years and you immediately understand everything is doing and you cannot simplify it more, than your code is PERFECT.3 ) Paper and pen I never found a tool more useful than paper and pen to design software. Before writing software I usually spend a bit of time trying to put the idea of the design down on paper. I then , if in doubt, go through it with a colleague. Because it is on paper I can fast go to the point with my pair and he can quick tell me if I’m missing something. Knowing that you are missing something in a piece of paper, it’s far way better then knowing that you are missing something in the code! So don’t waste time using UML tools, unless what you are doing is big-fantastic-amazing-new architectural prototype. 4 ) Don’t leave for tomorrow what you can do today If you are fixing a bug or adding new functionality and you notice in the existing code, DUPLICATION or BAD NAMES or a POSSIBLE BUG then FIX IT IMMEDIATELY. Don’t wait for the next sprint and don’t add a technical story in the technical backlog. Technical stories laid in the backlog for long time, when/if it will be the time to work on it, it will be more complicated to re-understand the context of the problem and fix it. The exception it’s when the fix/refactoring is too complex and required long time, then it’s appropriate to create a story in the technical backlog! 5 ) You have time! Really, you have time! Don’t rush. There is no competition and you are not at the programming Olympics. Your job is to deliver good quality code. That’s it. This is your job! If you feel that a task takes longer that what you initially estimated, just make the team aware in the next stand up and that’s it. If a client/manager is screaming above your shoulders because he wants something faster than you can deliver, it’s not your problem!!! When they say ‘I want this functionality to be ready asap’ translated in Truthlish it means ‘I want this functionality to be PERFECT with NO BUGS asap’. They will be happier to wait more and have less bugs than having a fast release full of issues. So take your time and be intransigent on the quality level of your software and forget about external pressures! 6 ) Implement leaves first So, you just received a brand new story to develop and you want to implement it right. For example the story is about calculating item prices. One price for each item. Each price is obtained with a different calculation. Choose one item price logic and as first thing write a unit test against this logic. Don’t be stressed about implementing the whole design or writing interfaces, abstract classes etc. Focus on one small leaf per time! When the first leaf is finished, start with a second test for the second small logic you want to implement. The design will come out almost by itself. The tests will tell you which direction is best to take.7 ) Do Pair programming! I fully applied this technique only recently (less than 1 year ago) and I have to say, it’s powerful! It is twice productive than working alone, the quality of the code is superior, the development speed is increased. A strength of this technique is the fact that is funny. being funny and so more stimulating means that you are more focused and you pay more attention to details. You can learn a lot doing pair programming, so push for it as much as you can! 8 ) Rarely use checked Exceptions Every time I use an API that force me to put around its method call the try and catch block, it makes me nervous, like a violent psychopath, but often I don’t know where they leave. Especially if there is nothing I can do about the exception. What the hell I’ m supposed to do with this error? If you want to use checked Exception used them ONLY if the client can reasonably recover from it at run time! Remember that checked exceptions will spread all over your design in case you need to extend the behavior! 9 ) Say NO to Hacks Programming is like sex. One mistake and you have to support it for the rest of your life. (Michael Sinz). And Hacks ARE mistakes! Simply don’t implement hacks. I did it in the past, for whatever reasons ( no time,pressure, desire to release a feature as soon as possible, managers screaming, Orcs, Leprechauns, whatever), and now after years we need to maintain them. Hacks are difficult to understand,they break the normal logic flow of your code and they are hard to die! You implement an hack today and tomorrow (1 year after maybe ) someone else will need to fix a bug on this hack and ..well good luck with that! Spend more time on the spec if the hack is a business request, and explain why this will cost a lot to maintain. Propose other solutions, discuss it with your team, a better solution is on the way! 10 ) Forget about comments, just write proper names! Write proper names when writing packages, classes, methods and variables. Write names that make sense, ask a review before committing your code, but don’t explain verbally what you have done, try first letting your reviewer read the code, and see how good you were in writing self explanatory code! Comments need to be maintained, otherwise they are misleading. How often did you fix a bug in the code and you changed the class documentation?me, rarely!Use javadoc only if it is really necessary!and if you do,then remember to update them every time you change the code and enforce the same behavior in your team! 11 ) Reinvent the wheels Read books, blogs, tutorial, etc as much as possible, but always with a critical and open mind! The same apply when listening to your team or leader. Don’t be fooled by authorities, think always with your own mind. If you decide to go for some approach it must be because you believe in it. Because you see good reasons to do so. If you feel that something could be done better or you see problems in the approach you are using , discuss it with your team and try to clarify your doubts. If after talking to the team you still feel that things should be done differently, then push for better approaches and fight for your ideas! Sometimes, to progress, you need to reinvent the wheels or at this time we were all developing in FORTRAN. Don’t forget to share! Reference: Some development tips and tricks from our JCG partner Marco Castigliego at the Remove duplication and fix bad names blog....
Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close