Wednesday, 5 July 2017

Spring MVC Java Config : Part 2 Bean Validation and Exception Handler


It is always good to check the validity and integrity of form data before passing to business logic, let's see how to do it.





1.TOOLS AND ENV

IDE : Spring Tool Suite 3.7.3 
JDK : 1.8 
Tomcat : 8.0.18 
Spring : 4.2.6.RELEASE

 

2. POM.XML

<?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>org.junjun.spring.tutorials</groupId>
<artifactId>spring-mvc-bean-validation</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
<org.springframework.version>4.2.6.RELEASE</org.springframework.version>
<slf4j.version>1.7.10</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- http://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
</plugins>
</build>
</project>
view raw pom.xml hosted with ❤ by GitHub
Java Bean Validation API and Bean Validation  Provider (Hibernate) added.



3. SEPARATE ROOT AND WEB CONFIG

package org.junjun.spring.tutorials.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
}

package org.junjun.spring.tutorials.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({ AppConfig.class })
public class RootConfig {
}
view raw RootConfig.java hosted with ❤ by GitHub

package org.junjun.spring.tutorials.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({ "org.junjun.spring.tutorials.service", "org.junjun.spring.tutorials.dao" })
public class AppConfig {
}
view raw AppConfig.java hosted with ❤ by GitHub

