├── .gitignore ├── src ├── main │ ├── resources │ │ ├── data.sql │ │ ├── messages.properties │ │ ├── application.properties │ │ └── logback.xml │ ├── java │ │ └── eu │ │ │ └── kielczewski │ │ │ └── example │ │ │ ├── repository │ │ │ └── UserRepository.java │ │ │ ├── service │ │ │ ├── UserService.java │ │ │ ├── exception │ │ │ │ └── UserAlreadyExistsException.java │ │ │ └── UserServiceImpl.java │ │ │ ├── validator │ │ │ └── UserCreateFormPasswordValidator.java │ │ │ ├── Application.java │ │ │ ├── controller │ │ │ ├── UserListController.java │ │ │ ├── UserRestController.java │ │ │ └── UserCreateController.java │ │ │ └── domain │ │ │ ├── User.java │ │ │ └── UserCreateForm.java │ └── webapp │ │ └── WEB-INF │ │ └── jsp │ │ ├── user_list.jsp │ │ └── user_create.jsp └── test │ └── java │ └── eu │ └── kielczewski │ └── example │ ├── util │ └── UserUtil.java │ ├── controller │ ├── UserListControllerTest.java │ ├── UserRestControllerTest.java │ └── UserCreateControllerTest.java │ ├── validator │ └── UserCreateFormPasswordValidatorTest.java │ └── service │ └── UserServiceImplTest.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target -------------------------------------------------------------------------------- /src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO user (id, password) VALUES ('user1', 'pass1'); 2 | INSERT INTO user (id, password) VALUES ('user2', 'pass1'); 3 | INSERT INTO user (id, password) VALUES ('user3', 'pass1'); 4 | INSERT INTO user (id, password) VALUES ('user4', 'pass1'); -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.repository; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface UserRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/service/UserService.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.service; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | 5 | import java.util.List; 6 | 7 | public interface UserService { 8 | 9 | User save(User user); 10 | 11 | List getList(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/service/exception/UserAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.service.exception; 2 | 3 | public class UserAlreadyExistsException extends RuntimeException { 4 | 5 | public UserAlreadyExistsException(final String message) { 6 | super(message); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/messages.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" for whole file 2 | 3 | user.list=List of users 4 | user.create=Create new user 5 | 6 | user.id=ID 7 | user.password1=Password 8 | user.password2=Repeat 9 | user.error.exists=User already exists 10 | user.error.password.no_match=Repeated password must be the same 11 | 12 | NotEmpty=Required 13 | Size.form.id=ID must be no more than {1} characters 14 | Size.form.password1=Password must be no more than {1} characters 15 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" for whole file 2 | 3 | # Spring 4 | spring.profiles.active=dev 5 | 6 | # Server 7 | server.port=8080 8 | server.sessionTimeout=30 9 | 10 | # MVC 11 | spring.view.prefix=/WEB-INF/jsp/ 12 | spring.view.suffix=.jsp 13 | 14 | # JPA 15 | spring.jpa.hibernate.ddl-auto=create-drop 16 | 17 | # Tomcat 18 | tomcat.accessLogEnabled=false 19 | tomcat.protocolHeader=x-forwarded-proto 20 | tomcat.remoteIpHeader=x-forwarded-for 21 | tomcat.backgroundProcessorDelay=30 22 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System.out 5 | 6 | %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/user_list.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 2 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 | 5 | 6 | 7 |

8 | 16 | 17 | "> 18 | 19 | -------------------------------------------------------------------------------- /src/test/java/eu/kielczewski/example/util/UserUtil.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.util; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class UserUtil { 9 | 10 | private static final String ID = "id"; 11 | private static final String PASSWORD = "password"; 12 | 13 | private UserUtil() { 14 | } 15 | 16 | public static User createUser() { 17 | return new User(ID, PASSWORD); 18 | } 19 | 20 | public static List createUserList(int howMany) { 21 | List userList = new ArrayList<>(); 22 | for (int i = 0; i < howMany; i++) { 23 | userList.add(new User(ID + "#" + i, PASSWORD)); 24 | } 25 | return userList; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example Spring Boot MVC Application 2 | =================================== 3 | 4 | The article: [http://kielczewski.eu/2014/04/spring-boot-mvc-application/](http://kielczewski.eu/2014/04/spring-boot-mvc-application/) 5 | 6 | Also check out: [http://kielczewski.eu/2014/04/developing-restful-web-service-with-spring-boot/](http://kielczewski.eu/2014/04/developing-restful-web-service-with-spring-boot/) 7 | 8 | Requirements 9 | ------------ 10 | * [Java Platform (JDK) 7](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 11 | * [Apache Maven 3.x](http://maven.apache.org/) 12 | 13 | Quick start 14 | ----------- 15 | 1. `mvn package` 16 | 2. `java -jar target/example-spring-boot-mvc-1.0-SNAPSHOT.war` 17 | 3. Point your browser to [http://localhost:8080/user_list.html](http://localhost:8080/user_list.html) -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/validator/UserCreateFormPasswordValidator.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.validator; 2 | 3 | import eu.kielczewski.example.domain.UserCreateForm; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.validation.Errors; 6 | import org.springframework.validation.Validator; 7 | 8 | @Component 9 | public class UserCreateFormPasswordValidator implements Validator { 10 | 11 | @Override 12 | public boolean supports(Class clazz) { 13 | return UserCreateForm.class.isAssignableFrom(clazz); 14 | } 15 | 16 | @Override 17 | public void validate(Object target, Errors errors) { 18 | UserCreateForm form = (UserCreateForm) target; 19 | if (!form.getPassword1().equals(form.getPassword2())) { 20 | errors.rejectValue("password2", "user.error.password.no_match"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/Application.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.context.web.SpringBootServletInitializer; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @EnableAutoConfiguration 12 | @ComponentScan 13 | public class Application extends SpringBootServletInitializer { 14 | 15 | public static void main(final String[] args) { 16 | SpringApplication.run(Application.class, args); 17 | } 18 | 19 | @Override 20 | protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) { 21 | return application.sources(Application.class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/controller/UserListController.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.controller; 2 | 3 | import eu.kielczewski.example.service.UserService; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.ModelMap; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | import javax.inject.Inject; 12 | 13 | @Controller 14 | public class UserListController { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(UserListController.class); 17 | private final UserService userService; 18 | 19 | @Inject 20 | public UserListController(final UserService userService) { 21 | this.userService = userService; 22 | } 23 | 24 | @RequestMapping("/user_list.html") 25 | public ModelAndView getListUsersView() { 26 | LOGGER.debug("Received request to get user list view"); 27 | ModelMap model = new ModelMap(); 28 | model.addAttribute("users", userService.getList()); 29 | return new ModelAndView("user_list", model); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/domain/User.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.domain; 2 | 3 | import com.google.common.base.Objects; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.Id; 8 | import javax.validation.constraints.NotNull; 9 | import javax.validation.constraints.Size; 10 | 11 | @Entity 12 | public class User { 13 | 14 | @Id 15 | @NotNull 16 | @Size(max = 64) 17 | @Column(name = "id", nullable = false, updatable = false) 18 | private String id; 19 | 20 | @NotNull 21 | @Size(max = 64) 22 | @Column(name = "password", nullable = false) 23 | private String password; 24 | 25 | private User() { 26 | } 27 | 28 | public User(final String id, final String password) { 29 | this.id = id; 30 | this.password = password; 31 | } 32 | 33 | public String getId() { 34 | return id; 35 | } 36 | 37 | public String getPassword() { 38 | return password; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return Objects.toStringHelper(this) 44 | .add("id", id) 45 | .add("password", password) 46 | .toString(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/user_create.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> 2 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 3 | <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 4 | 5 | 6 |

7 | "> 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/domain/UserCreateForm.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.domain; 2 | 3 | import com.google.common.base.Objects; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | import javax.validation.constraints.Size; 7 | 8 | public class UserCreateForm { 9 | 10 | @NotEmpty 11 | @Size(max = 64) 12 | private String id; 13 | @NotEmpty 14 | @Size(max = 64) 15 | private String password1; 16 | private String password2; 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getPassword1() { 27 | return password1; 28 | } 29 | 30 | public void setPassword1(String password1) { 31 | this.password1 = password1; 32 | } 33 | 34 | public String getPassword2() { 35 | return password2; 36 | } 37 | 38 | public void setPassword2(String password2) { 39 | this.password2 = password2; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return Objects.toStringHelper(this) 45 | .add("id", id) 46 | .add("password1", password1) 47 | .add("password2", password2) 48 | .toString(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/controller/UserRestController.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.controller; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.service.UserService; 5 | import eu.kielczewski.example.service.exception.UserAlreadyExistsException; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import javax.inject.Inject; 12 | import javax.validation.Valid; 13 | import java.util.List; 14 | 15 | @RestController 16 | public class UserRestController { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(UserRestController.class); 19 | private final UserService userService; 20 | 21 | @Inject 22 | public UserRestController(final UserService userService) { 23 | this.userService = userService; 24 | } 25 | 26 | @RequestMapping(value = "/user", method = RequestMethod.POST) 27 | public User createUser(@RequestBody @Valid final User user) { 28 | LOGGER.debug("Received request to create the {}", user); 29 | return userService.save(user); 30 | } 31 | 32 | @RequestMapping(value = "/user", method = RequestMethod.GET) 33 | public List listUsers() { 34 | LOGGER.debug("Received request to list all users"); 35 | return userService.getList(); 36 | } 37 | 38 | @ExceptionHandler 39 | @ResponseStatus(HttpStatus.CONFLICT) 40 | public String handleUserAlreadyExistsException(UserAlreadyExistsException e) { 41 | return e.getMessage(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.service; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.repository.UserRepository; 5 | import eu.kielczewski.example.service.exception.UserAlreadyExistsException; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | import org.springframework.validation.annotation.Validated; 11 | 12 | import javax.inject.Inject; 13 | import javax.validation.Valid; 14 | import javax.validation.constraints.NotNull; 15 | import java.util.List; 16 | 17 | @Service 18 | @Validated 19 | public class UserServiceImpl implements UserService { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class); 22 | private final UserRepository repository; 23 | 24 | @Inject 25 | public UserServiceImpl(final UserRepository repository) { 26 | this.repository = repository; 27 | } 28 | 29 | @Override 30 | @Transactional 31 | public User save(@NotNull @Valid final User user) { 32 | LOGGER.debug("Creating {}", user); 33 | User existing = repository.findOne(user.getId()); 34 | if (existing != null) { 35 | throw new UserAlreadyExistsException( 36 | String.format("There already exists a user with id=%s", user.getId())); 37 | } 38 | return repository.save(user); 39 | } 40 | 41 | @Override 42 | @Transactional(readOnly = true) 43 | public List getList() { 44 | LOGGER.debug("Retrieving the list of all users"); 45 | return repository.findAll(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/eu/kielczewski/example/controller/UserListControllerTest.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.controller; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.service.UserService; 5 | import eu.kielczewski.example.util.UserUtil; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.Mock; 10 | import org.mockito.runners.MockitoJUnitRunner; 11 | import org.springframework.web.servlet.ModelAndView; 12 | 13 | import java.util.List; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | import static org.mockito.Mockito.*; 17 | 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class UserListControllerTest { 20 | 21 | @Mock 22 | private UserService userService; 23 | 24 | private UserListController userController; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | userController = new UserListController(userService); 29 | } 30 | 31 | @Test 32 | public void shouldGetUserListPage() { 33 | List userList = stubServiceToReturnExistingUsers(5); 34 | ModelAndView view = userController.getListUsersView(); 35 | // verify UserService was called once 36 | verify(userService, times(1)).getList(); 37 | assertEquals("View name should be right", "user_list", view.getViewName()); 38 | assertEquals("Model should contain attribute with the list of users coming from the service", 39 | userList, view.getModel().get("users")); 40 | } 41 | 42 | private List stubServiceToReturnExistingUsers(int howMany) { 43 | List userList = UserUtil.createUserList(howMany); 44 | when(userService.getList()).thenReturn(userList); 45 | return userList; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/eu/kielczewski/example/validator/UserCreateFormPasswordValidatorTest.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.validator; 2 | 3 | import eu.kielczewski.example.domain.UserCreateForm; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mock; 7 | import org.mockito.runners.MockitoJUnitRunner; 8 | import org.springframework.validation.Errors; 9 | 10 | import static org.junit.Assert.assertFalse; 11 | import static org.junit.Assert.assertTrue; 12 | import static org.mockito.Matchers.anyString; 13 | import static org.mockito.Mockito.never; 14 | import static org.mockito.Mockito.verify; 15 | 16 | @RunWith(MockitoJUnitRunner.class) 17 | public class UserCreateFormPasswordValidatorTest { 18 | 19 | @Mock 20 | private Errors errors; 21 | 22 | private UserCreateFormPasswordValidator passwordValidator = new UserCreateFormPasswordValidator(); 23 | 24 | @Test 25 | public void shouldSayItSupports_GivenItReceivesUserCreateFormClass() throws Exception { 26 | assertTrue(passwordValidator.supports(UserCreateForm.class)); 27 | } 28 | 29 | @Test 30 | public void shouldSayItSupports_GivenItReceivesDifferentClass_ThenShouldReturnFalse() throws Exception { 31 | assertFalse(passwordValidator.supports(Object.class)); 32 | } 33 | 34 | @Test 35 | public void shouldValidate_GivenPasswordsMatch_ThenErrorIsNotSet() throws Exception { 36 | UserCreateForm form = new UserCreateForm(); 37 | form.setPassword1("password"); 38 | form.setPassword2("password"); 39 | passwordValidator.validate(form, errors); 40 | verify(errors, never()).rejectValue(anyString(), anyString()); 41 | } 42 | 43 | @Test 44 | public void shouldValidate_GivenPasswordsDoNotMatch_ThenErrorIsSet() throws Exception { 45 | UserCreateForm form = new UserCreateForm(); 46 | form.setPassword1("password"); 47 | form.setPassword2("different"); 48 | passwordValidator.validate(form, errors); 49 | verify(errors).rejectValue("password2", "user.error.password.no_match"); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/eu/kielczewski/example/controller/UserRestControllerTest.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.controller; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.service.UserService; 5 | import eu.kielczewski.example.util.UserUtil; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.Mock; 10 | import org.mockito.runners.MockitoJUnitRunner; 11 | 12 | import java.util.Collection; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.assertNotNull; 16 | import static org.mockito.Mockito.*; 17 | 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class UserRestControllerTest { 20 | 21 | @Mock 22 | private UserService userService; 23 | 24 | private UserRestController userRestController; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | userRestController = new UserRestController(userService); 29 | } 30 | 31 | @Test 32 | public void shouldCreateUser() { 33 | final User savedUser = stubServiceToReturnStoredUser(); 34 | final User user = UserUtil.createUser(); 35 | User returnedUser = userRestController.createUser(user); 36 | // verify user was passed to UserService 37 | verify(userService, times(1)).save(user); 38 | assertEquals("Returned user should come from the service", savedUser, returnedUser); 39 | } 40 | 41 | private User stubServiceToReturnStoredUser() { 42 | final User user = UserUtil.createUser(); 43 | when(userService.save(any(User.class))).thenReturn(user); 44 | return user; 45 | } 46 | 47 | @Test 48 | public void shouldListAllUsers() { 49 | stubServiceToReturnExistingUsers(10); 50 | Collection users = userRestController.listUsers(); 51 | assertNotNull(users); 52 | assertEquals(10, users.size()); 53 | // verify user was passed to UserService 54 | verify(userService, times(1)).getList(); 55 | } 56 | 57 | private void stubServiceToReturnExistingUsers(int howMany) { 58 | when(userService.getList()).thenReturn(UserUtil.createUserList(howMany)); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/eu/kielczewski/example/controller/UserCreateController.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.controller; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.domain.UserCreateForm; 5 | import eu.kielczewski.example.service.UserService; 6 | import eu.kielczewski.example.service.exception.UserAlreadyExistsException; 7 | import eu.kielczewski.example.validator.UserCreateFormPasswordValidator; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.validation.BindingResult; 12 | import org.springframework.web.bind.WebDataBinder; 13 | import org.springframework.web.bind.annotation.InitBinder; 14 | import org.springframework.web.bind.annotation.ModelAttribute; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestMethod; 17 | import org.springframework.web.servlet.ModelAndView; 18 | 19 | import javax.inject.Inject; 20 | import javax.validation.Valid; 21 | 22 | @Controller 23 | public class UserCreateController { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(UserCreateController.class); 26 | private final UserService userService; 27 | private final UserCreateFormPasswordValidator passwordValidator; 28 | 29 | @Inject 30 | public UserCreateController(UserService userService, UserCreateFormPasswordValidator passwordValidator) { 31 | this.userService = userService; 32 | this.passwordValidator = passwordValidator; 33 | } 34 | 35 | @InitBinder("form") 36 | public void initBinder(WebDataBinder binder) { 37 | binder.addValidators(passwordValidator); 38 | } 39 | 40 | @RequestMapping(value = "/user_create.html", method = RequestMethod.GET) 41 | public ModelAndView getCreateUserView() { 42 | LOGGER.debug("Received request for user create view"); 43 | return new ModelAndView("user_create", "form", new UserCreateForm()); 44 | } 45 | 46 | @RequestMapping(value = "/user_create.html", method = RequestMethod.POST) 47 | public String createUser(@ModelAttribute("form") @Valid UserCreateForm form, BindingResult result) { 48 | LOGGER.debug("Received request to create {}, with result={}", form, result); 49 | if (result.hasErrors()) { 50 | return "user_create"; 51 | } 52 | try { 53 | userService.save(new User(form.getId(), form.getPassword2())); 54 | } catch (UserAlreadyExistsException e) { 55 | LOGGER.debug("Tried to create user with existing id", e); 56 | result.reject("user.error.exists"); 57 | return "user_create"; 58 | } 59 | return "redirect:/user_list.html"; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/eu/kielczewski/example/service/UserServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.service; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.repository.UserRepository; 5 | import eu.kielczewski.example.service.exception.UserAlreadyExistsException; 6 | import eu.kielczewski.example.util.UserUtil; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.mockito.Mock; 11 | import org.mockito.runners.MockitoJUnitRunner; 12 | 13 | import java.util.Collection; 14 | 15 | import static org.junit.Assert.*; 16 | import static org.mockito.Matchers.any; 17 | import static org.mockito.Mockito.*; 18 | 19 | @RunWith(MockitoJUnitRunner.class) 20 | public class UserServiceImplTest { 21 | 22 | @Mock 23 | private UserRepository userRepository; 24 | 25 | private UserService userService; 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | userService = new UserServiceImpl(userRepository); 30 | } 31 | 32 | @Test 33 | public void shouldSaveNewUser_GivenThereDoesNotExistOneWithTheSameId_ThenTheSavedUserShouldBeReturned() throws Exception { 34 | final User savedUser = stubRepositoryToReturnUserOnSave(); 35 | final User user = UserUtil.createUser(); 36 | final User returnedUser = userService.save(user); 37 | // verify repository was called with user 38 | verify(userRepository, times(1)).save(user); 39 | assertEquals("Returned user should come from the repository", savedUser, returnedUser); 40 | } 41 | 42 | private User stubRepositoryToReturnUserOnSave() { 43 | User user = UserUtil.createUser(); 44 | when(userRepository.save(any(User.class))).thenReturn(user); 45 | return user; 46 | } 47 | 48 | @Test 49 | public void shouldSaveNewUser_GivenThereExistsOneWithTheSameId_ThenTheExceptionShouldBeThrown() throws Exception { 50 | stubRepositoryToReturnExistingUser(); 51 | try { 52 | userService.save(UserUtil.createUser()); 53 | fail("Expected exception"); 54 | } catch (UserAlreadyExistsException ignored) { 55 | } 56 | verify(userRepository, never()).save(any(User.class)); 57 | } 58 | 59 | private void stubRepositoryToReturnExistingUser() { 60 | final User user = UserUtil.createUser(); 61 | when(userRepository.findOne(user.getId())).thenReturn(user); 62 | } 63 | 64 | @Test 65 | public void shouldListAllUsers_GivenThereExistSome_ThenTheCollectionShouldBeReturned() throws Exception { 66 | stubRepositoryToReturnExistingUsers(10); 67 | Collection list = userService.getList(); 68 | assertNotNull(list); 69 | assertEquals(10, list.size()); 70 | verify(userRepository, times(1)).findAll(); 71 | } 72 | 73 | private void stubRepositoryToReturnExistingUsers(int howMany) { 74 | when(userRepository.findAll()).thenReturn(UserUtil.createUserList(howMany)); 75 | } 76 | 77 | @Test 78 | public void shouldListAllUsers_GivenThereNoneExist_ThenTheEmptyCollectionShouldBeReturned() throws Exception { 79 | stubRepositoryToReturnExistingUsers(0); 80 | Collection list = userService.getList(); 81 | assertNotNull(list); 82 | assertTrue(list.isEmpty()); 83 | verify(userRepository, times(1)).findAll(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/eu/kielczewski/example/controller/UserCreateControllerTest.java: -------------------------------------------------------------------------------- 1 | package eu.kielczewski.example.controller; 2 | 3 | import eu.kielczewski.example.domain.User; 4 | import eu.kielczewski.example.domain.UserCreateForm; 5 | import eu.kielczewski.example.service.UserService; 6 | import eu.kielczewski.example.service.exception.UserAlreadyExistsException; 7 | import eu.kielczewski.example.validator.UserCreateFormPasswordValidator; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.mockito.ArgumentCaptor; 12 | import org.mockito.Mock; 13 | import org.mockito.runners.MockitoJUnitRunner; 14 | import org.springframework.validation.BindingResult; 15 | import org.springframework.web.servlet.ModelAndView; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | import static org.mockito.Mockito.*; 20 | 21 | @RunWith(MockitoJUnitRunner.class) 22 | public class UserCreateControllerTest { 23 | 24 | @Mock 25 | private UserService userService; 26 | @Mock 27 | private BindingResult result; 28 | @Mock 29 | private UserCreateFormPasswordValidator passwordValidator; 30 | 31 | private UserCreateController userController; 32 | 33 | @Before 34 | public void setUp() throws Exception { 35 | userController = new UserCreateController(userService, passwordValidator); 36 | } 37 | 38 | @Test 39 | public void shouldGetCreateUserPage() throws Exception { 40 | ModelAndView view = userController.getCreateUserView(); 41 | assertEquals("View name should be right", "user_create", view.getViewName()); 42 | assertTrue("View should contain attribute with form object", view.getModel().containsKey("form")); 43 | assertTrue("The form object should be of proper type", view.getModel().get("form") instanceof UserCreateForm); 44 | } 45 | 46 | @Test 47 | public void shouldCreateUser_GivenThereAreNoErrors_ThenTheUserShouldBeSavedAndUserListViewDisplayed() { 48 | when(result.hasErrors()).thenReturn(false); 49 | UserCreateForm form = new UserCreateForm(); 50 | form.setId("id"); 51 | form.setPassword1("password"); 52 | form.setPassword2("password"); 53 | String view = userController.createUser(form, result); 54 | assertEquals("There should be proper redirect", "redirect:/user_list.html", view); 55 | ArgumentCaptor captor = ArgumentCaptor.forClass(User.class); 56 | verify(userService, times(1)).save(captor.capture()); 57 | assertEquals(form.getId(), captor.getValue().getId()); 58 | assertEquals(form.getPassword1(), captor.getValue().getPassword()); 59 | } 60 | 61 | @Test 62 | public void shouldCreateUser_GivenThereAreFormErrors_ThenTheFormShouldBeDisplayed() { 63 | when(result.hasErrors()).thenReturn(true); 64 | String view = userController.createUser(new UserCreateForm(), result); 65 | verify(userService, never()).save(any(User.class)); 66 | assertEquals("View name should be right", "user_create", view); 67 | } 68 | 69 | @Test 70 | public void shouldCreateUser_GivenThereAlreadyExistUserWithId_ThenTheFormShouldBeDisplayed() { 71 | when(result.hasErrors()).thenReturn(false); 72 | when(userService.save(any(User.class))).thenThrow(UserAlreadyExistsException.class); 73 | String view = userController.createUser(new UserCreateForm(), result); 74 | verify(result).reject("user.error.exists"); 75 | assertEquals("View name should be right", "user_create", view); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | eu.kielczewski.example.spring 6 | example-spring-boot-mvc 7 | 1.0-SNAPSHOT 8 | war 9 | 10 | 11 | org.springframework.boot 12 | spring-boot-starter-parent 13 | 1.0.1.RELEASE 14 | 15 | 16 | Example Spring Boot MVC Application 17 | 18 | 19 | 1.7 20 | 16.0.1 21 | UTF-8 22 | UTF-8 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-web 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-data-jpa 48 | 49 | 50 | 51 | 52 | 53 | org.apache.tomcat.embed 54 | tomcat-embed-jasper 55 | provided 56 | 57 | 58 | 59 | 60 | 61 | org.hibernate 62 | hibernate-validator 63 | 64 | 65 | 66 | 67 | 68 | org.hsqldb 69 | hsqldb 70 | runtime 71 | 72 | 73 | 74 | 75 | 76 | com.google.guava 77 | guava 78 | ${guava.version} 79 | 80 | 81 | 82 | 83 | 84 | javax.inject 85 | javax.inject 86 | 1 87 | 88 | 89 | 90 | javax.servlet 91 | jstl 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 106 | 107 | 108 | 109 | 110 | --------------------------------------------------------------------------------