package org.junjun.spring.tutorials.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan({ "org.junjun.spring.tutorials.web" })
public class WebConfig {
/**
* Register Bean Validator.
*
* @return
*/
@Bean
public javax.validation.Validator localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
/**
* Configure MessageSource to provide internationalized messages
*
*/
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
/**
* Configure View Resolver
*/
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
view raw WebConfig.java hosted with ❤ by GitHub





4. POJO
package org.junjun.spring.tutorials.bean;
import static org.junjun.spring.tutorials.common.Const.DF_DD_MMM_YYYY;
import java.io.Serializable;
import java.util.Date;
import org.hibernate.validator.constraints.NotEmpty;
public class ToDoEntity implements Serializable {
@NotEmpty
String content;
Date createdDate;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getCreatedDate() {
return createdDate;
}
public String getCreatedDateDisplay() {
return DF_DD_MMM_YYYY.format(createdDate);
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
}
view raw ToDoEntity.java hosted with ❤ by GitHub

We would make this POJO a JPA entity class in following posts when persistence is introduced.




5. DAO
package org.junjun.spring.tutorials.dao;
import java.util.List;
import org.junjun.spring.tutorials.bean.ToDoEntity;
public interface ToDoDao {
List<ToDoEntity> getAll();
ToDoEntity create(ToDoEntity toDoEntity);
}
view raw ToDoDao.java hosted with ❤ by GitHub

package org.junjun.spring.tutorials.dao.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junjun.spring.tutorials.bean.ToDoEntity;
import org.junjun.spring.tutorials.dao.ToDoDao;
import org.springframework.stereotype.Repository;
@Repository
public class ToDoDaoImpl implements ToDoDao {
private List<ToDoEntity> list = new ArrayList<>();
public ToDoDaoImpl() {
ToDoEntity defaultToDo = new ToDoEntity();
defaultToDo.setContent("default");
defaultToDo.setCreatedDate(new Date());
list.add(defaultToDo);
}
@Override
public List<ToDoEntity> getAll() {
return list;
}
@Override
public ToDoEntity create(ToDoEntity toDoEntity) {
list.add(toDoEntity);
return toDoEntity;
}
}

We would have DAO talk to database in the following posts.




6.  BUSINESS LOGIC LAYER

package org.junjun.spring.tutorials.service;
import java.util.List;
import org.junjun.spring.tutorials.bean.ToDoEntity;
public interface ToDoService {
List<ToDoEntity> getAll();
ToDoEntity create(ToDoEntity todo);
}

package org.junjun.spring.tutorials.service.impl;
import java.util.Date;
import java.util.List;
import org.junjun.spring.tutorials.bean.ToDoEntity;
import org.junjun.spring.tutorials.dao.ToDoDao;
import org.junjun.spring.tutorials.service.ToDoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ToDoServiceImpl implements ToDoService {
@Autowired
private ToDoDao toDoDao;
@Override
public List<ToDoEntity> getAll() {
return toDoDao.getAll();
}
@Override
public ToDoEntity create(ToDoEntity toDoEntity) {
toDoEntity.setCreatedDate(new Date());
return toDoDao.create(toDoEntity);
}
}




7. TODO CONTROLLER

package org.junjun.spring.tutorials.web.controller;
import java.util.List;
import javax.validation.Valid;
import org.apache.log4j.Logger;
import org.junjun.spring.tutorials.bean.ToDoEntity;
import org.junjun.spring.tutorials.service.ToDoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("todo")
public class ToDoController {
private static final Logger logger = Logger.getLogger(ToDoController.class);
@Autowired
private ToDoService toDoService;
@RequestMapping(value = "list", method = RequestMethod.GET)
public String list(Model model) {
String page = "todo/list";
List<ToDoEntity> toDoList = toDoService.getAll();
model.addAttribute("toDoList", toDoList);
model.addAttribute("newToDo", new ToDoEntity());
return page;
}
@RequestMapping(value = "create", method = RequestMethod.POST)
public String create(@Valid @ModelAttribute("newToDo") ToDoEntity toDoEntity, BindingResult result, Model model) {
String page = "todo/list";
if (result.hasErrors()) {
for (ObjectError error : result.getAllErrors()) {
logger.debug(error.getDefaultMessage());
}
} else {
toDoService.create(toDoEntity);
}
List<ToDoEntity> toDoList = toDoService.getAll();
model.addAttribute("toDoList", toDoList);
model.addAttribute("newToDo", toDoEntity);
return page;
}
@RequestMapping(value = "error", method = RequestMethod.GET)
public String error(Model model) {
String page = "todo/list";
String[] data = new String[] { "1" };
logger.error(data[2]);
return page;
}
}

1. Container will do bean validation on model attribute that marked with @Valid 
2. BindingResult must be declared right after @Valid




8. EXCEPTION HANDLER



package org.junjun.spring.tutorials.web.advice;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
@ControllerAdvice
public class ExceptionAdviceController {
@ExceptionHandler(NoHandlerFoundException.class)
public String dealWithNoHandlerFoundException(HttpServletRequest httpServletRequest) {
return "not-found";
}
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}

@ControllerAdvice  is typically used to define ExceptionHandler , InitBinder and ModelAttribute methods that apply to all RequestMapping methods.


9. WEB PAGE

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="spring">
<meta name="keywords" content="spring">
<title></title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
<div class="container" style="padding-top: 10%">
<div class="row">
<form:form class="form-horizontal" role="form"
modelAttribute="newToDo" method="post"
action="${pageContext.request.contextPath}/todo/create">
<div class="form-group">
<div class="col-md-offset-2 col-md-6">
<form:input id="todoContent" class="form-control" path="content"
placeholder="" />
</div>
<label for="todoContent" class="col-md-4 control-label"
style="text-align: left;"><spring:bind path="content">
<c:if test="${status.error}">
<form:errors path="content" class="text-danger" />
</c:if>
</spring:bind></label>
</div>
</form:form>
</div>
<div class="row">
<div class="col-md-offset-2 col-md-10">
<ul>
<c:forEach items="${toDoList}" var="toDo">
<li>${toDo.createdDateDisplay}&nbsp;:&nbsp;${toDo.content}</li>
</c:forEach>
</ul>
</div>
</div>
</div>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>
view raw list.jsp hosted with ❤ by GitHub


<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="spring">
<meta name="keywords" content="spring">
<title></title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
<div class="container" style="padding-top: 10%; height: 100%">
<div class="text-center col-md-12">
<h1>NOT FOUND</h1>
</div>
</div>
</body>
</html>
view raw not-found.jsp hosted with ❤ by GitHub


10. Messages for Validation Error







11. TEST



http://localhost:8080/spring-mvc-bean-validation/anything


http://localhost:8080/spring-mvc-bean-validation/todo/error






12. SOURCE CODE

https://github.com/junjun-dachi/spring-tutorials/tree/master/spring-mvc-bean-validation







No comments:

Post a Comment

Flag Counter