TERASOLUNAServer Framework For Java Development Guideline
TERASOLUNAServer Framework For Java Development Guideline
NTT DATA
CONTENTS
1 In the Beginning 3
1.1 Terms of Use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 This document covers the following . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Target readers of this document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Structure of this document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Reading this document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.6 Tested environments of this document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.7 Criteria based mapping of guideline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.8 Change Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2 Architecture Overview 37
2.1 Stack of TERASOLUNA Server Framework for Java (5.x) . . . . . . . . . . . . . . . . . . . . . 37
2.2 Overview of Spring MVC Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.3 First application based on Spring MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.4 Application Layering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
3 Application Development 93
3.1 Create Web application development project . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.2 Domain Layer Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
3.3 Implementation of Infrastructure Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
3.4 Implementation of Application Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
3.5 Build development project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
8 Messaging 1621
8.1 Sending E-mail (SMTP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1621
8.2 JMS(Java Message Service) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1651
10 Tutorials 2027
10.1 Tutorial (Todo Application) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2027
10.2 Tutorial (Todo Application REST) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2127
10.3 Session tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2192
ii
10.4 Spring Security Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2256
iii
iv
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Note: If there are any mistakes in the contents or any comments related to the contents, please raise them at
Issues on Github.
1
3
In the Beginning
In order to use this document, you are required to agree to abide by the following terms. If you do not agree with
the terms, you must immediately delete or destroy this document and all its duplicate copies.
1. Copyrights and all other rights of this document shall belong to NTT DATA or third party possessing such
rights.
2. This document may be reproduced, translated or adapted, in whole or in part for personal use. However,
deletion of the terms given on this page and copyright notice of NTT DATA is prohibited.
3. This document may be changed, in whole or in part for personal use. Creation of secondary work using
this document is allowed. However, “Reference document: TERASOLUNA Server Framework for Java
(5.x) Development Guideline” or equivalent documents may be mentioned in created document and its
duplicate copies.
4. Document and its duplicate copies created according to Clause 2 may be provided to third party only if
these are free of cost.
5. Use of this document and its duplicate copies, and transfer of rights of this contract to a third party, in
whole or in part, beyond the conditions specified in this contract, are prohibited without the written consent
of NTT Data.
6. NTT DATA shall not bear any responsibility regarding correctness of contents of this document, warranty of
fitness for usage purpose, assurance for accuracy and reliability of usage result, liability for defect warranty,
and any damage incurred directly or indirectly.
7. NTT DATA does not guarantee the infringement of copyrights and any other rights of third party through
this document. In addition to this, NTT DATA shall not bear any responsibility regarding any claim (Includ-
ing the claims occurred due to dispute with third party) occurred directly or indirectly due to infringement
of copyright and other rights.
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Registered trademarks or trademarks of company name and service name, and product name of their respective
companies used in this document are as follows.
• All other company names and product names are the registered trademarks or trademarks of their respective
companies.
4 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
This guideline provides best practices to develop highly maintainable Web applications using full stack framework
focussing on Spring, Spring MVC and JPA, MyBatis.
This guideline helps to proceed with the software development (mainly coding) smoothly.
This guideline is written for the architects and programmers having software development experience and knowl-
edge of the following.
• Knowledge of SQL
In order to check whether the readers have basic knowledge of Spring Framework, refer to Spring Framework
Comprehension Check. It is recommended to study the following books if one is not able to answer 40% of the
comprehension test.
•Architecture Overview Overview of Spring MVC and basic concepts of TERASOLUNA Server Frame-
work for Java (5.x) is explained.
•Application Development Knowledge and methods for application development using TERASOLUNA
Server Framework for Java (5.x) are explained.
•Tutorials Experience in application development using TERASOLUNA Server Framework for Java (5.x)
through simple application development.
•Appendix(Know How) Describing the additional information when TERASOLUNA Server Framework
for Java (5.x) is being used.
Use this tutorial to study the details of application development in “Application Development”.
Since the knowhow for development is explained using Spring MVC in “Implementation of Application Layer”,
it is recommended to read it again and again several times.
One can get a better understanding by studying “Tutorials” once again after reading this chapter.
It is strongly recommended that all the developers who use TERASOLUNA Server Framework for Java
(5.x) read it.
The technical leader understands all the contents and checks the type of policy to be decided in the project.
Note: If you do not have sufficient time, first go through the following.
2. Application Layering
4. Application Development
6 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
6. Input Validation
For tested environments of contents described in this guideline, refer to “Tested Environment”.
The chapter 4 of this guideline is structured functionality wise. This section shows a mapping from a point of
view other than functionality. It indicates which part of guideline contains which type of content.
Using OWASP Top 10 for 2013 as an axis, links to explanation of functionalities related to security have been
given
8 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
A1 Injection
• Input check validation for security
(Shows how to validate input values)
A2 Broken Authentication and Session Manage-
• Session Management
ment
• Authentication
A5 Security Misconfiguration
• Logging(Mention about message contents of log)
• Method to display system exception code on
screen(Mention about message output at the time
of system exception)
10 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
12 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
General Correction of errors in the guideline (typo mistakes and simple de-
scription errors)
Description details modified
• For details of modification, refer 5.1.0 Issue list (improve-
ment).
14 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
16 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
18 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
20 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Stack of TERASOLUNA Updated the OSS version(Spring IO Platform version) to protect se-
Server Framework for Java curity vulnerability
(5.x) • Spring IO Platform version updated to 1.1.3.RELEASE
• Spring Framework version updated to 4.1.7.RELEASE
(CVE-2015-3192)
• JSTL version updated to 1.2.5 (CVE-2015-0254)
Updated the OSS version by the Spring IO Platform version update
• Updated the OSS version to be used. For update details, refer
to Migration guide of version 5.0.1
Improved the description (guideline#1148)
• Added the description of
terasoluna-gfw-recommended-dependencies,terasolun
and terasoluna-gfw-parent
• Modified the description for some project
• Added the illustration to indicate project dependencies
22 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
24 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
26 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
28 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
30 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
32 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
34 1 In the Beginning
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Stack of TERASOLUNA Updated the OSS version in accordance with bug fixes.
Server Framework for Java • GroupId (org.springframework ) updated to
(5.x) 3.2.10.RELEASE from 3.2.4.RELEASE
• GroupId (org.springframework.data )/Artifac-
tId(spring-data-commons ) updated to 1.6.4.RE-
LEASE from 1.6.1.RELEASE
• GroupId (org.springframework.data )/Artifac-
tId(spring-data-jpa ) updated to 1.4.3.RELEASE from
1.4.1.RELEASE
• GroupId (org.aspectj ) updated to 1.7.4 from 1.7.3
• Deleted GroupId (javax.transaction )/ArtifactId(jta
)
Japanese version Change how to create following projects to be carried out from mvn
archetype:generate
• First application based on Spring MVC
• Tutorial (Todo Application)
• Tutorial (Todo Application)
36 1 In the Beginning
37
Architecture Overview
Software Framework being used in TERASOLUNA Server Framework for Java (5.x) is not a proprietary Frame-
work but a combination of various OSS technologies around Spring Framework.
Libraries which constitute TERASOLUNA Server Framework for Java (5.x) are as follows:
DI Container
MVC Framework
O/R Mapper
• MyBatis 3.3
• JPA2.1
Note: To be precise MyBatis is a “SQL Mapper”, but it is classified as “O/R Mapper” in this guidelines.
Warning: Every project must not adopt JPA. For situations in which table design has been done and “Most
of the tables are not normalized”, “The number of columns in the table is too large” etc, use of JPA is difficult.
Furthermore, this guideline does not explain the basic usage of JPA. Hence, it is pre-requisite to have JPA
experience people in the team.
38 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
View
Security
Tip: In addition to providing a mechanism of authentication and authorization in Spring Security 3.2, the mech-
anism has been enhanced to protect a Web application from malicious attackers.
• CSRF Countermeasures
Validation
Logging
Common Library
• https://github.com/terasolunaorg/terasoluna-gfw
dependencies resolved and OSS version to be used in the TERASOLUNA Server Framework for Java (5.x) is
following the rule of Spring IO platform definition.
40 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
3. Libraries which are not dependent on Common Library, but recommended in case of application
development using TERASOLUNA Server Framework for Java (5.x).
4. Libraries that are supported by Spring IO platform, but library that relies individually.
42 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
5. Library versions that are applied in the Spring IO platform is a Beta or RC (Release Candidate)
(Library that explicitly specify the GA version at TERASOLUNA Server Framework for Java (5.x))
Common Library includes + alpha functionalities which are not available in Spring Ecosystem or other depen-
dent libraries included in TERASOLUNA Server Framework for Java (5.x). Basically, application development
is possible using TERASOLUNA Server Framework for Java (5.x) without this library but “convenient to have”
kind of existence. With the default settings, provided two blank projects, Blank project of multi-project and Blank
project of single-project, contains built-in Common Library as shown in the following listing.
3. With the default settings of Common Library, when MyBatis3 is used for data access.
4. With the default settings of Common Library, when JPA is used for data access.
The project which does not contain the Java source code, only defines library dependencies.
Note: Excluding exceptions, a project assigned with “dependencies” at the end of project name exists in common
library. (For example, terasoluna-gfw-common-dependencies etc corresponding to terasoluna-gfw-common)
In these projects, since a dependency definition for OSS library recommended for use is provided along with
dependency definition for common library, it is recommended to add it to pom.xml as a dependency, for the
project assigned with “dependencies” while using a common library.
terasoluna-gfw-common
terasoluna-gfw-string
terasoluna-gfw-codepoints
terasoluna-gfw-validator
46 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
terasoluna-gfw-jodatime
terasoluna-gfw-web
terasoluna-gfw-web-jsp
48 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
terasoluna-gfw-security-web
Spring’s web MVC framework is, like many other web MVC frameworks, request-driven, designed around
a central Servlet that dispatches requests to controllers and offers other functionality that facilitates the
development of web applications. Spring’s DispatcherServlet however, does more than just that. It is
completely integrated with the Spring IoC container and as such allows you to use every other feature that
Spring has.
The processing flow of Spring MVC from receiving the request till the response is returned is shown in the
following diagram.
50 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
5. Controller executes the business logic, sets the processing result in Model and returns the logical
name of view to HandlerAdapter.
6. DispatcherServlet dispatches the task of resolving the View corresponding to the View name to
ViewResolver. ViewResolver returns the View mapped to View name.
Among the components explained previously, the extendable components are implemented.
Implementation of HandlerMapping
Usually org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
is used. This class reads @RequestMapping annotation from the Controller and uses the method of
Controller that matches with URL as Handler class.
Implementation of HandlerAdapter
Usually org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
is used. RequestMappingHandlerAdapter class calls the method of handler class (Controller)
selected by HandlerMapping. In Spring 3.1, this class is also configured by default throught
<mvc:annotation-driven>.
Implementation of ViewResolver
Classes that implement ViewResolver provided by Spring framework and dependent libraries are shown below.
• org.springframework.web.servlet.view.InternalResourceViewResolver is used,
• org.springframework.web.servlet.view.tiles3.TilesViewResolver
• org.springframework.web.servlet.view.BeanNameViewResolver
Thereby, it is required to use different viewResolver based on the type of the View that needs to be returned.
When View of multiple types is to be handled, multiple definitions of ViewResolver are required.
52 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
A typical example of using multiple ViewResolver is the screen application for which file download process
exists.
For screen (JSP), View is resolved using InternalResourceViewResolver and for File download View
is resolved using BeanNameViewResolver.
For details, refer File Download.
Implementation of View
Classes that implement View provided by Spring framework and its dependent libraries are shown below.
View changes with the type of response to be returned. When JSP is to be returned,
org.springframework.web.servlet.view.JstlView is used. When View not provided by
Spring framework and its dependent libraries are to be handled, it is necessary to extend the class in which View
interface is implemented. For details, refer File Download.
54 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Before entering into the advanced usage of Spring MVC, it is better to understand Spring MVC by actually trying
hands-on web application development using Spring MVC.
2.3.1 Prerequisites
The description of this chapter has been verified on the following environment. (For other environments, replace
the contents mentioned here appropriately)
Classification Product
OS Windows 7
JVM Java 1.8
IDE Spring Tool Suite 3.6.4.RELEASE (Hereafter referred as [STS])
Build Tool Apache Maven 3.3.9 (Hereafter referred as [Maven])
Application Server Pivotal tc Server Developer Edition v3.1 (enclosed in STS)
Web Browser Google Chrome 46.0.2490.80 m
Note: To connect the internet via proxy server, STS Proxy settings and Maven Proxy settings are required for the
following operations.
Select the archetype created project from STS menu [File] -> [Import] -> [Maven] -> [Existing Maven Projects]
-> [Next].
Click on [Finish] by selecting C:\work\helloworld in Root Directory and selecting pom.xml of helloworld
in Projects.
To understand the configuration of Spring MVC, the generated Spring MVC configuration file
(src/main/resources/META-INF/spring/spring-mvc.xml) is described briefly.
56 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/s
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-u
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/sp
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop
<context:property-placeholder
location="classpath*:/META-INF/spring/*.properties" />
<!-- (1) Enables the Spring MVC @Controller programming model -->
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean
class="org.springframework.data.web.PageableHandlerMethodArgumentResolver" />
<bean
class="org.springframework.security.web.method.annotation.AuthenticationPrincipalA
</mvc:argument-resolvers>
</mvc:annotation-driven>
<mvc:default-servlet-handler />
<mvc:resources mapping="/resources/**"
location="/resources/,classpath:META-INF/resources/"
cache-period="#{60 * 60}" />
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/resources/**" />
<mvc:exclude-mapping path="/**/*.html" />
<bean
class="org.terasoluna.gfw.web.logging.TraceLoggingInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/resources/**" />
<mvc:exclude-mapping path="/**/*.html" />
<bean
class="org.terasoluna.gfw.web.token.transaction.TransactionTokenInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/resources/**" />
<mvc:exclude-mapping path="/**/*.html" />
<bean class="org.terasoluna.gfw.web.codelist.CodeListInterceptor">
<property name="codeListIdPattern" value="CL_.+" />
</bean>
</mvc:interceptor>
58 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
<!-- (3) Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-I
<!-- Settings View Resolver. -->
<mvc:view-resolvers>
<mvc:jsp prefix="/WEB-INF/views/" />
</mvc:view-resolvers>
<bean id="requestDataValueProcessor"
class="org.terasoluna.gfw.web.mvc.support.CompositeRequestDataValueProcessor">
<constructor-arg>
<util:list>
<bean class="org.springframework.security.web.servlet.support.csrf.CsrfRequestData
<bean
class="org.terasoluna.gfw.web.token.transaction.TransactionTokenRequestDataVal
</util:list>
</constructor-arg>
</bean>
</beans>
Define the package which will be target of searching components used in Spring MVC.
(2)
(3)
Tip: <mvc:view-resolvers> element is a XML element that added from Spring Framework
4.1. By using <mvc:view-resolvers> element, it is possible to define ViewResolver simply.
The definition example of using the conventional <bean> element is shown below.
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResol
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
package com.example.helloworld.app.welcome;
import java.text.DateFormat;
import java.util.Date;
60 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller // (4)
public class HomeController {
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = {RequestMethod.GET, RequestMethod.POST}) // (5)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
It gets executed when the HTTP method is GET or POST and the Resource is (or request URL) is “/”.
(5)
(6)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet"
href="${pageContext.request.contextPath}/resources/app/css/styles.css">
</head>
<body>
<div id="wrapper">
<h1>Hello world!</h1>
<p>The time on the server is ${serverTime}.</p> <%-- (8) --%>
</div>
</body>
</html>
62 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Right click “helloworld” project in STS, and start helloworld project by executing “Run As” -> “Run On Server”
-> “localhost” -> “Pivotal tc Server Developer Edition v3.0” -> “Finish”.
Enter “http://localhost:8080/helloworld/” in browser to display the following screen.
Note: Since tc Server internally uses the Tomcat, it is possible to choose below two versions in the STS.
• tomcat-7-0.57.A.RELEASE
If you want to switch the Tomcat to be used, change the [Version] field of ts Server by opening the [Edit Server
Runtime Environment] dialog box. Java (JRE) version can also be changed from this dialog box.
Lets go ahead and create a simple application. It is a typical eco application in which message will be displayed if
name is entered in the text field as given below.
64 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
package com.example.helloworld.app.echo;
import java.io.Serializable;
Create a Controller
package com.example.helloworld.app.echo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("echo")
public class EchoController {
@ModelAttribute // (1)
public EchoForm setUpEchoForm() {
EchoForm form = new EchoForm();
return form;
}
@RequestMapping // (2)
public String index(Model model) {
return "echo/index"; // (3)
}
66 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
(1) Add @ModelAttribute annotation to the method. Return value of such a method is automatically
added to the Model.
Attribute name of the Model can be specified in @ModelAttribute, but the class name with the
first letter in lower case is the default attribute name. In this case it will be “echoForm”. This attribute
name must match with the value of modelAttribute of form:form tag.
(2) When nothing is specified in value attribute of @RequestMapping annotation at the method
level, it is mapped to @RequestMapping added at class level. In this case, index method is
called, if “<contextPath>/echo” is accessed.
When nothing is set in method attribute, mapping is done for any HTTP method.
(4) Since “hello” is specified in value attribute and RequestMethod.POST is specified in method
attribute of the @RequestMapping annotation method, if “<contextPath>/echo/hello” is accessed
with POST method, hello method is called.
Note: The value specified in the method attribute of @RequestMapping annotation is generally varied by
how the data transmitted from the client.
• POST method in case of storing data to the server (in case of updating process).
• GET method or unspecified (any method) in case of not storing data to the server (in case of referring
process).
In echo application,
• index method is not going to save data to the server, it is unspecified (any method)
• hello method is going to save data into Model object, it is POST method
Finally create JSP for input screen and output screen. Each file path must match with View name as follows.
<!DOCTYPE html>
<html>
<head>
<title>Echo Application</title>
</head>
<body>
<%-- (1) --%>
<form:form modelAttribute="echoForm" action="${pageContext.request.contextPath}/echo/hello">
<form:label path="name">Input Your Name:</form:label>
<form:input path="name" />
<input type="submit" />
</form:form>
</body>
</html>
(1) HTML form is constructed using tag library. Specify the name of form object created by Controller in
modelAttribute attribute.
Refer here for tag library.
<!DOCTYPE html>
<html>
<head>
<title>Echo Application</title>
</head>
<body>
68 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
<!DOCTYPE html>
<html>
<head>
<title>Echo Application</title>
</head>
<body>
<p>
Hello <c:out value="${name}" /> <%-- (2) --%>
</p>
</body>
</html>
(2) Output “name” passed from Controller. Take countermeasures for XSS using c:out tag.
Note: Here, XSS countermeasure taken using c:out standard tag, however, f:h() function provided in com-
mon library can be used easily. Refer to XSS Countermeasures for details.
Till this point Input validation is not implemented in this application. In Spring MVC, Bean Validation and
annotation based input validation can be easily implemented. For example name input validation is performed in
Eco Application.
package com.example.helloworld.app.echo;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@NotNull // (1)
@Size(min = 1, max = 5) // (2)
private String name;
(1) Whether name parameter exists in HTTP request or not can be checked by @NotNull annotation.
(2) Whether the size of name is more than or equal to 1 and less than or equal to 5 can be checked by
@Size(min = 1, max = 5).
Implement the input check and the error handling when an error occurs in the input check.
70 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
package com.example.helloworld.app.echo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("echo")
public class EchoController {
@ModelAttribute
public EchoForm setUpEchoForm() {
EchoForm form = new EchoForm();
return form;
}
@RequestMapping
public String index(Model model) {
return "echo/index";
}
(1) In Controller, add @Validated annotation to the argument on which validation needs to be
executed. Also add BindingResult object to the arguments.
Input validation is automatically performed using Bean Validation and the result is stored in
BindingResult object.
(2) Error can be checked by executing hasErrors method. If there is an input error, it returns View
name to display the input screen.
Add the implementation of displaying input error message on input screen (src/main/webapp/WEB-
INF/views/echo/index.jsp).
<!DOCTYPE html>
<html>
<head>
<title>Echo Application</title>
</head>
<body>
<form:form modelAttribute="echoForm" action="${pageContext.request.contextPath}/echo/hello">
<form:label path="name">Input Your Name:</form:label>
<form:input path="name" />
<form:errors path="name" cssStyle="color:red" /><%-- (1) --%>
<input type="submit" />
</form:form>
</body>
</html>
(1) Add form:errors tag for displaying error message when an error occurs on input screen.
<!DOCTYPE html>
<html>
<head>
<title>Echo Application</title>
</head>
<body>
<form id="echoForm" action="/helloworld/echo/hello" method="post">
<label for="name">Input Your Name:</label>
<input id="name" name="name" type="text" value=""/>
72 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Summary
If above points are still not understood, it is recommended to read this chapter again and start again from building
the environment.
74 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
• Application layer
• Domain layer
• Infrastructure layer
Both application layer and infrastructure layer depend on domain layer, however domain layer should not depend
on other layers. There can be changes in the application layer with the changes in domain layer, However, there
should be no changes in domain layer with the changes in application layer.
Note: Application layer, domain layer, infrastructure layer are the terms explained in “Domain-Driven Design
(2004, Addison-Wesley)” of Eric Evans. However, though the terms are used, they are not necessarily as per the
concept used in Domain Driven Design.
Flow of data from input to output is Application layer → Domain layer → Infrastructure layer, hence explanation
is given in this sequence.
Application layer
• Calls components in the domain layer corresponding to the request from clients.
This layer should be as thin as possible and should not contain any business rules.
Controller
• Controls the screen flow (mapping the request and returning the View corresponding to the result)
• Calls services in the domain layer (executing main logic corresponding to the request)
In Spring MVC, POJO class having @Controller annotation becomes the Controller class.
Note: When storing the client input data in the session, controllers also have a role to control the life cycle of
data to be stored in the session.
View
View’s responsibilities is generating output (including the UI provision) to the client. Returns output results in
various formats such as HTML/PDF/Excel/JSON.
Tip: If use the JSON or XML format output results for the REST API or Ajax request,
HttpMessageConverter class plays View’s responsibility.
76 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Form
• Represents the form of HTML. (sending form information to the Controller, or outputting the processing
result data to form of HTML)
In Spring MVC, form object are the POJO class that store request parameters. It is called as form backing bean.
Note: To retain the domain layer independent from the application layer, need to perform following processing
at the application layer.
If conversion code is too many, recommend to perform of either or both of the following measures and keep
Controller’s source code in simple state.
Tip: If use the JSON or XML as input data for REST API or Ajax request, Resource class plays Form’s
responsibility. Also, HttpMessageConverter class plays responsibility to convert JSON or XML input data
into Resource class.
Helper
Note: The main duty of Controller is routing (URL mapping and sepcifying the destination). If there is any
processing required (converting JavaBean etc), then that part must be cut-off from controller and must be delegated
to helper classes.
The existance of Helper class improves the visibility of Controller; hence, it is OK to think as a part of Controller.
(Similar to private methods of Controller)
Domain layer
Domain layer is the core of the application, and execute business rules.
• DomainObject
• Checking business rules corresponding to the DomainObject (like whether there is sufficient balance when
crediting amount into the account)
• Executing business rules for the DomainObject (reflects values corresponding to business rules)
DomainObject
DomainObject is a model that represents resources required for business and items generated in the business
process.
• Resource models such as Employee, Customer, Product etc. (generally indicated by a noun)
Domain Object is the Entity that represents an object which indicates 1 record of database table.
Note: Mainly Models holding state only are handled in this guideline.
In “Patterns of Enterprise Application Architecture (2002, Addison-Wesley)” of Martin Fowler, Domain Model is
defined as Item having state and behavior. We will not be touching such models in detail.
78 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
In this guideline, Rich domain model proposed by Eric Evans is also not included. However, it is mentioned here
for classification.
Repository
It can be thought of as a collection of DomainObjects and is responsible for CRUD operations such as create, read,
update and delete of DomainObject.
It is implemented by RepositoryImpl of infrastructure layer. Hence, in this layer, there is no information about
how data access is implemented.
Service
In this guideline, it is recommended to draw the transaction boundary at the method of Service.
Note: Information related to Web such as Form and HttpRequest should not be handled in service.
This information should be converted into domain object in Application layer before calling the Service.
Infrastructure layer
It is responsible for storing the data permanently (location where data is stored such as RDBMS, NoSQL etc.) as
well as transmission of messages.
RepositoryImpl
Represents implementation of Repository interface of domain layer. It covers life cycle management of Domain-
Object.
With the help of this structure, it is possible to hide implementation details from domain layer.
This layer also can be the transaction boundary depending on the requirements.
Tip: When using MyBatis3 or Spring Data JPA, most RepositoryImpls are created automatically.
O/R Mapper
Note: It is more correct to classify MyBatis and Spring JDBC as “SQL Mapper” and not “O/R Mapper”; however,
in this guideline it is treated as “O/R Mapper”.
It integrates a data store other than the database( such as messaging system, Key-Value-Store, Web service, existing
legacy system, external system etc.).
80 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
As explained earlier, domain layer is the core of the application, and application layer and infrastructure layer are
dependent on it.
Even if there is change in implementation technology, the differences can be absorbed in respective layers and
there should not be any impact on domain layer. Coupling between layers is done by using interfaces and hence
they can be made independent of implementation technology being used in each layer.
82 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
No. Description
Controller receives the Request.
1.
(Optional) Controller calls Helper and converts the Form information to DomainObject or DTO.
2.
Service returns DomainObject or DTO which is the result of business logic execution to Controller.
7.
(Optional) Controller calls the Helper and converts DomainObject or DTO to Form.
8.
Please refer to the below table to determine whether it is OK to call a component from another component.
Controller
Service
Repository
If services that can be used even from other services are required, SharedService should be created in order to
clarify such a possibility. Refer to Domain Layer Implementation for the details.
Note: It may be difficult to follow the above rules at the initial phase of application development. If looking at
a very small application, it can be created quickly by directly calling the Repository from Controller. However,
when rules are not followed, there will be many maintainability issues when development scope becoms larger.
It may be difficult to understand the impact of modifications and to add common logic which is cross-cutting in
nature. It is strongly recommended to pay attention to dependency from the beginning of development so that
there will be no problem later on.
Hiding the implementation details and sharing of data access logic are the merits of creating a Repository.
However, it is difficult to share data access logic depending on the project team structure (multiple companies may
separately implement business processes and control on sharing may be difficult etc.) In such cases, if abstraction
of data access is not required, O/R Mapper can be called directly from Service as shown in the following diagram,
without creating the repository.
84 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Controller
Service
Here, recommended project structure is explained when application layering is done as described earlier.
Basically, it is recommended to create the multiple projects with the following configuration.
Note: Classes of infrastructure layer such as RepositoryImpl are also included in project-domain. Originally,
[projectName]-infra project should be created separately; however, normally there is no need to conceal the im-
plementation details in the infra project and development becomes easy if implementation is also stored in domain
project. Moreover, when required, [projectName]-infra project can be created.
Tip: For the example of multi-project structure, refer to Sample Application or Test Application of Common
Library.
[projectName]-domain
[projectName]-domain
└ src
└ main
├ java
| └ com
| └ example
| └ domain ...(1)
| ├ model ...(2)
| | ├ Xxx.java
| | ├ Yyy.java
| | └ Zzz.java
| ├ repository ...(3)
| | ├ xxx
| | | └ XxxRepository.java
| | ├ yyy
| | | └ YyyRepository.java
| | └ zzz
| | ├ ZzzRepository.java
| | └ ZzzRepositoryImpl.java
| └ service ...(4)
| ├ aaa
| | ├ AaaService.java
| | └ AaaServiceImpl.java
| └ bbb
| ├ BbbService.java
| └ BbbServiceImpl.java
└ resources
└ META-INF
└ spring
├ [projectName]-codelist.xml ...(5)
├ [projectName]-domain.xml ...(6)
└ [projectName]-infra.xml ...(7)
86 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
No. Details
Package to store configuration elements of domain layer.
(1)
(2)
(5)
(6)
(7)
[projectName]-web
[projectName]-web
└ src
└ main
├ java
| └ com
| └ example
| └ app ...(1)
| ├ abc
| | ├ AbcController.java
| | ├ AbcForm.java
| | └ AbcHelper.java
| └ def
| ├ DefController.java
| ├ DefForm.java
| └ DefOutput.java
├ resources
| ├ META-INF
| | └ spring
| | ├ applicationContext.xml ...(2)
| | ├ application.properties ...(3)
| | ├ spring-mvc.xml ...(4)
| | └ spring-security.xml ...(5)
| └ i18n
| └ application-messages.properties ...(6)
└ webapp
├ resources ...(7)
└ WEB-INF
├ views ...(8)
| ├ abc
| | ├ list.jsp
| | └ createForm.jsp
| └ def
| ├ list.jsp
| └ createForm.jsp
└ web.xml ...(9)
88 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
No, Details
Package to store configuration elements of application layer.
(1)
(2)
(3)
(4)
(5)
Define the messages and other contents to be used for screen display (internationalization).
(6)
(7)
(8)
(9)
[projectName]-env
[projectName]-env
├ configs ...(1)
| └ [envName] ...(2)
| └ resources ...(3)
└ src
└ main
└ resources ...(4)
├ META-INF
| └ spring
| ├ [projectName]-env.xml ...(5)
| └ [projectName]-infra.properties ...(6)
├ dozer.properties
├ log4jdbc.properties
└ logback.xml ...(7)
No. Details
Directory to define configurations depends on the environment for all environments.
(1)
(4)
Bean definitions that depend on the local development environment (like dataSource etc).
(5)
(6)
(7)
Note: The purpose of separating [projectName]-domain and [projectName]-web into different projects is to
prevent reverse dependency among them.
It is natural that [projectName]-web uses [projectName]-domain; however, [projectName]-domain must not refer
projectname]-web.
90 2 Architecture Overview
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
If configruation elements of both, [projectName]-web and [projectName]-domain, are kept in a single project, it
may lead to an incorrect reference by mistake. It is strongly recommended to prevent reference to [projectName]-
web from [projectName]-domain by separating the project and setting order of reference.
Note: The reason for creating [projectName]-env is to separate the information depending on the environment
and thereby enable switching of environment.
For example, set local development environment by default and at the time of building the application, create war
file without including [projectName]-env. By creating a separate jar for integration test environment or system test
environment, deployment becomes possible just by replacing the jar of corresponding environment.
Further, it is also possible to minimize the changes in case of project where RDBMS being used changes.
If the above point is not considered, contents of configuration files have to changed according to the target envi-
ronment and the project has to be re-build.
Application Development
Description of various rules as well as recommended implementation on the use of TERASOLUNA Server Frame-
work for Java (5.x).
In this guideline, it is recommended to adopt multi-project configuration. For description of the recommended
multi-project configuration, refer [Project structure].
Type Usage
This should be used when developing a real application that is to be released in commercial
environment.
Blank project of
Following types of project templates are provided as Archetype of Maven.
multi-project structure
• Template that includes settings for MyBatis3
• Template that includes settings for JPA (Spring Data JPA)
This guideline recommends using a project having multi-project structure.
This should be used when creating simple applications such as POC (Proof Of Concept),
prototype, sample, etc.
Blank project of
Following types of project templates are provided as Archetype of Maven. (Projects for
single-project
Eclipse WTP are also provided; however, their description is omitted in this chapter)
structure
• Template that includes settings for MyBatis3
• Template that includes settings for JPA (Spring Data JPA)
• Template that does not depend on O/R Mapper
This guideline follows a procedure wherein various tutorials are performed using a single
project.
The multi-project structured development project will be created using the archetype:generate of the Maven
Archetype Plugin .
Note: Prerequisite
are prerequisites.
If prerequisite conditions are not satisfied, it is necessary to perform these setups first.
94 3 Application Development
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
cd C:\work
Parameter Description
batch mode (skips interaction)
-B
-DarchetypeCatalog
-DarchetypeGroupId
Specify archetypeId (ID to identify the template) of the blank project. (Customiza-
tion required)
-DarchetypeArtifactId
specify one of the following archetypeId.
• terasoluna-gfw-multi-web-blank-mybatis3-archetype
• terasoluna-gfw-multi-web-blank-jpa-archetype
In above example, terasoluna-gfw-multi-web-blank-mybatis3-archetype
is specified.
Specify version of the blank project.(Fixed)
-DarchetypeVersion
Specify groupId of the project that you want to create. (Customization required)
In above example, "com.example.todo" is specified.
-DgroupId
Specify artifactId of the project that you want to create. (Customization required)
In above example, "todo" is specified.
-DartifactId
Specify version of the project that you want to create. (Customization required)
In above example, "1.0.0-SNAPSHOT" is specified.
-Dversion
If the project creation successes, following type of log will be printed. (The following output is an example when
project is created using the MyBatis3 Archetype)
(... omit)
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: terasoluna-gfw-multi-web-bl
96 3 Application Development
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.example.todo
[INFO] Parameter: artifactId, Value: todo
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: com.example.todo
[INFO] Parameter: packageInPathFormat, Value: com/example/todo
[INFO] Parameter: package, Value: com.example.todo
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.example.todo
[INFO] Parameter: artifactId, Value: todo
[INFO] Parent element not overwritten in C:\work\todo\todo-env\pom.xml
[INFO] Parent element not overwritten in C:\work\todo\todo-domain\pom.xml
[INFO] Parent element not overwritten in C:\work\todo\todo-web\pom.xml
[INFO] Parent element not overwritten in C:\work\todo\todo-initdb\pom.xml
[INFO] Parent element not overwritten in C:\work\todo\todo-selenium\pom.xml
[INFO] project created from Archetype in dir: C:\work\todo
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.929 s
[INFO] Finished at: 2015-07-31T12:03:21+00:00
[INFO] Final Memory: 10M/26M
[INFO] ------------------------------------------------------------------------
If the project creation successes, Maven multi-project gets created. For detail description of the project that you
have created in the Maven Archetype, Refer [Structure of the development project].
todo
├-- pom.xml
├-- todo-domain
├-- todo-env
├-- todo-initdb
├-- todo-selenium
└-- todo-web
Depending upon the application, there are several locations where customization is required in the Maven
Archetype created project.
• Message wording
• Error screen
• DataSource configuration
• Settings of Authentication・Authorization
• Definition of Logging
For these customizations, Refer to “How to use” of each section and customize if required.
Note: Part that is expressed as artifactId in the following description needs to be read by replacing the
artifactId which is specified at the time of creating a project.
98 3 Application Development
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
such information set in Archetype projects. The actual settings contents indicated below.
<name>TERASOLUNA Server Framework for Java (5.x) Web Blank Multi Project</name>
<description>Web Blank Multi Project using TERASOLUNA Server Framework for Java (5.x)</description
<url>http://terasoluna.org</url>
<inceptionYear>2014</inceptionYear>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>manual</distribution>
</license>
</licenses>
<organization>
<name>TERASOLUNA Framework Team</name>
<url>http://terasoluna.org</url>
</organization>
In the Maven Archetype created project, the x.xx.fw.9999 format message ID used at the time of,
[application-messages.properties]
[JSP]
<div class="error">
<c:if test="${!empty exceptionCode}">[${f:h(exceptionCode)}]</c:if>
<spring:message code="e.xx.fw.5001" />
</div>
[applicationContext.xml]
<bean id="exceptionCodeResolver"
class="org.terasoluna.gfw.common.exception.SimpleMappingExceptionCodeResolver">
<!-- ... -->
<entry key="ResourceNotFoundException" value="e.xx.fw.5001" />
<!-- ... -->
</bean>
The x.xx.fw.9999 format message ID is a message ID system that is introduced in [Message Management] of
this guideline but, the value of the project division is in the state of provisional value [xx].
Note:
• If the message ID system introduced in this guideline is used, specify the appropriate values to the
project classification. For the message ID system introduced in this guideline, Refer [Result messages].
• If the message ID system introduced in this guideline is not used, it is necessary to replace all the message
IDs those are used in the customization targeted file indicated below.
Message wording
In the Maven Archetype created project, number of message definitions are provided but, message wordings are
simple messages. Actual messages (sampling) are indicated below.
[application-messages.properties]
# ...
# typemismatch
typeMismatch="{0}" is invalid.
# ...
Note: Modify the message wording depending upon the application requirements (such as message terms)
Error screen
In the Maven Archetype created project, JSP and HTML are provided for displaying an error screen for every kind
of errors but,
• screen layout
• screen title
etc are simple implementation. Actual JSP implementation (sampling) is indicated below.
[JSP]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Resource Not Found Error!</title>
<link rel="stylesheet"
href="${pageContext.request.contextPath}/resources/app/css/styles.css">
</head>
<body>
<div id="wrapper">
<h1>Resource Not Found Error!</h1>
<div class="error">
<c:if test="${!empty exceptionCode}">[${f:h(exceptionCode)}]</c:if>
<spring:message code="e.xx.fw.5001" />
</div>
<t:messagesPanel />
<br>
<!-- ... -->
<br>
</div>
</body>
</html>
Note: Modify the JSP and HTML depending upon the application requirements (such as UI terms) used
for displaying an error screen.
In the Maven Archetype created project, screen layouts are configured using Tiles but, the copyright of the screen
footer portion is in a state of provisional value [Copyright © 20XX CompanyName]. Actual JSP
implementation (sampling) is indicated below.
[template.jsp]
<div class="container">
<tiles:insertAttribute name="header" />
<tiles:insertAttribute name="body" />
<hr>
<p style="text-align: center; background: #e5eCf9;">Copyright
© 20XX CompanyName</p>
</div>
Note: If screen layouts are configured using Tiles, specify appropriate value to the copyright.
In the Maven Archetype created project, in-memory database (H2 Database) setting is configured but, these set-
tings are done for the small operation (Prototyping and POC (Proof Of Concept)) verification. Therefore, these
could be unnecessary settings while having regular application development.
[artifactId-env.xml]
<jdbc:initialize-database data-source="dataSource"
ignore-failures="ALL">
<jdbc:script location="classpath:/database/${database}-schema.sql" />
<jdbc:script location="classpath:/database/${database}-dataload.sql" />
</jdbc:initialize-database>
└-- src
└-- main
└-- resources
├-- META-INF
(...)
├-- database
| ├-- H2-dataload.sql
| └-- H2-schema.sql
Note: While having regular application development, remove the directory which is maintained for defini-
tion and SQL files for setting up a In-memory database (H2 Database)
DataSource configuration
In the Maven Archetype created project, DataSource setting is done for accessing in-memory database (H2
Database) but, these settings are done for the small operation (Prototyping and POC (Proof Of Concept)) veri-
fication. Therefore it is necessary to change the DataSource settings for accessing the actual running database
application while having regular application development.
[artifactId/artifactId-domain/pom.xml]
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
[artifactId-infra.properties]
database=H2
database.url=jdbc:h2:mem:todo;DB_CLOSE_DELAY=-1
database.username=sa
database.password=
database.driverClassName=org.h2.Driver
# connection pool
cp.maxActive=96
cp.maxIdle=16
cp.minIdle=0
cp.maxWait=60000
[artifactId-env.xml]
Note: Change the DataSource settings for accessing the actual running database application while having
regular application development.
In the Maven Archetype created project, the use of Apache Commons DBCP is configured but, there are many
cases that adopting a method of accessing a DataSource via JNDI (Java Naming and Directory Interface) by use
of DataSource provided by the application server.
Again there are some cases where Apache Commons DBCP is used on development environment and DataSource
provided by the application server is used on test as well as production environment.
For how to set-up the DataSource, Refer [Datasource settings of Database Access (Common)].
Bean definition file for defining environment de- If DataSource provided by the application server
3.
pendent components is used, change the configuration to use the Data-
Source that is obtained via JNDI.
artifactId/artifactId-env/src/main/resources/META-INF/spring/artifactId-env.x
For how to set-up the DataSource, Refer [Data-
source settings of Database Access (Common)].
Note: About the database property of the property file for defining environment dependent setting
The database property is unnecessary property if MyBatis is used as O/R Mapper. You may remove this but
you may leave the settings in order to specify the database being used.
It is fine to remove the comment out of POM file in case of PostgreSQL or Oracle database is used.. Modify the
JDBC driver version by actual use of the corresponding database version.
However, if Oracle is used, it is necessary to install the Oracle JDBC driver in the local repository of Maven before
removing the comment.
• artifactId/pom.xml
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<!-- <dependency> -->
<!-- <groupId>com.oracle</groupId> -->
<!-- <artifactId>ojdbc7</artifactId> -->
<!-- <version>${ojdbc.version}</version> -->
<!-- </dependency> -->
<postgresql.version>9.3-1102-jdbc41</postgresql.version>
<ojdbc.version>12.1.0.2</ojdbc.version>
• artifactId/artifactId-domain/pom.xml
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>provided</scope> -->
</dependency> -->
<!-- <dependency> -->
<!-- <groupId>com.oracle</groupId> -->
<!-- <artifactId>ojdbc7</artifactId> -->
<!-- <scope>provided</scope> -->
<!-- </dependency> -->
• Project structure that takes into account the exclusion of environmental dependency introduced in this
guideline
In addition, various settings have been included that is recommended in this guideline
and, as a simple component implementation of low (=necessary to develop every kind of application) dependency
on the application requirements,
In the Maven Archetype created project, the recommended settings are done which are required for building a
traditional Web application (application that receives the request parameters and respond the HTML).
Therefore, unnecessary setting exists in building a REST API for handling JSON or XML. If you want to create
a project for building REST API, need to apply the REST API related settings by referring to the [Application
settings of RESTful Web Service].
Note: Part that is expressed as artifactId in the following description needs to be read by replacing the
artifactId which is specified at the time of creating a project.
Multi-project structure
artifactId
├-- pom.xml ... (1)
├-- artifactId-web ... (2)
├-- artifactId-domain ... (3)
├-- artifactId-env ... (4)
├-- artifactId-initdb ... (5)
└-- artifactId-selenium ... (6)
The entire multi-project configuration is defined in POM (Project Object Model) file.
Mainly following definitions are done in this file.
(1)
• Version of the dependent libraries
• Build plug-ins settings (setting of how to build)
Refer [Hierarchical structure of the project] for the hierarchical relationship of multi-project.
Module that manages the application layer (Web layer) components.
Mainly following components and files are managed in this module.
(2)
• Controller class
• Validator class for relational check
• Form class (the Resource class in case of REST API)
• View (JSP)
• CSS file
• JavaScript file
• JUnit for the application layer components
• Bean definition file for defining the application layer components
• Web application configuration file (web.xml)
• Message definition file
The project created in Maven Archetype is the exact multi-module structured project.
This is supplement that the multi-module and multi-project is being used as the same meaning in this guideline.
Note: Development projects required for two Web applications and one common library
• bar-parent
• bar-initdb
• bar-common
• bar-common-web
• bar-domain-a
• bar-domain-b
• bar-web-a
• bar-web-b
• bar-env
• bar-web-a-selenium
• bar-web-b-selenium
• bar-parent
Project called as a parent-pom (parent POM). A simple project consisting of only pom.xml file. It never
contains other source code or configuration files. Common setting information specified in the parent POM
can be reflected in other project by specifying this bar-parent project into <parent> tag.
• bar-initdb
Stores RDBMS table definitions (DDL) and SQL statements for INSERT the initial data. This also managed
as a maven project. By defining sql-maven-plugin in pom.xml, it is possible to automate the execution of
DDL statements and initial data INSERT statements for any RDBMS in the course of the build lifecycle.
• bar-common
Stores common library in the project. Web related classes are placed in the bar-common-web by making it
as a web-independent.
• bar-common-web
• bar-domain-a
Stores unit test cases and domain layer java classes related to “a” domain. Finally *.jar file is created.
• bar-domain-b
• bar-web-a
Stores application layer java classes, jsps, configuration files, unit test cases. Finally created *.war file is
created as the Web application. bar-web-a having dependency on bar-common and bar-env.
• bar-web-b
This is a Web application as one more subsystem. Structure is the same as the bar-web-a.
• bar-env
• bar-web-a-selenium
Project that stores test cases using Selenium WebDriver for web-a project.
• bar-web-b-selenium
Project that stores test cases using Selenium WebDriver for web-b project.
Module that manages the application layer (Web layer) components are explained.
artifactId-web
├-- pom.xml ... (1)
The web module configuration is defined in POM (Project Object Model) file. Following definitions
are done in this file.
(1)
• Definition of dependent libraries and build plug-ins
• Definition to create a war file
Note: About the module name of the web module while creating a project for REST API
The application type can be easily distinguished, if the module name is assigned the name of artifactId-api
while building a REST API.
└-- src
├-- main
| ├-- java
| | └-- com
| | └-- example
| | └-- project
| | └-- app ... (2)
| | └-- welcome
| | └-- HelloController.java ... (3)
| ├-- resources
| | ├-- META-INF
| | | ├-- dozer ... (4)
| | | └-- spring ... (5)
| | | ├-- application.properties ... (6)
| | | ├-- applicationContext.xml ... (7)
| | | ├-- spring-mvc.xml ... (8)
| | | └-- spring-security.xml ... (9)
| | └-- i18n ... (10)
| | └-- application-messages.properties ... (11)
The controller class for receiving a request to display the Welcome page.
(3)
The directory in which a mapping definition file of Dozer (Bean Mapper) is stored, Refer to [Bean
Mapping (Dozer)] for Dozer.
(4)
It is an empty directory at the time of creation. If the mapping file is required (if high mapping is
required), it gets automatically read in case of stored under this directory.
Directory contains the property file and Spring Framework bean definition file.
(5)
Properties file that defines the settings to be used in the application layer.
It is an empty file at the time of creation.
(6)
(10)
Property file that defines the messages to be used in the application layer.
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Note: Refer [Relationship of bean definition file and application context structure] for the application context
and bean definition file related.
| └-- webapp
| ├-- WEB-INF
| | ├-- tiles ... (12)
| | | └-- tiles-definitions.xml
| | ├-- views ... (13)
| | | ├-- common
| | | | ├-- error ... (14)
| | | | | ├-- accessDeniedError.jsp
| | | | | ├-- businessError.jsp
| | | | | ├-- dataAccessError.jsp
| | | | | ├-- invalidCsrfTokenError.jsp
| | | | | ├-- missingCsrfTokenError.jsp
| | | | | ├-- resourceNotFoundError.jsp
| | | | | ├-- systemError.jsp
| | | | | ├-- transactionTokenError.jsp
| | | | | └-- unhandledSystemError.html
| | | | └-- include.jsp ... (15)
| | | ├-- layout ... (16)
| | | | ├-- header.jsp
| | | | └-- template.jsp
| | | └-- welcome
| | | └-- home.jsp ... (17)
| | └-- web.xml ... (18)
| └-- resources ... (19)
| └-- app
| └-- css
| └-- styles.css ... (20)
└-- test
├-- java
└-- resources
Directory that contains the Tiles configuration files. Refer [Screen Layout using Tiles] for the Tiles
configuration files.
(12)
(13)
Directory that contains the JSP and HTML for displaying error screens.
At the time of creation, JSPs (HTMLs) are stored corresponding to the errors that may occur during
(14)
application execution.
Note: Error screen JSP and HTML should be modified according to the application require-
ments (Such as UI Terms).
Directory that contains the JSP files for the Tiles layout. Refer [Screen Layout using Tiles] for JSP
files for the Tiles layout.
(16)
(17)
(18)
(20)
artifactId-domain
├-- pom.xml ... (1)
The domain module configuration is defined in POM (Project Object Model) file. Following definitions
are done in this file.
(1)
• Definition of dependent libraries and build plug-ins
• Definition to create a jar file
└-- src
├-- main
| ├-- java
| | └-- com
| | └-- example
| | └-- project
| | └-- domain ... (2)
| | ├-- model
| | ├-- repository
| | └-- service
| └-- resources
| └-- META-INF
| ├-- dozer ... (3)
| └-- spring ... (4)
| ├-- artifactId-codelist.xml ... (5)
| ├-- artifactId-domain.xml ... (6)
| └-- artifactId-infra.xml ... (7)
(2)
The directory in which a mapping definition file of Dozer (Bean Mapper) is stored, Refer to [Bean
Mapping (Dozer)] for Dozer.
(3)
It is an empty directory at the time of creation. If the mapping file is required (if high mapping is
required), it gets automatically read in case of stored under this directory.
Directory contains the property file and Spring Framework bean definition file.
(4)
(5)
└-- test
├-- java
| └-- com
| └-- example
| └-- project
| └-- domain
| ├-- repository
| └-- service
└-- resources
└-- test-context.xml ... (8)
Bean definition file for defining the domain layer unit test components.
(8)
└-- src
├-- main
| ├-- java
(...)
| └-- resources
| ├-- META-INF
| | ├-- dozer
| | ├-- mybatis ... (9)
| | | └-- mybatis-config.xml ... (10)
| | └-- spring
(...)
| └-- com
| └-- example
| └-- project
| └-- domain
| └-- repository ... (11)
| └-- sample
| └-- SampleRepository.xml ... (12)
(9)
(11)
Module that manages the environment dependent configuration files are explained.
artifactId-env
├-- configs ... (1)
| ├-- production-server ... (2)
| | └-- resources
| └-- test-server
| └-- resources
├-- pom.xml ... (3)
The env module configuration is defined in POM (Project Object Model) file. Following definitions
are done in this file.
(3)
• Definition of dependent libraries and build plug-ins
• Definition of Profile to create a jar file for each environment
└-- src
└-- main
└-- resources ... (4)
├-- META-INF
| └-- spring
| ├-- artifactId-env.xml ... (5)
| └-- artifactId-infra.properties ... (6)
├-- database ... (7)
| ├-- H2-dataload.sql
| └-- H2-schema.sql
├-- dozer.properties ... (8)
├-- log4jdbc.properties ... (9)
└-- logback.xml ... (10)
(4)
Directory that contains the SQL to set up an in-memory database (H2 Database).
This directory is prepared while performing small operation verification . Basically remove this di-
(7)
rectory because this directory is not intended to use in the actual application development.
Property file for carrying out the Dozer (Bean Mapper) global settings. For Dozer refer [Bean Mapping
(Dozer)].
(8)
It is an empty file at the time of creation. (The warning log appears at the start-up time if file is not
exist, the empty file is prepared in order to prevent it)
Property file for carrying out the Log4jdbc-remix (library to perform the JDBC-related log output)
global settings. For Log4jdbc-remix, refer [JDBC debug log settings].
(9)
At the time of creation, new line character related setting are specified for those SQLs which are going
to be printed in log.
Configuration file of the Logback (log output). For the log output refer [Logging].
(10)
Module that manages the SQL file to initialize the database is explained.
artifactId-initdb
├-- pom.xml ... (1)
└-- src
└-- main
└-- sqls ... (2)
The initdb module configuration is defined in POM (Project Object Model) file. Following definitions
are done in this file.
(1)
• Definition of build plug-ins (SQL Maven Plugin)
Simple configuration for PostgreSQL is defined at the time of creation.
Directory for storing the database initialization SQL files.
It is an empty directory at the time of creation. For how to create, Refer Sample application of initdb
(2)
project.
mvn sql:execute
Module that manages the E2E (End To End) testing components used in Selenium explained.
artifactId-selenium
├-- pom.xml ... (1)
└-- src
└-- test ... (2)
├-- java
| └-- com
| └-- example
| └-- project
| └-- selenium
| └-- welcome
| └-- HelloTest.java ... (3)
└-- resources
└-- META-INF
└-- spring
├-- selenium.properties ... (4)
└-- seleniumContext.xml ... (5)
The selenium module configuration is defined in POM (Project Object Model) file.
Following definitions are done in this file.
(1)
• Definition of dependent libraries and build plug-ins
• Definition to create a war file
3.1.5 Appendix
The hierarchical structure of the project indicated below which is created in Maven Archetype.
Tip: The configuration has been changed like <dependencyManagement> of Spring IO Platform is imported
from version 5.0.0.RELEASE, we have adopted a style that version management of recommended libraries are
done in Spring IO Platform.
Warning: Since the configuration has been changed like <dependencyManagement> of Spring IO Plat-
form is imported from version 5.0.0.RELEASE, You are no longer able to access the version management
properties from the child project.
Therefore, if property values are referring or overwriting at the child project, pom file should be modified while
upgrading from version 1.0.x.
Furthermore, it is possible to access the conventional version management properties for recommended li-
braries (TERASOLUNA Server Framework for Java (5.x) recommended library) which are not managed by
the Spring IO Platform.
Relationship of bean definition file and structure of the Spring Framework application context (DI container)
indicated below.
Note: About the operation when registered the same components in both application contexts.
If same components are registered in both application context for web application and application context for
DispatcherServlet, injected component will be the registered component in the same application con-
text(Application context for DispatcherServlet) and this point is supplemented here.
In particular, it is necessary to be careful that do not register the domain layer component (such as Service and
Repository) to application context for the DispatcherServlet.
If domain layer components are registered to the application context for the DispatcherServlet, trouble like
the database operations are not committed occurs due to component that performs the transaction control (AOP)
is not enabled.
Furthermore, the settings are done in the project created using Maven Archetype so that the above events don’t
occur. It is necessary to be careful while performing modification or addition of the settings.
Todo
In order to increase the understanding of various settings, planning to add explanation of a configuration file.
• If functional description is explained somewhere, Reference to the functional description will be noted
down.
In “Create development project”, a method to create a development project of multi-project configuration by using
archetype:generate of Maven Archetype Plugin is described. Although Maven is used for the operations in the
online environment, a method is described below for how to use it in offline environment as well.
To continue project development in the offline environment, the files like libraries and plugins necessary for
development must be copied in advance. The operation below should be performed in online environment.
Move to root directory of development project. Here, the project created using “Create development project” is
used for the explanation.
cd C:\work\todo
Copy the files like libraries and plugins necessary for project development. Files are copied by executing
dependency:go-offline of Maven Archetype Plugin.
Parameter Description
Specify copy destination. A new destination is created if a copy destination does not
exist. At present, copy destination is specified as a repository.
–Dmaven.repo.local
Create a war file or a jar file in order to facilitate the distribution of deliverables. At that time, files like libraries
and plugins necessary for build are copied.
(... omit)
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] TERASOLUNA Server Framework for Java (5.x) Web Blank Multi Project (MyBa
tis3) SUCCESS [ 0.006 s]
[INFO] todo-env ........................................... SUCCESS [ 46.565 s]
[INFO] todo-domain ........................................ SUCCESS [ 0.684 s]
[INFO] todo-web ........................................... SUCCESS [ 12.832 s]
[INFO] todo-initdb ........................................ SUCCESS [ 0.067 s]
[INFO] todo-selenium ...................................... SUCCESS [01:13 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:14 min
[INFO] Finished at: 2015-10-01T10:32:34+09:00
[INFO] Final Memory: 36M/206M
[INFO] ------------------------------------------------------------------------
Above, files like libraries and plugins necessary for project development are copied. Operation is completed
when the repository is copied to ${HOME}/.m2 of offline environment machine. If a process which has not been
executed even once in online environment is executed in offline environment, necessary files like libraries and
plugins cannot be fetched resulting in the process failure. However, by copying the files, the development can be
continued uninterrupted even after moving to offline environment.
This guideline recommends the structure of creating Entity classes and Repository for the following reasons.
1. By splitting the overall logic into business logic (Service) and the logic to access business data, the
implementation scope of business logic gets limited to the implementation of business rules,
2. Access logic to business data is standardized by consolidating the operations of business data in the
Repository.
Note: Though this guideline recommends a structure to create Entity classes and Repository, it is not
mandatory to perform development in this structure.
Decide a structure by taking into account the characteristics of the application as well as the project (struc-
ture of development team and development methodology).
A case where application is created by multiple development teams is assumed; however, the flow itself remains
same even if developed by a single team.
(1) Common Common development team designs and creates Entity classes.
development team
(2) Common Common development team works out access pattern for the Entity
development team classes extracted in (1) and designs methods of Repository interface.
Common development team should implement the methods to be shared
by multiple development teams.
(3) Common Common development team provides Entity classes and Repository
development team created in (1) and (2) to the business application development team.
At this time, it requests each business application development team to
implement the Repository interface.
(4) Business application Business application development team takes charge of the
development team implementation of Repository interface.
(5) Business application Business application development team develops Service interface and
development team Service class using the Entity class and Repository provided by
the common development team and the Repository implementation class
created by the team itself.
Warning: A system having a large development scope is often developed by assigning the application
to multiple teams. In that case, it is strongly recommended to provide a common team to design Entity
classes and Repository.
When there is no common team, O/R Mapper(MyBatis, etc.) should be called directly from Service and
a method to access business data should be adopted without creating Entity classes and Repository .
1. Create Entity class for each table. However, Entity class is not required for mapping tables
which represent the relationship between the tables.
Further, when the tables are not normalized, Entity class
for each table rule may not be applicable. Refer to the
Warning as well as Note outside this table
for the approach related to not-normalized tables.
2. When there is a FK (Foreign Key) in When there is 1:N relationship with FK destination table,
the table, the Entity class of FK use either java.util.List<E> or
destination table must be defined as java.util.Set<E>.
one of the properties of this Entity. The Entity corresponding to the FK destination table is
called as the related Entity in this guideline.
3. Treat the code related tables as Code related tables are to manage the pairs of code value
java.lang.String rather than and name.
as an Entity. When there is a need to bifurcate the process as per code
values, enum class corresponding to code value should be
created and it must be defined as property.
Warning: When table is not normalized, check whether to use the method of creating the Entity
classes and Repository by considering the following points. Since the unnormalized tables do not have
good compatibility with JPA, it is better not to use JPA.
• Creating an appropriate Entity class may often not be possible because of increased difficulty in
creating entities if the tables are not normalized.
In addition, efforts to create an Entity classes also increases.
Two viewpoints must be taken into consideration here. Firstly “Can we assign an engineer who
can perform normalization properly?” and secondly “Is it worth taking efforts for creating
normalized Entity classes?”.
• If the tables are not normalized, the logic to fill the gap of differences between the Entity class
and structure of table is required in data access.
Here the viewpoint to be considered is, “Is it worth taking efforts to fill the gap of differences
between the Entity class and structure of table ?”.
The method of creating Entity classes and Repository is recommended; however, the characteristics of
the application as well as the project (structure of development team and development methodology)
must also be taken into account.
Note: If you want to operate business data as application, and as normalized Entity even if the tables
are not normalized, it is recommended to use MyBatis as an implementation of RepositoryImpl of the
infrastructure layer.
MyBatis is the O/R Mapper developed to map the SQL with object and not to map the database table record
with object. Therefore, mapping to the object independent of table structure is possible depending on the
implementation of SQL, .
Table structure
(1) Transaction related t_order Table to store orders. 1 record is stored for 1 order.
(3) t_order_coupon Table to store the coupon used in a single order. Record of
each coupon is stored when multiple coupons are used in
1 order. No record is stored when coupon is not used.
3.2. Domain
(5) Layer Implementation m_category Master table to define product category. 137
Entity structure
If Entity classes are created with the help of policy defined by the above table, it results into the following structure.
(6) ItemCategory Entity class is not created since m_item_category table is the
mapping table to store the relationship between m_item table and
m_category table.
(8) OrderStatus Entity class is not created since c_order_status table is code table.
As it can be observed from the above entity diagram, it might first seem that Order class is the only main entity
class in the shopping site application; however, there are other main entity class as well other than Order class.
Below is the classification of main Entity classes as well as Entity class which are not main.
The following 4 Entities are treated as the main Entity for creating shopping site application.
Sr. No. Entity class Reasons for treating as the main Entity.
(1) Order class It is one of the most important Entity class in the shopping site.
Order class is the Entity indicating the order itself and a
shopping site cannot be created without the Order class.
(2) Item class It is one of the most important Entity class in the shopping site.
Item class is the Entity indicating the products handled in the
shopping site and a shopping site cannot be created without
Item class.
(3) Category class Product categories are displayed usually on the top page or as a
common menu in shopping sites. In such shopping sites,
Category becomes a main entity. Usually operations like
‘search category list’ can be expected.
(4) Coupon class Often discounts through coupons are offered in the shopping
sites as a measure of promoting sales of the products.
In such shopping sites, Coupon becomes a main entity. Usually
operations like ‘search coupon list’ can be expected.
The following are not main Entities for creating shopping site application.
Sr. No. Entity class Reason of not treating Entity as main Entity
(5) OrderItem class This class indicates 1 product purchased in 1 order and exists
only as the related Entity of Order class.
So OrderItem class should not be considered as main Entity.
(6) OrderCoupon This class indicates 1 coupon used in 1 order and exists only as
the related Entity of Order class.
So, OrderCoupon class should not be considered as main Entity.
Roles of Repository
1. To provide to Service, the operations necessary to control Entity lifecycle (Repository interface).
The operations for controlling Entity lifecycle are CRUD operations.
Structure of Repository
Repository consists of Repository interface and RepositoryImpl and performs the following roles.
(1) Repository interface Defines methods to control Defines methods for CRUD operations of
Entity lifecycle required for the Entity and is not dependent on
implementing business logic persistence layer.
(Service). Repository interface belongs to the
domain layer since it plays the roles of
defining the operations on Entity required
for implementing business logic (Service).
Note: Is it possible to hide 100% of persistence platform dependent logic from the Service class ?
In some cases it cannot be hidden completely due to constraints of persistence platform and the libraries
used to access the platform. As much as possible, platform dependent logic should be implemented in
RepositoryImpl instead of Service class. When it is difficult to exclude the platform dependent logic and
merits of doing so are less, persistence platform dependent logic can be implemented as a part of business
logic (Service) process.
A specific example of this is given here. There are cases when unique
constraints violation error is needed to be handled when save method of
org.springframework.data.jpa.repository.JpaRepository interface provided by
Spring Data JPA is called. In case of JPA, there is a mechanism of cache entity operations and SQL is
executed when transactions are committed. Therefore, since SQL is not executed even if save method of
JpaRepository is called, unique constraints violation error cannot be handled in logic. There is a method
(flush method) to reflect cached operations as means to explicitly issue SQLs in JPA. saveAndFlush
and flush methods are also provided in JpaRepository for the same purpose. Therefore, when unique
constraints violation error needs to be handled using JpaRepository of Spring Data JPA, JPA dependent
method (saveAndFlush or flush) must be called.
Warning: The most important purpose of creating Repository is not to exclude the persistence platform
dependent logic from business logic. The most important purpose is to limit the implementation scope
of business logic (Service) to the implementation of business rules. This is done by separating the
operations to access business data in Repository. As an outcome of this, persistence platform dependent
logic gets implemented in Repository instead of business logic (Service).
Creation of Repository
1. Create Repository for the main This means separate Repository for operations of related
Entity only. Entity is not required.
However, there are case when it is better to provide
Repository for the related Entity in specific applications
(for example,
application having high performance requirements etc).
2. Place Repository interface and Repository interface belongs to domain layer and
RepositoryImpl in the same package RepositoryImpl belongs to infrastructure layer. However,
of domain layer. Java package of RepositoryImpl can be same as the
Repository interface of domain layer.
3. Place DTO used in Repository in the For example, DTO to store search criteria or summary
same package as Repository DTO for that defines only a few items of Entity.
interface.
as follows.
Structure of Repository
Entity class used in the explanation of Example of creating Entity class is used as an example, the resulting
configuration is as follows:
• SimpleCrudRepository.java
(3) Method to retrieve the list of all Entities. In Spring Data, it was java.util.Iterable.
Here as a sample, it is set to java.util.List.
(4) Method to fetch collection of Entity objects corresponding to the specified pagination
information (start position, record count, sort information).
Pageable and Page are the interfaces provided by Spring Data.
• TodoRepository.java
An example of creating Repository of Todo Entity, which was created in tutorial, on the basis of
SimpleCrudRepository interface created above is shown below.
// (1)
public interface TodoRepository extends SimpleCrudRepository<Todo, String> {
// (2)
long countByFinished(boolean finished);
}
(1) TodoRepository interface is created by specifying Todo entity in the generic type parameter “T”
and
String class in the generic type parameter “ID”.
(2) Methods not provided by SimpleCrudRepository interface are added in this interface.
In this case, “Method for acquiring count of Todo entity objects for which specified tasks have
been finished” is added.
Count related
4. 1. Method name beginning with countBy to indicate that this method
method
fetches count of Entities which matches with the condition.
2. Return value must be long type.
3. In the method name after “countBy”, physical or logical name of the
field used as search condition must be specified. Hence, the method
150 name must be such that it becomes possible to
3 Application estimate “the kind of
Development
entity that can be fetched using this method”.
4. There must be an argument for each search condition. However, when
there are many conditions, DTO containing all search conditions can
be provided.
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Note: In case of methods related to update processing, it is recommended to construct methods in the
same way as shown above. “find” in the method name above can be replaced by “update” or “delete”.
• Todo.java (Entity)
• TodoRepository.java
(1) Example of method that fetches TODO objects whose title matches with specified value
(TODO in which todoTitle=[argument value]).
Physical name(todoTitle) of condition field is specified after findOneBy.
(2) Example of method that fetches unfinished TODO objects (TODO objects where
finished=false).
Logical condition name is specified after findAllBy.
(3) Example of method that fetches pages of unfinished TODOs (TODO objects where
finished=false).
Logical condition name is specified after findPageBy.
(4) Example of method that fetches count of TODO objects for which the finish deadline has
already passed (TODO for which createdAt < sysdate - [finish deadline in days] &&
finished=false).
Logical condition name is specified after countBy.
(5) Example of method that checks whether a TODO is created on a specific date
(createdAt=specified date).
Physical name (createdAt) is specified after existsBy.
Creation of RepositoryImpl
Roles of Service
Business logic consists of create, update, consistency check etc of business data as well as all the
processes related to business logic.
Create and update process of business data should be delegated to Repository(or O/R Mapper) and service
should be limited to implementation of business rules.
In this guideline, the logic to be implemented by Controller and Service should be as per the rules given
below.
1. For the data requested from the client, single item check and correlated item check is to be performed
in Controller (Bean Validation or Spring Validator).
2. Conversion processes (Bean conversion, Type conversion and Format conversion) for the data to be
passed to Service, must be performed in Controller instead of Service.
4. Conversion processes (Type conversion and Format conversion) for the data received from Service
(data to respond to the client), must be performed in Controller (View class etc).
Service consists of Service classes and SharedService classes and plays the following role.
In this guideline, POJO (Plain Old Java Object) having @Service annotation is defined as Service or
SharedService class.
We are not preventing the creation of interface and base classes that limit the signature of methods.
2 SharedService
1. Methods of other SharedService classes can
class
Provides shared (reusable) be called from a SharedService (Figure 2-
logicfor multiple Controllers 1). However, Calling hierarchy should
and Service classes. not become complicated. If calling hier-
archy becomes complicated, there is a risk
of reduction in maintainability.
2. Methods of SharedService classes can be
called from Controller (Figure 2-2). How-
ever, it can be only be done if there is
no problem from transaction manage-
ment perspective.If there is a problem
from transaction management perspective,
first create a method in Service class and
implement transaction management in this
method.
3. It is prohibited to call methods of Service
class from SharedService (Figure 2-3).
Logic that cannot be (should not be) reused and logic that can be (should be) reused exist in the business logic.
To implement these 2 logics in the same class, it is difficult to decide whether a method can be re-used or not.
To avoid this problem, it is strongly recommended to implement the method to be re-used in the
SharedService classin this guideline.
Reason for prohibiting the calling of other Service classes from Service class
In this guideline, calling methods of other Service classes from a Service class is prohibited.
Service provides business logic to a specific controller and is not created with the assumption of using it from
other services.
If it is called directly from other Service classes, the following situations can easily occur and there is a risk of
reduced maintainability.
1.
The logic that must be implemented in the calling service class, gets implemented in the called
service class for reasons like “having the logic at a single location” etc.
As a result, arguments for identifying the caller, get added to the method easily;
Ultimately, the logic is incorrectly abstracted out as shared logic (like utilities). It results
into a modular structure without much insight.
2.
If the stack patterns or stack of services calling each other is large in number, understanding
the impact of modifications in source-code due to change in specifications or bug fixes,
becomes difficult.
In order to bring consistency in development of business logic, interfaces and base classes are created which limit
the signature of the methods.
The purpose is also to prevent the injection of differences due to development style of each developer by limiting
the signature through interfaces and base classes.
Note: In large scale development, there are situations where not every single developer is highly skilled
or situations like having consistency in development of business logic considering maintainability after
servicing. In such situations, limiting the signature through interfaces can be an appropriate decision.
In this guideline, we do not specifically recommended to create interface to limit signature; however, type
of architecture must be selected on the basis of characteristics of the project. decide the type of architecture
taking into account the project properties.
Note: Sample of implementation of interface and base classes to limit signature - Interface to limit
signature
// (1)
public interface BLogic<I, O> {
O execute(I input);
}
• Controller
// (2)
@Inject
XxxBLogic<XxxInput, XxxOutput> xxxBLogic;
// (3)
XxxOutput output = xxxBlogic.execute(input);
// omitted
redirectAttributes.addFlashAttribute(output.getTourReservation());
return "redirect:/xxx?complete";
}
(3) Controller calls execute method of BLogic interface and executes business logic.
To standardize process flow of business logic when a fixed common process is included in Service, base
classes are created to limit signature of method.
// omitted
// (4)
preExecute(input);
// (5)
O output = doExecute(input);
// omitted
return output;
} finally {
// omitted
}
(4) Call the method to perform pre-processing before executing business logic from base
classes.
In the preExecute method, business rules are checked.
(5) Call the method executing business logic from the base classes.
// (6)
protected void preExecute(XxxInput input) {
// omitted
Tour tour = tourRepository.findOne(input.getTourId());
Date reservationLimitDate = tour.reservationLimitDate();
if(input.getReservationDate().after(reservationLimitDate)){
throw new BusinessException(ResultMessages.error().add("e.xx.xx.0001"));
}
// (7)
protected XxxOutput doExecute(XxxInput input) {
TourReservation tourReservation = new TourReservation();
// omitted
tourReservationRepository.save(tourReservation);
XxxOutput output = new XxxOutput();
output.setTourReservation(tourReservation);
// omitted
return output;
}
1.
For each Entity Create Service paired with Main Entity is in other words, business data. If the
the main Entity. application is to be designed and implemented
with focus on business data, then Service classes
should be created in this way.
2.
For each Create Service paired with If the application is to be designed and
use-case the use-case. implemented with focus on events on the screen,
Service should be created in this way.
3.
For each event Create Service paired with If the application is to be designed and
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Warning: The pattern of Service creation must be decided by taking into account the features of
application to be developed and the structure of development team.
It is not necessary to narrow down to any one pattern out of the 3 indicated patterns. Creating Ser-
vices using different patterns randomly should be avoided for sure; however, patterns can be used in
combinations, if policy of usage of patterns in certain specific conditions has been well-thought
decision and has been directed by the architect. For example, the following combinations are possi-
ble.
[Example of usage of patterns in combination]
• For the business logic very important to the whole application, create as SharedService class for
each Entity.
• For the business logic to be processed for the events from the screen, create as Service class for
each Controller.
• In the Service class for each controller, implement business logic by calling the sharedService as
and when required.
Tip: BLogic is generated directly from design documents when using “TERASOLUNA ViSC”.
Following is the image of application development when creating a Service for each Entity.
Note: An example of a typical application in which a Service is created for each Entity is a REST applica-
tion. REST application provides CRUD operations (POST, GET, PUT, DELETE of HTTP) for published
resources on HTTP. Most of the times, the resources published on HTTP are business data (Entity) or part
of business data (Entity), they have good compatibility with the pattern of creating Service for each Entity.
In case of REST application, most of the times, use-cases are also extracted on a “per Entity” basis. Hence,
the structure is similar to the case when Service is created for on a “per use-case” basis.
(2) Implement SharedService if there is shared logic between multiple business logics.
In the above figure, different person is assigned as the in-charge. However, he may be the same
person as (1) depending per the project structure.
Following is the image of application development when creating a Service for each use-case.
In case of use-case which performs CRUD operations on the Entity, structure is same as in case of creating
(2) Implement SharedService if there is shared logic between multiple business logics.
In the above figure, different person is assigned as the in-charge. However, he may be the same
person as (1) depending per the project structure.
Note: With an increase in the size of the use-cases, the development scope of a person increases. At
such a point of time, it becomes difficult to divide the work of this use-case with other developers. In case
of application which has to be developed by introducing a large number of developer at the same time,
the use-case can be further split into finer use-cases and which can then be allocated to more number of
developers.
Below is the image of application development when the use-case is further split.
Splitting a use-case has no impact on SharedService. Hence, the explanation is omitted here.
(1) Divide the use-case into finer processes which make-up the complete use-case. Assign each
fine process to a developer. Each developer creates the Service for assigned process.
Note that the processes here are operations like search, create, update, delete etc. and these
processes do not have a direct mapping to the processing required to be done for
each event generated on screen.
For example, if it the event generated on screen is “Update”, it includes multiple finer processes
such as “Fetching the data to be updated”, “Compatibility check of update contents” etc.
If there is no specific reason, it is desirable that Controller must also be created for each of these
finer processes and must be developed by the same developer who creates the Service class.
Tip: In some projects, “group of use-cases” and “use-cases” are used in place of “use-case” and “pro-
cesses” used in this guideline.
Following is the image of application development when creating a Service(BLogic) for each event.
(2) If there is no specific reason, controller also should be created on “per use-case” basis.
(3) Even if the separate Service(BLogic) is created for each event, it is recommended that same
person is the in-charge of the complete use-case.
3.2. Domain Layer Implementation 167
(4) Implement in SharedService to share the logic with multiple business logics.
In the above figure, different person is assigned as the in-charge. However, he may be the same
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Note: With an increase in the size of the use-cases, the development scope of a person increases. At
such a point of time, it becomes difficult to divide the work of this use-case with other developers. In case
of application which has to be developed by introducing a large number of developer at the same time,
the use-case can be further split into finer use-cases and which can then be allocated to more number of
developers.
Below is the image of application development when the use-case is further split.
Splitting a use-case has no impact on SharedService. Hence, the explanation is omitted here.
(1) Divide the use-case into finer processes which make-up the complete use-case. Assign each
fine process to a developer. Each developer creates the Service for assigned process.
Note that the processes here are operations like search, create, update, delete etc. and these
processes do not have a direct mapping to the processing required to be done for
each event generated on screen.
For example, if it the event generated on screen is “Update”, it includes multiple finer processes
such as “Fetching the data to be updated”, “Compatibility check of update contents” etc.
If there is no specific reason, it is desirable that Controller must also be created for each of these
finer processes and must be developed by the same developer who creates the Service class.
Below are the points to be taken care of while creating Service class.
1. If interface is there, When using AOP, Dynamic proxies functionality of standard JDK is used. In
case of no interface, CGLIB included in Spring Framework is used. In case of CGLIB there are certain
restrictions like “Advice cannot be applied on final methods” etc. Refer to Spring Reference Document
-Aspect Oriented Programming with Spring(Proxying mechanisms)-for details.
2. It becomes easier to create a stub of business logic. When application layer and domain layer are
developed in parallel using different development teams, stubs of Service are required. When there is
a need to create stubs, it is recommended to have interface .
@Service // (1)
@Transactional // (2)
public class CartServiceImpl implements CartService { // (3) (4)
// omitted
}
(4) Service class must not maintain state. Register it in container as bean of singleton scope .
Objects (POJO such as Entity/DTO/VO) and values (primitive type, primitive wrapper class)
where state changes in each thread should not be maintained in class level fields.
Setting scope to any value other than singleton (prototype, request, session) using @Scope
annotation is also prohibited.
Transaction boundary is required only for the business logic that updates the database. However, it is
recommended to apply the annotation at class level to prevent bugs due to skipped annotation. However,
defining @Transactional annotation only at required places (methods which update the database) is
also fine.
1. prototype, request, session are the scopes for registering bean that maintains state. Hence they must
not be used in Service class.
2. When scope is set to request or prototype, performance is affected as the bean generation frequency is
high in DI container.
3. When scope is set to request or session, it cannot be used in non Web applications (for example, Batch
application).
Below are the points to be taken care of while writing methods of Service class.
@Service
@Transactional
public class CartServiceImpl implements CartService {
@Inject
CartRepository cartRepository;
(2) Define methods in Service interface and implement business logic in its implementation
class.
(3) Add @Transactional annotation for changes to default transaction definition (class level
annotation).
Attributes should be specified as per the requirement.
Refer to Information required for “Declarative transaction management” for details.
The way of handling read-only transactions depends on the implementation of JDBC driver; hence, confirm
the specifications of JDBC driver to be used.
If it is set to “perform health check” when retrieving a connection from connection pool, “Read-only
transactions” may not be enabled. For details on this event and to avoid the same, refer to About cases
where “Read-only transactions” are not enabled.
The below points must be considered for arguments and return values of methods of Service class.
Serializable classes (class implementing java.io.Serializable) must be used for arguments and return
values of Service class.
Since there is possibility of Service class getting deployed as distributed application, it is recommended to allow
only Serializable class.
• void
• etc ...
1. Input object indicates the object that has all the input values required for executing Service method.
2. Output object indicates the object that has all the execution results (output values) of Service method.
If business logic(BLogic class) is generated using “TERASOLUNA ViSC” then, input and output
objects are used as argument and return value of the of BLogic class.
Values that are forbidden as arguments and return values are as follows.
• Objects (javax.servlet.http.HttpServletRequest ,
javax.servlet.http.HttpServletResponse , javax.servlet.http.HttpSession
, org.springframework.http.server.ServletServerHttpRequest) which are de-
pendent on implementation architecture of application layer (Servlet API or web layer API of
Spring).
1. If objects depending on implementation architecture of application layer are allowed, then application
layer and domain layer get tightly coupled.
2. java.util.Map is too generalized. Using it for method arguments and return values makes it
difficult to understand what type of object is stored inside it. Further, since the values are managed
using keys, the following problems may occur.
• Values are mapped to a unique key and hence cannot be retrieved by specifying a key name which is
different from the one specified at the time of inserting the value.
• When key name has to be changed, it becomes difficult to determine the impacted area.
How to sharing the same DTO between the application layer and domain layer is shown below.
• DTO belonging to the package of domain layer can be used in application layer.
Warning: Form and DTO of application layer should not be used in domain layer.
Below are the points to be taken care of while creating SharedService class.
Only the points which are different from Service class are explained here.
Below are the points to be taken care of while writing methods of SharedService class.
Only the points which are different from Service class are explained here.
1. Methods in SharedService class must be created for each logic which is shared between multiple
business logics.
Points are same as Regarding arguments and return values of methods of Service class.
Implementation of logic
Service and SharedService has implementation of logic related to operations such as data fetch, update, consis-
tency check of business data and implementation related to business rules.
Refer to the following for the examples of data (Entity) fetch and update.
Returning messages
Warning message and business error message are the two type of messages which must be resolved in Service
(refer to the figure in red broken line below).
Other messages should be resolved in application layer.
Refer to Message Management for message types and message pattern.
In service, instead of the actual message the information required for building the message (message
code, message insert value) is resolved.
Message object must be returned for warning message. If domain object such as Entity needs to be returned with
it,
message object and domain object should inserted into output object (DTO) and this output object must be
returned.
• Creation of DTO
// omitted
// omitted
// omitted
// omitted
(1) When the order includes products which are not available right now, set hasOrderProduct
to true.
(2) In the above example, when the order includes products which are not available right now, a
warning message occurs.
(3) In the above example, the registered Order object and warning message are returned by
storing objects in a DTO called OrderResult.
Business exception is thrown when business rules are violated while executing business logic.
The following can be the cases.
• etc ...
Since business exceptions need to be handled in controller class, they can be configured as checked ex-
ception. However in this guideline, it is recommended that business exception be subclass of unchecked
exception (java.lang.RuntimeException). By default, if there is a RuntimeException, transaction
will be rolled back. Hence, doing this will prevent leaving a bug in the source-code due to inadequate
settings of @Transactional annotation. Obviously, if settings are changed such that transaction rollbacks
even in case checked exceptions, business exception can be configured as subclass of checked exceptions.
// omitted
if(currentDate.after(reservationLimitDate)) { // (1)
throw new BusinessException(ResultMessages.error().add("e.xx.xx.0001"));
}
// omitted
System exception is thrown when error occurs in system while executing business logic.
The following can be the cases.
• When master data, directories and files that should already exist, do not exist
• When a checked exception generated by a library method is caught and this exception indicates abnormal
system state.
• etc ...
Example that throws system exception while catching IO exception while copying the file is shown below.
// ...
try {
FileUtils.copy(srcFile, destFile);
} catch(IOException e) { // (1)
throw new SystemException("e.xx.fw.0002",
"Failed file copy. src file '" + srcFile + "' dest file '" + destFile + "'.", e);
}
(1) System exception that is classified into invalid system state is thrown by the library method.
The exception generated by library must be passed to system exception class as cause
exception.
If cause exception is lost, error occurrence location and basic error cause cannot be traced from
the stack trace.
When data access error occurs in Repository and O/R Mapper while executing business logic, it is con-
verted to subclass of org.springframework.dao.DataAccessException and thrown. Error
can be handled in application layer instead of catching in business logic. However, some errors like unique
constraints violation error should be handled in business logic as per business requirements. Refer to
Database Access (Common) for details.
Transaction management is required in the logic where data consistency must be ensured.
There are various transaction management methods. However, in this guideline, it is recommended to use
“Declarative Transaction Management” provided by Spring Framework.
In “Declarative transaction management”, the information required for transaction management can be declared
by the following 2 methods.
Refer to Spring Reference Document -Transaction Management(Declarative transaction management)- for the
details on “Declarative type transaction management” provided by Spring Framework.
1. The transaction management to be performed can be understood by just looking at the source code.
2. AOP settings for transaction management is not required if annotations are used and so XML becomes
simple.
Specify @Transactional annotation for at class level or method level which are considered as target of
transaction management and specify the information required for
transaction control in attributes of @Transactional annotation.
However, in this guideline, it is recommended to use an annotation of Spring Framework that can specify
the information required for “Declarative transaction management” in a much more detailed way.
[REQUIRED]
Starts transaction if not started. (default when omitted)
[REQUIRES_NEW]
Always starts a new transaction.
[SUPPORTS]
Uses transaction if started. Does not use if not started.
[NOT_SUPPORTED]
Does not use transaction.
[MANDATORY]
Transaction should start. An exception occurs if not started.
[NEVER]
Does not use transaction (never start). An exception occurs if started.
[NESTED]
save points are set. They are valid only in JDBC.
2 isolation
[DEFAULT]
Isolation level provided by DB by default.(default when omitted)
[READ_UNCOMMITTED]
Reads (uncommitted) data modified in other transactions.
[READ_COMMITTED]
Does not read (uncommitted) data modified in other transactions.
[REPEATABLE_READ]
Data read by other transactions cannot be updated.
[SERIALIZABLE]
Isolates transactions completely.
3 timeout
It is recommended to specify the annotation at the class level or method level of the class. Must
be noted that it should not interface or method of interface. For the reason, refer to 2nd Tips of Spring
Reference Document -Transaction Management(Using @Transactional)-.
There is a value attribute in @Transactional annotation. However, this attribute specifies which
Transaction Manager to be used when multiple Transaction Managers are declared. It is not required
to specify when there is only one Transaction Manager. When multiple Transaction Managers need to be
used, refer to Spring Reference Document -Transaction Management(Multiple Transaction Managers with
@Transactional)-.
• Oracle : READ_COMMITTED
• DB2 : READ_COMMITTED
• PostgreSQL : READ_COMMITTED
• MySQL : REPEATABLE_READ
A mechanism is provided to run SQL under “Read-only transactions” by specifying readOnly = true;
however, when all of the following conditions are satisfied, there will be a JDBC driver where “Read-only
transactions” are not enabled.
In a case where “Read-only transactions” are not enabled, if readOnly = true is specified, it ends up
carrying out the unnecessary processes. Therefore, it is recommended to execute the SQL under “Updatable
transactions” even for the reference processes.
• Do not perform health check when retrieving a connection from connection pool.
• Enable ‘Auto commit’ of the connection retrieved from connection pool. (Disable ‘Auto commit’ only
when transaction management is required)
However, However, do not change the design for ‘health check’ and ‘auto commit’ to avoid this event.
[Remarks]
• Reproduction of this event is confirmed on PostgreSQL 9.3 and Oracle 12c. It is not performed on any
other database and versions.
• When SQL or API call of JDBC is logged in using log4jdbc, SQLException occurred from JDBC
driver is output to log with ERROR level.
• SQL Exception occurred from JDBC driver is ignored by exception handling of Spring Frame-
work. Hence, even though it is not an error as application behavior, the “Read-only transactions”
is not enabled.
[Reference]
When following log is output using log4jdbc, it will be treated as a case corresponding to this event.
Propagation of transaction
1. Controller calls a method of Service class. At this time, since started transaction does not exist, transaction
is started using TransactionInterceptor.
2. TransactionInterceptor calls the method of service class after starting the transaction.
3. Service class calls a method of SharedService. This method is also under transaction control. At this
time, though started transaction exists, TransactionInterceptor participates in the started transac-
tion without starting a new transaction.
4. TransactionInterceptor calls the method under transaction control after participating in the started
transaction.
When propagation method of transaction is set to “REQUIRED”, though there is only one physical transaction, in-
ternally Spring Framework creates transaction boundaries. In case of above example, when a method of SharedSer-
vice is called, a TransactionInterceptor is started which internally provides transaction control boundary at Shared-
Service level. Therefore, when an exception (which is set as target of rollback) occurs in SharedService
method, status of transaction is set to rollback (rollback-only) by TransactionInterceptor. This trans-
action now cannot be committed. Going further, if the Service method tries to commit this transaction
due to conflicting settings of rollback target exception between Service method and SharedShared method,
UnexpectedRollbackException is generated by Spring Framework notifying that there is inconsistency
in transaction control settings. When UnexpectedRollbackException is generated, it should be checked that there
is no inconsistency in rollbackFor and noRollbackFor settings.
1. Controller calls a method of Service class. This method is under transaction control. At this time, since
started transaction does not exist, transaction is started by TransactionInterceptor (Hereafter, the
started transaction is referred as “Transaction A”).
3. Service class calls a method of SharedService class. At this time, though started transaction (Transac-
tion A) exists, since propagation method of transaction is “REQUIRES_NEW”, new transaction is started
5. TransactionInterceptor performs commit or rollback according to the process result and ends the
Transaction B. At this time, “Transaction A” is resumed and status is changed to Active.
6. TransactionInterceptor performs commit or rollback according to the process result and ends the
Transaction A.
Since “Declarative transaction management” provided by Spring Framework is implemented using AOP,
transaction management is applied only for method calls for which AOP is enabled.
Since the default mode of AOP is “proxy” mode, transaction control will be applied only when public
method is called from another class.
Note that transaction control is not applied if the target method is called from an internal method even if
the target method is a public method.
It is possible to enable transaction control for internal method calls as well by setting the AOP mode
to "aspectj". However, if internal method call of transaction management is enabled, the route of
transaction management may become complicated; hence it is recommended to use the default “proxy” for
AOP mode.
PlatformTransactionManager settings
• xxx-env.xml
Example of settings for managing the transaction using JDBC connection which is fetched from DataSource
is given below.
Note: When transaction management (Global transaction management) is required for multiple
DBs (Multiple resources)
1.
Implementation class for managing the transaction by
org.springframework.jdbc.datasource.
calling API of JDBC(java.sql.Connection).
DataSourceTransactionManager
Use this class when MyBatis and JdbcTemplate is to
be used.
2.
org.springframework.orm.jpa. Implementation class for managing the transaction by
JpaTransactionManager calling API of
JPA(javax.persistence.EntityTransaction).
Use this class when JPA is to be used.
3.
org.springframework.transaction.jta. Implementation class for managing the transaction by
JtaTransactionManager calling API of
JTA(javax.transaction.UserTransaction).
Use this class to manage transaction with resources
(Database/Messaging service/General-purpose
EIS(Enterprise Information System) etc.) using JTS (Java
Transaction Service) provided by application server.
When it is necessary to execute the operations with
multiple resources in a single transaction, it is necessary
to use JTA for managing transactions.
• xxx-domain.xml
Since the article of IBM DeveloperWorks is an old article (year 2009), some of the content is different than
the behavior when using Spring Framework 4.1.
Specifically, the contents of “Listing 7. Using read-only with REQUIRED propagation mode - JPA”.
From Spring Framework 4.1, when Hibernate ORM 4.2 or higher version is used as JPA provider, it has
been improved so that instruction can be given to run the SQL under “Read-only transactions” for JDBC
driver (SPR-8959).
The way of handling read-only transactions depends on the implementation of JDBC driver; hence, confirm
the specifications of JDBC driver to be used.
Various attributes can be specified in <tx:annotation-driven> and default behavior can be customized.
• xxx-domain.xml
<tx:annotation-driven
transaction-manager="txManager"
mode="aspectj"
proxy-target-class="true"
order="0" />
4 order Order of Advice of AOP (Priority). When omitted, it will be “Last (Lowest
priority)”.
3.2.7 Tips
When it is necessary to output the error of business rules for each field, the mechanism of (Bean Validation or
Spring Validator) on the Controller side should be used.
In this case, It is recommended to implement check logic as Service class and then to call the method of Service
class from Bean Validation or Spring Validator.
Refer to Input Validation business logic approach for details.
Methods to create a Repository for relational database using MyBatis3 and JPA are introduced below.
When MyBatis3 is to be used as persistence API with relational database, RepositoryImpl need not be imple-
mented, if Repository interface is created using “Mapper interface mechanism” provided by MyBatis3.
This is because it is a mechanism where MyBatis3 automatically maps the method of Mapper interface and the
statement (SQL) to be called.
package com.example.domain.repository.todo;
import com.example.domain.model.Todo;
// (1)
public interface TodoRepository {
// (2)
Todo findOne(String todoId);
}
</mapper>
Define statement (SQL) to be run for each method defined in Repository interface.
Specify a method name of Repository interface in statement ID of each statement element (id
(4)
attribute of select/insert/update/delete element).
When JPA is to be used as persistence API with relational database, Repository can be very easily created if
org.springframework.data.jpa.repository.JpaRepository of Spring Data JPA is used.
For details on how to use Spring Data JPA, refer to Database Access (JPA).
When Spring Data JPA is used, only an interface with inherited JpaRepository is required to be created for basic
CRUD operations. In other words, RepositoryImpl is not required.
However, RepositoryImpl is needed for using dynamic query (JPQL).
Refer to Database Access (JPA) for implementing RepositoryImpl when using Spring Data JPA.
• TodoRepository.java
Describe the case to add operations which are not provided by JpaRepository.
When Spring Data JPA is used, if it is a static query, it is advisable to add a method to the interface and to specify
the query (JPQL) to be executed when that method is called, using the annotation.
• TodoRepository.java
(1)
Todo
TBD
This chapter explains implementation of application layer of a web application that uses HTML forms.
Note: Refer to the following page for the implementation required for Ajax development and REST API.
• Ajax
1. Implementing Controller
Controller receives the request, calls business logic, updates model, decides View. Thereby, controls one
complete round of operations after receiving the request.
It is the most important part in the implementation of application layer.
3. Implementing View
View (JSP) acquires the data from model (form object, domain object etc.) and generates screen (HTML).
Controller does not implement business logic but delegates by calling Service method.
Note: It is recommended that controller implements only the routing logic such as calling business logic,
reflecting output of the business logic to Model, deciding the View name is implemented in the Controller.
Controller class is created with @Controller annotation added to POJO class (Annotation-based
Controller).
Controller in Spring MVC can also be created by implementing
org.springframework.web.servlet.mvc.Controller interface (Interface-based Controller).
However, it is preferred to avoid using it as it is Deprecated from Spring 3 onwards.
@Controller
public class SampleController {
// ...
}
@RequestMapping(value = "hello")
public String hello() {
// ...
}
The rules for mapping the incoming request with a handler method can be specifying as attributes of
@RequestMapping annotation.
method
2.
Specify HTTP method (RequestMethod type) which needs to be mapped
(multiple methods allowed).
GET/POST are mainly used for mapping requests from HTML form, while other
HTTP methods (such as PUT/DELETE) are used for mapping requests from REST
APIs as well.
params
3.
Specify request parameters which need to be mapped (multiple parameters allowed).
Request parameters are mainly used for mapping request from HTML form. If this
mapping method is used, the case of mapping multiple buttons on HTML page can
be implemented easily.
headers
4.
Specify request headers which need to be mapped (multiple headers allowed).
Mainly used while mapping REST API and Ajax requests.
consumes
5.
Mapping can be performed using Content-Type header of request. Specify media
type which needs to be mapped (multiple types allowed).
Mainly used while mapping REST API and Ajax requests.
produces
6.
Mapping can be performed using Accept header of request. Specify media type
which needs to be mapped (multiple types allowed).
Mainly used while mapping REST API and Ajax requests.
Complex mapping can be performed by combining multiple attributes, but considering maintainability,
mapping should be defined and designed in the simplest way possible . It is recommended to consider
combining 2 attributes (value attribute and any other 1 attribute).
In the following explanation, it is prerequisite to define the handler method in the Controller class.
@Controller // (1)
@RequestMapping("sample") // (2)
public class SampleController {
// ...
}
All the handler methods in this class are mapped to URLs with “sample” by adding
@RequestMapping("sample") annotation at class level.
(2)
In case of the following definition, if the URL "sample/hello" is accessed, then hello() method is exe-
cuted.
@RequestMapping(value = "hello")
public String hello() {
Pattern can be specified instead of a specific value for request path. For details of specifying patterns, refer to
reference documentation of Spring Framework.
• Path Patterns
In case of the following definition, if the URL "sample/hello" is accessed with POST method, then
hello() method is executed. For the list of supported HTTP methods, refer to Javadoc of RequestMethod.
When not specified, all supported HTTP methods are mapped.
In case of following definition, if the URL "sample/hello?form" is accessed, then hello() method is
executed.
When request is sent as a POST request, request parameters may exist in request body even if they do not exist in
URL.
Refer to the details on the following page to mainly use the controller to map REST API and Ajax requests.
• Ajax
Refer to the details on the following page to mainly use the controller to map REST API and Ajax requests.
• Ajax
Refer to the details on the following page to mainly use the controller to map REST API and Ajax requests.
• Ajax
• Grouping of URL of request is done for each unit of business flow or functional flow.
URL grouping means defining @RequestMapping(value = "xxx") as class level annotation.
• Use the same URL for requests for screen transitions within same functional flow
The same URL means the value of ‘value’ attribute of @RequestMapping(value = "xxx") must
be same.
Determining which handler method is used for a particular request with same functional flow is performed
using HTTP method and HTTP parameters.
The following is an example of mapping between incoming request and handler method by a sample application
with basic screen flow.
• Request URL
Create Entity Create a new Entity with the specified contents. Screen flow (form screen,
2.
confirmation screen, completion screen) exists for this process.
Entity update Update Entity of specified ID. Screen flow (form screen, confirmation
4.
screen, completion screen) exists for this process.
Request URL
• Request URLs of all the requests required by the process flow are grouped.
This functionality performs CRUD operations of Entity called ABC, therefore URL that starts with
"/abc/" is considered.
Note: "{id}" specified in URL of ‘Fetching details of Entity’, ‘Entity update’, ‘Entity delete’ operations
is called as, URI Template Pattern and any value can be specified. In this sample application, Entity ID is
specified.
Assigned URL of each operation of screen flow diagram is mapped as shown below:
Figure.3.3 Picture - Screen flow of entity management function and corresponding assigned URL
Multiple requests exist for each of Create Entity, Entity Update and Entity Delete functions. Therefore switching
(1)
(2)
Note: In this handler method, method attribute is not specified since it is not required for HTTP GET
method.
Besides implementing the handler method for form display, points mentioned below are required:
• Implement generation process of form object. Refer to Implementing form object for the details of form
object.
• Implement View of form screen. Refer to Implementing View for the details of View.
@NotEmpty
private String input1;
@NotNull
@Min(1)
@Max(10)
private Integer input2;
// omitted setter&getter
}
@ModelAttribute
public AbcForm setUpAbcForm() {
return new AbcForm();
}
(1)
To check user input in the form, data is sent by POST method and confirm is specified as HTTP parameter.
(1)
In case of input validation errors, it is recommended to call the handler method of form re-
display.
(2)
Return view-name of JSP to render the screen for user input confirmation.
(3)
Note: POST method is specified to prevent displaying confidential information such as password and other
personal information etc. in the address bar. (Needless to say that these security measures not sufficient
and needs more secure measures such as SSL etc.)
Besides implementing handler method for user input confirmation screen, points mentioned below are required.
• Implement view of user-input confirmation screen. Refer to Implementing View for the details of view.
(2)
Parameter name need not be specified for submit button. Submit button will do the actual create
operation.
(3)
Note: In the above example, HTML escaping is performed as an XSS countermeasure using f:h()
function while displaying the user input values. For details, refer to Cross Site Scripting.
Since HTTP parameters are sent across through HTTP POST method after clicking the Confirm button, it does
not appear in URI. However, “confirm” is included as HTTP parameter.
(1)
(2)
Since HTTP parameters are sent across through HTTP POST method after clicking the Back button, it does not
appear in URI. However, “redo” is included as HTTP parameter. Moreover, since input values of form had been
sent as hidden fields, input values can be restored on redisplayed form screen.
• Behavior when page having Back button is accessed and Back button is clicked.
• History of browser
To register input contents of form, the data (hidden parameters) to be registered is sent with HTTP POST method.
Sorting is not carried out using HTTP parameters since new request will be the main request of this operation.
Since the state of database changes in this process, it should not be executed multiple times due to double
submission.
Therefore, it is ‘redirected’ to the next screen (create complete screen) instead of directly displaying View
(screen) after
completing this process. This pattern is called as POST-Redirect-GET(PRG) pattern. For the details of PRG
(Post-Redirect-Get) pattern
refer to Double Submit Protection .
(1)
Return URL to the request needs to be redirected as view name in order to use PRG pattern.
(2)
Warning: PRG pattern is used to avoid double submission when the browser gets reloaded by clicking F5
button. However, as a countermeasure for double submission, it is necessary to use TransactionTokenCheck
functionality. For details of TransactionTokenCheck, refer to Double Submit Protection .
‘Create’ request does not return to the screen directly, but it is redirected to create complete display
("/abc/create?complete"). Hence HTTP status is changed to 302.
In order to notify the completion of create process, complete must be present in the request as HTTP parameter.
(1)
(2)
Note: In this handler method, method attribute is not specified since it is not required for HTTP GET
method.
Note: Since PRG pattern is used, even if browser is reloaded, create completion screen is only re-displayed
To place multiple buttons on a single form, send HTTP parameter to identify the corresponding button and so
that the handler method of controller can be switched. An example of Create button and Back button on input
confirmation screen of sample application is explained here.
‘Create’ button to perform ‘user creation’ and ‘Back’ button to redisplay ‘create form’ exists on the form of input
confirmation screen as shown below.
To redisplay ‘create form’ using request ( "/abc/create?redo" ) when Back button is clicked, the following
code is required in HTML form.
For the operations when Back button is clicked, refer to Implementing ‘redisplay of form’.
Source-code of controller after implementing create process of sample application are shown below.
Fetching list of Entities, Fetching detail of Entity, Entity update, Entity delete are implemented using the same
guidelines.
@Controller
@RequestMapping("abc")
public class AbcController {
@ModelAttribute
public AbcForm setUpAbcForm() {
return new AbcForm();
}
Model model) {
if (result.hasErrors()) {
return createRedo(form, model);
}
// omitted
return "abc/createConfirm";
}
The arguments of handler method can be used to fetch various values; however, as a principle rule, the following
should not be fetched using arguments of handler method of controller.
• ServletRequest
• HttpServletRequest
• org.springframework.web.context.request.WebRequest
• org.springframework.web.context.request.NativeWebRequest
• java.io.InputStream
• java.io.Reader
• java.io.OutputStream
• java.io.Writer
• java.util.Map
• org.springframework.ui.ModelMap
Note: When generalized values like getAttribute/setAttribute of HttpServletRequest and get/put of Map
are allowed, liberal use of these can degrade the maintainability of the project with an increase in project size.
For the above reason, using HttpSession is not reccomended in the case when there are alternatives.
When common parameters (request parameters) need to be stored in JavaBean and passed as an argument to a
method of controller, it can be implemented using Implementing HandlerMethodArgumentResolver as described
later.
• SampleController.java
@RequestMapping("hello")
public String hello(Model model) { // (1)
model.addAttribute("hello", "Hello World!"); // (2)
model.addAttribute(new HelloBean("Bean Hello World!")); // (3)
return "sample/hello"; // returns view name
}
• hello.jsp
(1)
(2) Call addAttribute method of Model object received as argument, and add the data to
Model object.
For example, "HelloWorld!" string is added to the attribute name "hello".
(3) If first argument of addAttribute method is omitted, the class name beginning with lower
case letter will become the attribute name.
For example, the result of model.addAttribute("helloBean", new
HelloBean()); is same as the result of model.addAttribute(new
HelloBean());
(4) In View (JSP), it is possible to acquire the data added to Model object by specifying
“${Attribute name}”.
For example, HTML escaping is performed using “${f:h(Attribute name)}” function of EL
expression.
For details of functions of EL expression that perform HTML escaping, refer to Cross Site
Scripting.
(5) The values of JavaBean stored in Model can be acquired by specifying “${Attribute
name.property name}”.
Note: Even though the Model is not used, it can be specified as an argument. Even if it is not required
at the initial stage of implementation, it can be used later (so that the signature of methods need not be
changed in future).
Note: The value can also be referred from the module which is not managed under Spring MVC
To retrieve values from URL path, add @PathVariable annotation to argument of handler method of
controller.
In order to retrieve values from the path using @PathVariable annotation, value of @RequestMapping
annotation must contain those values in the form of variables (for example, {id}).
@RequestMapping("hello/{id}/{version}") // (1)
public String hello(
@PathVariable("id") String id, // (2)
@PathVariable Integer version, // (3)
Model model) {
// do something
return "sample/hello"; // returns view name
}
(1) Specify the portion to be extracted as path variable in the value of @RequestMapping
annotation. Specify path variable in “{variable name}” format.
For example, 2 path variables such as "id" and "version" are specified.
(3) Value attribute of @PathVariable annotation can be omitted. When it is omitted, the
argument name is considered as the request parameter name.
In the above example, when the URL "sample/hello/aaaa/1" is accessed, value "1" is
passed to argument “version”.
However, in this method compilation needs to be done by specifying either of:
Note: Binding argument can be of any data type other than string. In case of different data type,
org.springframework.beans.TypeMismatchException is thrown and default response is
400 (Bad Request). For example, when the URL "sample/hello/aaaa/v1" is accessed, an ex-
ception is thrown since "v1" cannot be converted into Integer type.
Warning: When omitting the value attribute of @PathVariable annotation, the application to be
deployed needs to be compiled by specifying -g option or -parameters option which is added
from Java8. When these options are specified, there is a likely impact on memory and processing
performance since information or processing required for debugging gets appended to the class after
compilation. Basically, it is recommended to explicitly specify the value attribute.
@RequestMapping("bindRequestParams")
public String bindRequestParams(
@RequestParam("id") String id, // (1)
@RequestParam String name, // (2)
@RequestParam(value = "age", required = false) Integer age, // (3)
@RequestParam(value = "genderCode", required = false, defaultValue = "unknown") Strin
Model model) {
// do something
return "sample/hello"; // returns view name
}
(1) Specify request parameter name in the value attribute of @RequestParam annotation.
For example, when the URL "sample/hello?id=aaaa" is accessed, the string "aaaa"
is passed to argument “id”.
(2) value attribute of @RequestParam annotation can be omitted. When it is omitted, the
argument name becomes the request parameter name.
For example, when the URL "sample/hello?name=bbbb&...." is accessed, string
"bbbb" is passed to argument “name”.
However, in this method compilation needs to be done by specifying either of:
(3) By default, an error occurs if the specified request parameter does not exist. When request
parameter is not required, specify false in the required attribute.
For example, when it is accessed where request parameter age does not exist, null is passed
to argument “age”.
(4) When default value is to be used if the specified request parameter does not exist, specify the
default value in defaultValue attribute.
For example, when it is accessed where request parameter genderCode does not exist,
"unknown" is passed to argument “genderCode”.
Note: Binding argument can be of any data type. In case the data type do not match,
org.springframework.beans.TypeMismatchException is thrown and default response is
400 (Bad Request). For example, when "sample/hello?age=aaaa&..." URL is accessed, ex-
Binding to form object must be done only when any of the following conditions are met.
• If request parameter is not an item in HTML form, however, input validation other than mandatory check
needs to be performed.
• If error details of input validation error needs to be output for each parameter.
• If there are 3 or more request parameters. (maintenance and readability point of view)
Following is an example that shows the difference between handler method that fetches each request parameter
using @RequestParam and the same handler method when fetching request parameters in a form object
Handler method that receives request parameter separately using @RequestParam is as shown below.
@RequestMapping("bindRequestParams")
public String bindRequestParams(
@RequestParam("id") String id,
@RequestParam String name,
@RequestParam(value = "age", required = false) Integer age,
@RequestParam(value = "genderCode", required = false, defaultValue = "unknown") Strin
Model model) {
// do something
return "sample/hello"; // returns view name
}
Note: Request parameter name should match with form object property name.
Make changes such that request parameters which were being fetched individually using
@RequestParam now get fetched as form object.
@RequestMapping("bindRequestParams")
public String bindRequestParams(@Validated SampleForm form, // (1)
BindingResult result,
Model model) {
// do something
return "sample/hello"; // returns view name
}
(1)
Note: When form object is used as argument, unlike @RequestParam, mandatory check is not per-
formed. When using form object, Performing input validation should be performed as described below.
Warning: Domain objects such as Entity, etc. can also be used as form object without any changes required.
However, the parameters such as password for confirmation, agreement confirmation checkbox, etc. should
exist only on WEB screen. Since the fields depending on such screen items should not be added to domain
objects, it is recommended to create class for form object separate from domain object. When a domain object
needs to be created from request parameters, values must first be received in form object and then copied to
domain object from form object.
When performing input validation for the form object, add @Validated annotation to form object ar-
gument, and specify org.springframework.validation.BindingResult (Hereafter called as
BindingResult) to argument immediately after form object argument.
Add annotations required in input validation to the fields of form object class.
@NotNull
@Size(min = 10, max = 10)
private String id;
@NotNull
@Size(min = 1, max = 10)
private String name;
@Min(1)
@Max(100)
private Integer age;
@RequestMapping("bindRequestParams")
public String bindRequestParams(@Validated SampleForm form, // (1)
BindingResult result, // (2)
Model model) {
if (result.hasErrors()) { // (3)
return "sample/input"; // back to the input view
}
// do something
return "sample/hello"; // returns view name
}
(2)
(3)
To redirect after executing a handler method ofcontroller and to pass data along with it, fetch
org.springframework.web.servlet.mvc.support.RedirectAttributes (Henceforth called
as RedirectAttributes) as an argument of handler method, and add the data to RedirectAttributes
object.
• SampleController.java
@RequestMapping("hello")
public String hello(RedirectAttributes redirectAttrs) { // (1)
redirectAttrs.addFlashAttribute("hello", "Hello World!"); // (2)
redirectAttrs.addFlashAttribute(new HelloBean("Bean Hello World!")); // (3)
return "redirect:/sample/hello?complete"; // (4)
}
• complete.jsp
(1)
(3) If first argument of addFlashAttribute method is omitted, the class name beginning with
lower case letter becomes the attribute name.
For example, the result of model.addFlashAttribute("helloBean", new
HelloBean()); is same as model.addFlashAttribute(new HelloBean());.
(4) Send a redirect request to another URL which will display the next screen instead of displaying
screen (View) directly.
(5) In the handler method after redirection, return view name of the screen that displays the data
added in (2) and (3).
(6) In the View (JSP) side, the data added to RedirectAttributes object can be obtained by
specifying “${attribute name}”.
For example, HTML escaping is performed using “${f:h(attribute name)}” function of EL
expression.
For the details of functions of EL expression that performs HTML escaping, refer to Cross Site
Scripting.
(7) The value stored in RedirectAttributes can be obtained from JavaBean by using
“${Attribute name.Property name}”.
Warning: The data cannot be passed to redirect destination even though it is added to Model.
Note: It is similar to the addAttribute method of Model. However survival time of data differs. In
addFlashAttribute of RedirectAttributes, the data is stored in a scope called flash scope. Data of
only 1 request (G in PRG pattern) can be referred after redirect. The data from the second request onwards is
deleted.
When request parameters are to be set dynamically to redirect destination, add the values to be passed to
RedirectAttributes object of argument.
@RequestMapping("hello")
public String hello(RedirectAttributes redirectAttrs) {
String id = "aaaa";
redirectAttrs.addAttribute("id", id); // (1)
// must not return "redirect:/sample/hello?complete&id=" + id;
return "redirect:/sample/hello?complete";
}
(1) Specify request parameter name in argument name and request parameter value
in argument ‘‘value and call addAttribute method of RedirectAttributes
object.
In the above example, it is redirected to "/sample/hello?complete&id=aaaa".
To insert values in redirect destination URL path dynamically, add the value to be inserted in
RedirectAttributes object of argument as shown in the example to set request parameters.
@RequestMapping("hello")
public String hello(RedirectAttributes redirectAttrs) {
String id = "aaaa";
redirectAttrs.addAttribute("id", id); // (1)
// must not return "redirect:/sample/hello/" + id + "?complete";
return "redirect:/sample/hello/{id}?complete"; // (2)
}
(1) Specify attribute name and the value using addAttribute method of
RedirectAttributes object.
(2) Specify the path of the variable “{Attribute name}” to be inserted in the redirect URL.
In the above example, it is redirected to "/sample/hello/aaaa?complete".
Add @CookieValue annotation to the argument of handler method to acquire the values from a cookie.
@RequestMapping("readCookie")
public String readCookie(@CookieValue("JSESSIONID") String sessionId, Model model) { // (1)
// do something
return "sample/readCookie"; // returns view name
}
(1) Specify name of the cookie in the value attribute of @CookieValue annotation.
In the above example, “JSESSIONID” value is passed from cookie to sessionId argument.
Note:
As in the case of @RequestParam , it has required attribute and defaultValue attribute. Also, the data type of the a
Refer to Retrieving request parameters individually for details.
To write values in cookie, call addCookie method of HttpServletResponse object directly and add the
value to cookie.
Since there is no way to write to cookie in Spring MVC (3.2.3 version), ** Only in this case,
HttpServletResponse can fetched as an argument of handler method of controller.**
@RequestMapping("writeCookie")
public String writeCookie(Model model,
HttpServletResponse response) { // (1)
Cookie cookie = new Cookie("foo", "hello world!");
response.addCookie(cookie); // (2)
// do something
return "sample/writeCookie";
}
(1)
Pagination related information is required for the requests performing list search.
Fetching org.springframework.data.domain.Pageable (henceforth called as Pageable) object
as an argument of handler method enables to handle pagination related information (page count, fetch record
count) easily.
Model object or RedirectAttributes object can be obtained as an argument of handler method and re-
sult message of business logic execution can be displayed by adding ResultMessages object to Model or
RedirectAttributes.
For return values of handler methods, various values can be fetched ; however, only the following values should
be used.
• HTML response
HTML response
To get HTML response to display the output of handler method, it has to return view name of JSP.
ViewResolver when generating HTML using JSP will be an inherited class of UrlBasedViewResolver
(InternalViewResolver and‘‘TilesViewResolver‘‘, etc).
An example using InternalViewResolver for JSP is given below; however, it is recommended to use
TilesViewResolver when the screen layout is in a templated format.
Refer to Screen Layout using Tiles for the usage of TilesViewResolver.
• spring-mvc.xml
Example of definition when using <mvc:view-resolvers> element added from Spring Framework
4.1
<mvc:view-resolvers>
<mvc:jsp prefix="/WEB-INF/views/" /> <!-- (5) -->
</mvc:view-resolvers>
• SampleController.java
@RequestMapping("hello")
public String hello() {
// omitted
return "sample/hello"; // (6)
}
(1)
Specify base directory (prefix of file path) where JSP files are stored.
By specifying prefix, there is no need to specify physical storage location of JSP files at the time
(2)
of returning View name in Controller.
Define InternalViewResolver for JSP using <mvc:jsp> element added from Spring
Framework 4.1.
(5)
• In prefix attribute, specify base directory (prefix of file path) where JSP file is stored.
• It need not be explicitly specified in suffix attribute as ".jsp" is used as default value.
Note: HTML output is generated using JSP in the above example, however, even if HTML is generated using
other template engine such as Velocity, FreeMarker, return value of handler method will be "sample/hello".
ViewResolver takes care of task to determine which template engine is to be used.
The solution to create a separate ViewResolver to resolve a view using its view name, however,
BeanNameViewResolver provided by Spring Framework is recommended.
Refer to File Download for the details of download processing.
• spring-mvc.xml
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
<property name="order" value="1" />
</bean>
Example of definition when using <mvc:view-resolvers> element added from Spring Framework
4.1
<mvc:view-resolvers>
<mvc:bean-name /> <!-- (3) -->
<mvc:jsp prefix="/WEB-INF/views/" />
</mvc:view-resolvers>
• SampleController.java
@RequestMapping("report")
public String report() {
// omitted
return "sample/report"; // (4)
}
• XxxExcelView.java
@Component("sample/report") // (5)
public class XxxExcelView extends AbstractExcelView { // (6)
@Override
protected void buildExcelDocument(Map<String, Object> model,
HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
HSSFSheet sheet;
HSSFCell cell;
sheet = workbook.createSheet("Spring");
sheet.setDefaultColumnWidth(12);
// write a text at A1
cell = getCell(sheet, 0, 0);
setText(cell, "Spring-Excel test");
When View name "sample/report" is the return value of handler method, the data gener-
ated by View instance which is registered in step (5), is responded as download data.
(4)
Register View object as Bean by specifying View name to the name of component.
In above example, x.y.z.app.views.XxxExcelView instance is registered as a bean
(5)
with bean name (view name) as "sample/report" .
Refer to Domain Layer Implementation for the details of implementation of business logic.
Note: Controller should be used only for routing purposes (mapping requests to corresponding business logic)
and deciding the screen transition for each request as well as setting model data. Thereby, controller should be
simple as much as possible. By consistently following this policy, the contents of controller become clear which
ensures maintainability of controller even if the size of development is large.
Correlation check of input values should be done using Validation class which implements
org.springframework.validation.Validator interface.
Bean Validation can also be used for correlation check of input values.
Refer to Input Validation for the details of implementation of correlation check.
The implementation of correlation check itself should not be written in the handler method of controller.
However, it is necessary to add the Validator to
org.springframework.web.bind.WebDataBinder.
@Inject
PasswordEqualsValidator passwordEqualsValidator; // (1)
@InitBinder
protected void initBinder(WebDataBinder binder){
binder.addValidators(passwordEqualsValidator); // (2)
}
(1)
Execute business logic by injecting the Service in which business logic is implemented and calling the injected
Service method.
@Inject
SampleService sampleService; // (1)
@RequestMapping("hello")
public String hello(Model model){
String message = sampleService.hello(); // (2)
model.addAttribute("message", message);
return "sample/hello";
}
(2)
In this guideline, it is recommended to bind the data sent by HTML form to form object instead of the domain
object.
Therefore, the controller should perform the process of reflecting the values of form object to domain object
which is then passed to the method of service class.
@RequestMapping("hello")
public String hello(@Validated SampleForm form, BindingResult result, Model model){
// omitted
Sample sample = new Sample(); // (1)
sample.setField1(form.getField1());
sample.setField2(form.getField2());
sample.setField3(form.getField3());
// ...
// and more ...
// ...
String message = sampleService.hello(sample); // (2)
model.addAttribute("message", message); // (3)
return "sample/hello";
}
(1) Create domain object and reflect the values bound to form object in the domain object.
The process of reflecting values to domain object should be implemented by the handler method of controller.
However considering the readability of processing
method in case of large amount of code, it is recommended to delegate the process to Helper class.
Example of delegating the process to Helper class is shown below:
• SampleController.java
@Inject
SampleHelper sampleHelper; // (1)
@RequestMapping("hello")
public String hello(@Validated SampleForm form, BindingResult result){
// omitted
String message = sampleHelper.hello(form); // (2)
model.addAttribute("message", message);
return "sample/hello";
}
• SampleHelper.java
@Inject
SampleService sampleService;
(1)
Value is reflected to the domain object by calling the method of the injected Helper class. Dele-
gating the process to Helper class enables to keep the implementation of controller simple.
(2)
Call the Service class method to execute the business logic after creating domain object.
(3)
Note: Bean conversion functionality can be used as an alternative way to delegate the process of reflecting
form object values, to Helper class. Refer to Bean Mapping (Dozer) for the details of Bean conversion
functionality.
In this guideline, it is recommended that form object (and not domain object) must be used to for that data which
is to be bound to HTML form.
For this, it is necessary to reflect the values of domain object (returned by service layer) to form object. This
conversion should be performed in controller class.
@RequestMapping("hello")
public String hello(SampleForm form, BindingResult result, Model model){
// omitted
Sample sample = sampleService.getSample(form.getId()); // (1)
form.setField1(sample.getField1()); // (2)
form.setField2(sample.getField2());
form.setField3(sample.getField3());
// ...
// and more ...
// ...
model.addAttribute(sample); // (3)
return "sample/hello";
}
(1) Call the method of service class in which business logic is implemented and fetch domain
object.
(3) When there are fields only for display, add domain object to Model so that data can be referred.
Note: In JSP, it is recommended to refer the values from domain object instead of form object for the
fields to be only displayed on the screen.
The process of reflecting value to form object should be implemented by the handler method of controller. How-
ever considering the readability of handler method in case of large amount of code, it is recommended to delegate
the process to Helper class method.
• SampleController.java
@RequestMapping("hello")
public String hello(@Validated SampleForm form, BindingResult result){
// omitted
Sample sample = sampleService.getSample(form.getId());
sampleHelper.applyToForm(sample, form); // (1)
model.addAttribute(sample);
return "sample/hello";
}
• SampleHelper.java
(1) Call the method to reflect the values of domain object to form object.
Note: Bean conversion functionality can be used as an alternative method to delegate the process to Helper
class. Refer to Bean Mapping (Dozer) for the details of Bean conversion functionality.
Form object is the object (JavaBean) which represents HTML form and plays the following role.
1. Holds business data stored in the database so that it can be referred by HTML form (JSP).
2. Holds request parameters sent by HTML form so that they can be referred by handler method of
controller.
Create form object as a JavaBean. Spring Framework provides the functionality to convert and bind the request
parameters (string) sent by HTML form to the format defined in form object. Hence, the fields to be defined in
form object need not only be in java.lang.String format.
Tip: Regarding the mechanism provided by Spring Framework that performs format conversion
Spring Framework executes format conversion using the following 3 mechanisms and supports conversion
to basic format as standard. Refer to linked page for the details of each conversion function.
• java.beans.PropertyEditor implementations
Warning: In form object, it is recommended to maintain only the fields of HTML form and
not the fields which are just displayed on the screen. If display only fields are also maintained
in form object, more memory will get consumed at the time of storing form object in HTTP ses-
sion object causing memory exhaustion. In order to display the values of display only fields on the
screen, it is recommended to add objects of domain layer (such as Entity) to request scope by using
(Model.addAttribute).
Number format can be specified for each field using @NumberFormat annotation.
(1) Specify the number format of request parameter sent by HTML form. For example, binding of
value formatted by ”,” is possible since “”#, #”” format is specified as pattern.
When value of request parameter is “”1,050””, Integer object of “”1050”” will bind to the
property price of form object.
Date and time format for each field can be specified using @DateTimeFormat annotation.
pattern Specify Java date and time format. Refer to ‘Javadoc of SimpleDateFormat
2.
<http://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html>’_for
details.
style
3.
Specify style of date and time as two-digit string.
First digit will be style of date and second digit will be style of time.
Values that can be specified as style are given below.
Warning: @DateTimeFormat’s formatter is not strict in case of pattern attribute specified with
java.time.LocalDate on JSR-310 Date and Time API ("20150229" is invalid , but it will be regarded
as 28st February, 2015). Specifications are improved in Spring Framework 4.3 , but are affected because they
use Spring Framework 4.2 in TERASOLUNA Server Framework for JAVA (5.x). For details reference to
‘@DateTimeFormat’s JSR-310 formatter is not strict in case of pattern‘
@InitBinder // (1)
public void initWebDataBinder(WebDataBinder binder) {
binder.registerCustomEditor(
Long.class,
new CustomNumberEditor(Long.class, new DecimalFormat("#,#"), true)); // (2)
}
@InitBinder("sampleForm") // (3)
public void initSampleFormWebDataBinder(WebDataBinder binder) {
// ...
}
(1) If a method with @InitBinder annotation is provided, it is called before executing the
binding process and thereby default operations can be customized.
(2) For example, "#. #" format is specified for a field of type Long. This enables binding of
value formatted with ”,”.
(3) Default operation for each form object can be customized by specifying it in the value attribute
of @InitBinder annotation.
In the above example, the method is called before binding form object "sampleForm".
Since form object is validated using Bean Validation, it is necessary to specify the annotation which indicates
constraints of the field. Refer to Input Validation for the details of input validation.
Form object can also be called as form-backing bean and binding can be performed using @ModelAttribute
annotation. Initialize form-backing bean by the method having @ModelAttribute annotation. In this guide-
line, such methods are called as ModelAttribute methods and defined with method names like setUpXxxForm.
@ModelAttribute // (1)
public SampleForm setUpSampleForm() {
SampleForm form = new SampleForm();
// populate form
return form;
}
@ModelAttribute("xxx") // (2)
public SampleForm setUpSampleForm() {
SampleForm form = new SampleForm();
// populate form
return form;
}
@ModelAttribute
public SampleForm setUpSampleForm(
@CookieValue(value = "name", required = false) String name, // (3)
@CookieValue(value = "age", required = false) Integer age,
@CookieValue(value = "birthDate", required = false) Date birthDate) {
SampleForm form = new SampleForm();
form.setName(name);
form.setAge(age);
form.setBirthDate(birthDate);
return form;
}
(1) Class name beginning with lower case letter will become the attribute name to add to Model.
In the above example, "sampleForm" is the attribute name.
The returned object is added to Model and an appropriate process
model.addAttribute(form) is executed.
(2) When attribute name is to be specified to add to Model, specify it in the value attribute of
@ModelAttribute annotation. In the above example, "xxx" is the attribute name.
For returned object, appropriate process “model.addAttribute(“xxx”, form)”is executed and it is
returned to Model.
When attribute name other than default value is specified, it is necessary to specify
@ModelAttribute("xxx") at the time of specifying form object as an argument of
handler method.
(3) ModelAttribute method can pass the parameters required for initialization as with the case of
handler method. In the above example, value of cookie is specified using @CookieValue
annotation.
Note: When form object is to be initialized with default values, it should be done using ModelAttribute method.
In point (3) in above example , value is fetched from cookie, However, fixed value defined in constant class can
be set directly.
Note: Multiple ModelAttribute methods can be defined in the controller. Each method is executed before calling
handler method of controller.
Warning: If ModelAttribute method is executed for each request, initialization needs to be repeated for each
request and unnecessary objects will get created. So, for form objects which are required only for specific
requests, should be created inside handler method of controller and not through the use of ModelAttribute
method.
It is possible to bind form object added to the Model to HTML form(JSP) using <form:xxx> tag.
For the details of <form:xxx> tag, refer to Using Spring’s form tag library.
<form:form modelAttribute="sampleForm"
action="${pageContext.request.contextPath}/sample/hello"> <!-- (2) -->
Id : <form:input path="id" /><form:errors path="id" /><br /> <!-- (3) -->
Name : <form:input path="name" /><form:errors path="name" /><br />
Age : <form:input path="age" /><form:errors path="age" /><br />
Gender : <form:input path="genderCode" /><form:errors path="genderCode" /><br />
Birth Date : <form:input path="birthDate" /><form:errors path="birthDate" /><br />
</form:form>
(1)
(3)
It is possible to bind the request parameters sent by HTML form to form object and pass it as an argument to the
handler method of controller.
@RequestMapping("hello")
public String hello(
@Validated SampleForm form, // (1)
BindingResult result,
Model model) {
if (result.hasErrors()) {
return "sample/input";
}
// process form...
return "sample/hello";
}
@ModelAttribute("xxx")
public SampleForm setUpSampleForm() {
SampleForm form = new SampleForm();
// populate form
return form;
}
@RequestMapping("hello")
public String hello(
@ModelAttribute("xxx") @Validated SampleForm form, // (2)
BindingResult result,
Model model) {
// ...
}
(1) Form object is passed as an argument to the handler method of controller after reflecting
request parameters to the form object.
(2) When the attribute name is specified in ModelAttribute method, it is necessary to explicitly
specify attribute name of form object as @ModelAttribute("xxx").
Warning: When attribute name specified by ModelAttribute method and attribute name specified in the
@ModelAttribute("xxx") in the argument of handler method are different, it should be noted that a
new instance is created other than the instance created by ModelAttribute method. When attribute name is not
specified with @ModelAttribute in the argument to handler method, the attribute name is deduced as the
class name with first letter in lower case.
Error (including input validation error) that occurs while binding request parameter sent by HTML form to form
object, is stored in org.springframework.validation.BindingResult.
@RequestMapping("hello")
public String hello(
@Validated SampleForm form,
BindingResult result, // (1)
Model model) {
if (result.hasErrors()) { // (2)
return "sample/input";
}
// ...
}
It is also possible to determine field errors, global errors (correlated check errors at class level) separately. These
can be used separately if required.
Implementing JSP
Implement View using JSP to generate response(HTML) as per the requirement of the client.
Use the class provided by Spring Framework as the ViewResolver for calling JSP. Refer to HTML response
for settings of ViewResolver.
• Displaying codelist
In this chapter, usage of main JSP tag libraries are described. However, refer to respective documents for the
detailed usage since all JSP tag libraries are not described here.
JSTL
3. • http://download.oracle.com/otndocs/jcp/jstl-1.2-mrel2-
eval-oth-JSpec/
Create a JSP that contains directive declaration which are required by all the JSP files of the project. By specifying
this JSP in <jsp-config>/<jsp-property-group>/<include-prelude> element of web.xml,
eliminates the need to declare these directives and each and every JSP file of the project. Further, this file is
provided in blank project also.
• include.jsp
• web.xml
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>false</el-ignored>
<page-encoding>UTF-8</page-encoding>
<scripting-invalid>false</scripting-invalid>
<include-prelude>/WEB-INF/views/common/include.jsp</include-prelude> <!-- (4) -->
</jsp-property-group>
</jsp-config>
(1) JSP tag libraries of JSTL are declared. In this example, core and fmt are used.
(2) JSP tag libraries of Spring Framework are declared. In this example, spring, form and sec
are used.
To display the value stored in Model (form object or domain object) in HTML, use EL expressions or JSP tag
libraries provided by JSTL.
• SampleController.java
@RequestMapping("hello")
public String hello(Model model) {
model.addAttribute(new HelloBean("Bean Hello World!")); // (1)
return "sample/hello"; // returns view name
}
• hello.jsp
(2) In View(JSP), data added to the Model object can be retrieved by describing ${Attribute
name.Property name of JavaBean}.
In this example, HTML escaping is performed using ${f:h(Attribute
name.Property name of JavaBean)} function of EL expression.
Note: Since HTML escaping function (f:h) is provided in the common components, always use it if
EL expressions are used to output values in HTML. For details of function of EL expression that perform
HTML escaping, refer to Cross Site Scripting.
(1) Specify the values fetched using EL expressions in value attribute of <c:out> tag. HTML
escaping is also performed.
Note: Refer to CHAPTER 4 General-Purpose Actions of JavaServer Pages Standard Tag Li-
brary(Version 1.2)for the details of <c:out>.
Note: Refer to CHAPTER 9 Formatting Actions of JavaServer Pages Standard Tag Library(Version
1.2)for the details of <fmt:formatNumber>.
Use JSP tag library provided by JSTL to output format date and time value.
(1) Specify the value fetched using EL expression in value attribute of <fmt:formatDate>
tag. Specify the format to be displayed in pattern attribute. In this example,
“yyyy-MM-dd” is specified.
When value received for ${helloBean.dateItem} is 2013-3-2, “2013-03-02” is
displayed on the screen.
Note: Refer to CHAPTER 9 Formatting Actions of JavaServer Pages Standard Tag Library(Version 1.2)
for details of <fmt:formatDate>.
Note: JSP tag library provided by Joda Time should be used to use org.joda.time.DateTime as date and
time object type. Refer to Date Operations (Joda Time) for the details of Joda Time.
When a request URL (a URL for calling Controller method) is to be set for action attribute of <form> el-
ement(<form:form> element of JSP tag library) of HTML and href attribute of <a> element, a URL is
generated using either of the methods described below.
• Build a request URL using EL function which has been added from Spring Framework 4.1
Note: Although either of these methods can be used, using both the methods together in a single application
should be avoided since it may result in decrease in maintainability.
An implementation sample of the Controller method used further in the description is shown.
In the description hereafter, an implementation method is described wherein a request URL is generated for
calling the method given below.
package com.example.app.hello;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("hello")
@Controller
public class HelloController {
// (1)
@RequestMapping({"", "/"})
public String hello() {
return "hello/home";
}
(1)
First, the method to build a request URL using a character string is explained.
Tip:
are available as JSP tag libraries which build URL. A request URL can also be built using these JSP tag
libraries.
When it is necessary to build a request URL dynamically, a URL is preferably built by using these JSP tag
libraries.
Building a request URL by using EL function added from Spring Framework 4.1
Next, a method to build a request URL using EL function (spring:mvcUrl) added from Spring Framework
4.1 is described.
If spring:mvcUrl function is used, a request URL can be built by linking with meta information of Controller
method (method signature and annotation etc).
Request mapping name must not be duplicated. When the name is duplicated, a unique name must be
specified in name attribute of @RequestMapping annotation.
When a request mapping name assigned to Controller method is to be verified, settings given below should
be added to logback.xml.
<logger name="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandler
<level value="trace" />
</logger>
If the program is rebooted after applying the settings given above, the log shown below is output.
Use JSP tag library provided by Spring Framework to bind form object to HTML form and to display the values
stored in form object.
<form:form action="${pageContext.request.contextPath}/sample/hello"
modelAttribute="sampleForm"> <%-- (1) --%>
Id : <form:input path="id" /> <%-- (2) --%>
</form:form>
Specify name of property to bind in the path attribute of <form:xxx> tag. xxx part changes
along with each input element.
(2)
Note: For the details of <form:form> , <form:xxx> tag refer to Using Spring’s form tag library.
To display the contents of input validation error, use JSP tag library provided by Spring Framework.
<form:form action="${pageContext.request.contextPath}/sample/hello"
modelAttribute="sampleForm">
Id : <form:input path="id" /><form:errors path="id" /><%-- (1) --%>
</form:form>
(1) Specify name of the property to display the error in path attribute of <form:errors> tag.
To display the message notifying the output of processing the request, use JSP tag library provided in common
components.
<div class="messages">
<h2>Message pattern</h2>
<t:messagesPanel /> <%-- (1) --%>
</div>
Displaying codelist
To display the codelist (provided in common components), use JSP tag library provided by Spring Framework.
Codelist can be referred from JSP in the same way as java.util.Map interface.
Refer to Codelist for details.
<form:select path="orderStatus">
<form:option value="" label="--Select--" />
<form:options items="${CL_ORDERSTATUS}" /> <%-- (1) --%>
</form:select>
Label part is displayed on the screen for the value selected in select box.
Strings for screen name, element name and guidance etc can be directly written in JSP when internationalization
is not required.
However, when internationalization is required, display the values acquired from property file using JSP tag
library provided by Spring Framework.
• properties
# (1)
label.orderStatus=Order status
• jsp
(1)
Note: The value specified in text attribute is displayed when property value could not be acquired.
When display is to be switched according to some value in model, use JSP tag library provided by JSTL.
Switch display using <c:if> tag or <c:choose> provided by JSP tag library of JSTL.
<c:choose>
<c:when test="${customer.type == 'premium'}"> <%-- (1) --%>
<%-- ... --%>
</c:when>
<c:when test="${customer.type == 'general'}">
<%-- ... --%>
</c:when>
<c:otherwise> <%-- (2) --%>
<%-- ... --%>
</c:otherwise>
</c:choose>
When result of test attribute of all <c:when> tags is false, <c:otherwise> tag is eval-
uated.
(2)
Note: Refer to CHAPTER 5 Conditional Actions of JavaServer Pages Standard Tag Library(Version
1.2)for details.
To repeat display of collection stored in model, use JSP tag library provided by JSTL.
Repeated display can be done using <c:forEach> provided by JSP tag library of JSTL.
<table>
<tr>
<th>No</th>
<th>Name</th>
</tr>
<c:forEach var="customer" items="${customers}" varStatus="status"> <%-- (1) --%>
<tr>
<td>${status.count}</td> <%-- (2) --%>
<td>${f:h(customer.name)}</td> <%-- (3) --%>
</tr>
</c:forEach>
</table>
This is the value acquired from the object stored in variable specified by var attribute of
<c:forEach> tag.
(3)
Note: Refer to CHAPTER 6 Iterator Actions of JavaServer Pages Standard Tag Library(Version 1.2)for
details.
To display links of pagination on the screen while displaying the list, use JSP tag library provided in common
components.
Display the link for pagination using <t:pagination> provided in common components. Refer to Pagination
for details.
To switch display according to authority of the user who has logged in, use JSP tag library provided by Spring
Security.
Switch display using <sec:authorize> provided by Spring Security. Refer to Authorization for details.
Implementing JavaScript
When it is necessary to control screen items (controls of hide/display, activate/deactivate, etc.) after screen ren-
dering, control items using JavaScript.
Todo
TBD
It is recommended to specify attribute values related to screen design in style sheet (css file) and not in
JSP(HTML) directly.
In JSP(HTML), specify id attribute to identify the items uniquely and class attribute which indicates
classification of elements.
While, specify values related to actual location of elements or appearance should be specified in style sheet (css
file).
By configuring in this way, it is possible to reduce the design related aspects from the implementation of JSP.
Simultaneously, if there is any change in design, only style sheet (css file) is modified and not JSP.
Note: When form is created using <form:xxx>, id attribute will be set automatically. Application developer
should specify class attribute.
Here, common logic indicates processes which are required to be executed before and after execution the con-
troller.
Common processes independent of Spring MVC are implemented using Servlet Filter.
However, when common processes are to be executed only for the certain requests mapped with handler method
of controller,
then it should be implemented using Handler Interceptor and not Servlet Filter.
• java
• web.xml
(2)
(3)
• web.xml
<filter>
<filter-name>clientInfoPutFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- (1
</filter>
<filter-mapping>
<filter-name>clientInfoPutFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
• applicationContext.xml
Add the Servlet Filter class to Bean definition file (applicationContext.xml). At this
time, id attribute of bean definition should be assigned with the filter name (value specified in
(2)
<filter-name> tag) specified in web.xml.
Implementing HandlerInterceptor
HandlerInterceptor can execute the process keeping in mind the following 3 points.
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method m = handlerMethod.getMethod();
logger.info("[SUCCESS CONTROLLER] {}.{}", new Object[] {
m.getDeclaringClass().getSimpleName(), m.getName()});
}
• spring-mvc.xml
<mvc:interceptors>
<!-- ... -->
<mvc:interceptor>
<mvc:mapping path="/**" /> <!-- (2) -->
(2)
Specify the pattern of path, where the created HandlerInterceptor need not be applied.
(3)
(4)
Here, common process indicates the process that should be commonly implemented in all the controllers.
Implementing HandlerMethodArgumentResolver
When an object that is not supported by default in Spring Framework is to be passed as controller argument,
HandlerMethodArgumentResolver is to be implemented in order to enable controller to be able to receive the
argument.
• JavaBean
// ....
• HandlerMethodArgumentResolver
@Override
public boolean supportsParameter(MethodParameter parameter) {
return CommonParameters.class.equals(parameter.getParameterType()); // (3)
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
CommonParameters params = new CommonParameters(); // (4)
params.setParam1(webRequest.getParameter("param1"));
params.setParam2(webRequest.getParameter("param2"));
params.setParam3(webRequest.getParameter("param3"));
return params;
}
• Controller
@RequestMapping(value = "home")
public String home(CommonParameters commonParams) { // (5)
logger.debug("param1 : {}",commonParams.getParam1());
logger.debug("param2 : {}",commonParams.getParam2());
logger.debug("param3 : {}",commonParams.getParam3());
// ...
return "sample/home";
• spring-mvc.xml
<mvc:annotation-driven>
<mvc:argument-resolvers>
<!-- ... -->
<bean class="x.y.z.CommonParametersMethodArgumentResolver" /> <!-- (6) -->
<!-- ... -->
</mvc:argument-resolvers>
</mvc:annotation-driven>
(1)
Implement org.springframework.web.method.support.HandlerMethodArgumentResolv
interface.
(2)
Determine parameter type. For example, when type of JavaBean that retains common parameters
is specified as argument of controller, resolveArgument method of this class is called.
(3)
Fetch the values of request parameters, set the parameters and return the JavaBean that retains
the value of common parameters.
(4)
(5) Specify JavaBean that retains common parameters in the argument of handler method of
controller.
Object returned in step (4) is passed.
Note: When parameters are to be passed commonly to handler methods of all controllers, it is effective to convert
the parameters to JavaBean using HandlerMethodArgumentResolver. Parameters referred here are not restricted
to request parameters.
Implementing “@ControllerAdvice”
In a class with @ControllerAdvice annotation, implement common processes which are to be executed in
multiple Controllers.
Tip: @ControllerAdvice annotation is a mechanism added from Spring Framework 3.2; however, since the
processing was applied to all Controllers, it could only implement common processes of entire application.
From Spring Framework 4.0, it has been improved in such a way that controller can be specified flexibly for
applying common processes. With this improvement, it is possible to implement a common process in various
granularities.
Methods to specify Controller (methods to specify attributes) for applying common processes are described below.
Tip: basePackageClasses attribute / basePackages attribute / value attribute are the at-
tributes to specify base package that stores the Controller for applying common processes. However, when
basePackageClasses attribute is used,
@ControllerAdvice // (1)
@Order(0) // (2)
public class SampleControllerAdvice {
// (3)
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class,
new CustomDateEditor(dateFormat, true));
}
Specify priority for common processes by assigning the @Order annotation. It should be speci-
fied when multiple ControllerAdvice have ordering for example there are dependencies between
(2)
them. Otherwise it is not necessary.
(3)
// (1)
@ExceptionHandler(PessimisticLockingFailureException.class)
public String handlePessimisticLockingFailureException(
PessimisticLockingFailureException e) {
return "error/lockError";
}
• ControllerAdvice
// (1)
@ModelAttribute
public CommonParameters setUpCommonParameters(
@RequestParam(value = "param1", defaultValue="def1") String param1,
@RequestParam(value = "param2", defaultValue="def2") String param2,
@RequestParam(value = "param3", defaultValue="def3") String param3) {
CommonParameters params = new CommonParameters();
params.setParam1(param1);
params.setParam2(param2);
params.setParam3(param3);
return params;
}
• Controller
@RequestMapping(value = "home")
public String home(@ModelAttribute CommonParameters commonParams) { // (2)
logger.debug("param1 : {}",commonParams.getParam1());
logger.debug("param2 : {}",commonParams.getParam2());
logger.debug("param3 : {}",commonParams.getParam3());
// ...
return "sample/home";
}
(2)
Measures should be taken to prevent double submission as the same process gets executed multiple times by
clicking Send button multiple times or refreshing (refresh using F5 button) the Finish screen.
For the problems occurring when countermeasures are not taken and details of implementation method, refer to
Double Submit Protection .
In the default operations of Spring MVC, model (form object, domain object etc.) is not stored in session.
When it is to be stored in session, it is necessary to assign @SessionAttributes annotation to the controller
class.
When input forms are split on multiple screens, usage of @SessionAttributes annotation should be studied
since model (form object, domain object etc.)
can be shared between multiple requests for executing a series of screen transitions.
However, whether to use @SessionAttributes annotation should be determined after confirming the
warning signs of using the session.
For details of session usage policy and implementation at the time of session usage, refer to Session Management
.
The method to create a war file to be deployed on application server and a jar file of env module (module to store
the file environment dependent file) is described below.
In case of a project created using Maven Archetype, the following 2 methods are provided as methods to create a
war file.
• Build method wherein jar file of env module is not included in war file (recommended)
• Build method wherein jar file of env module is included in war file
This guideline recommends Build method wherein jar file of env module is not included in war file. Other build
method apart from those mentioned here can also be used.
However, the war file and jar file to be released in test environment and production environment should not
be created using the functionality provided by IDE such as Eclipse. In some of the IDE functionalities like
Eclipse, class files are created using an independent compiler which has been optimized for development, hence
there could be a risk of unexpected error during the application execution due to difference in the compiler.
1. Make sure to have a multi-project structure. 2.As far as possible, consolidate the configuration files (ex. log-
back.xml, jdbc.properties) having environment dependency in one project. Hereafter, this project is expressed
as *-env.
• ex. terasoluna-tourreservation-env
3. Projects other than *-env never have a setting for environment dependency.
• However, it is allowed to store the environment dependency configuration files for test under
src/test/resources.
• Deploy not only *.jar files but also *.war files as work products in package repository. Consequently, these
jar/war files should not include environment dependencies.
• Store the configuration values required for operations on the developer’s PC in the file under
src/main/resources, as default values.
• Store the configuration files that differ with each environment such as test server, production server, in the
folder other than src/main/resources (ex. configs/test-server). Then, use “profile” function of Maven that
automatically replaces the configuration values depending on environment in order to build *-env-x.y.z.jar
file.
The above structure facilitates proper development in all the scenarios of software development lifecycle.
1. In local development environment, check out both main project and *-env project and include env project
in build path of the main project so as to do coding and testing in the local development environment.
2. On CI server, execute the test and perform packaging using a build tool (Maven) and deploy the artifact in
package repository whenever required.
3. Application can work on test server and production server by adding *-env project built as per the applica-
tion release environment, to the main project which is stored in package repository.
When build is done using Maven, confirm whether home directory of JDK which is used during compilation in
the environment variable JAVA_HOME, has been specified.
If the environment variable is not set or the home directory of JDK having different version has been specified, an
appropriate home directory should be specified in environment variable.
echo %JAVA_HOME%
set JAVA_HOME={Please set home directory of JDK}
echo $JAVA_HOME
JAVA_HOME={Please set home directory of JDK}
Note: It is advisable to set the environment variable JAVA_HOME in the user environment variable of OS user
wherein build is to be done.
Build method wherein jar file of env module is not included in war file
cd C:\work\todo
Specify warpack in Maven profile (-P parameter) and run Maven install.
If the Maven package is run successfully, a war file that does not include jar file of env module is created in the
target directory of web module.
(Example: C:\work\todo\todo-web\target\todo-web.war)
In the above example, install is specified in goal and war file is installed in local repository, however it is
advisable to specify
cd C:\work\todo\todo-env
Specify Profile ID to identify environment in Maven profile (-P parameter) and run Maven package.
If Maven package is run successfully, jar file for the specified environment is created in target directory of env
module.
(Example:
C:\work\todo\todo-env\target\todo-env-1.0.0-SNAPSHOT-test-server.jar)
In case of a project created using Maven Archetype, following profile IDs are defined by default.
• local: Profile for local environment of the developer (for IDE development environment) (default profile)
The above 3 profiles are provided by default; however you can add or modify them as per the environment config-
uration of the system to be developed.
Build method wherein jar file of env module is included in war file
Warning: Points to be noted when including a jar file of env module in war file
When jar file of env module is included in war file, the war file cannot be deployed in other environment;
hence war file should be managed so that it is not deployed to other environment (especially in production
environment) by mistake.
Moreover, when using a method in which war file is created for each environment and released in each en-
vironment, it should be noted that war file released in production environment can never be the war file for
which testing is complete. This is for the re-compilation at the time of creating war file for the production en-
vironment. When creating the war file and releasing the same for each environment, it is especially important
to use the VCS (Version Control System) functionality (Tag functionality etc.) like Git or Subversion and to
establish a mechanism to create a war file which is to be released in production environment and various test
environments, through the use of tested source files.
cd C:\work\todo
In Maven profile (-P parameter), specify Profile ID to identify environmentdefined in env module and
warpack-with-env, and then run the Maven package.
If Maven package is run successfully, war file which includes jar file of env module is created in target directory
of web module.
(Example: C:\work\todo\todo-web\target\todo-web.war)
Deploy
Deploy on Tomcat
Deployment method (procedure) when Tomcat is used as an application server is given below.
1. Specify the profile of Maven as per the AP server environment in which the application is to be released
and build *-env project.
2. Place *-env-x.y.z.jar file built above in the folder of AP server decided in advance. ex. /etc/foo/bar/abcd-
env-x.y.z.jar
4. If Tomcat 7 is used, add /etc/foo/bar/*.jar into class path using VirtualWebappLoader function of the Tom-
cat.
<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
virtualClasspath="/etc/foo/bar/*.jar" />
5. If Tomcat 8 is used, add /etc/foo/bar/*.jar into class path using Resource function of the Tomcat.
<Resources className="org.apache.catalina.webresources.StandardRoot">
<PreResources className="org.apache.catalina.webresources.DirResourceSet"
base="/etc/foo/bar/"
internalPath="/"
webAppMount="/WEB-INF/lib" />
</Resources>
Note:
• When autoDeploy is disabled, Web application does not start by just placing the war file in
[CATALINA_HOME]/webapps. war file should always be unjarred (unzipped).
When releasing the Web application on application servers (Example: WebSphere, WebLogic, JBoss) where
a mechanism for adding a class path for each web application (which is provided in VirtualWebappLoader of
Tomcat) is not provided, the method to release it after adding *-env-x.y.z.jar file under WEB-INF/lib of war file is
the easiest.
1. Specify profile of Maven as per the AP server environment in which application is to be released and build
*-env project.
2. Copy *.war file deployed in the package repository to the working directory.
3. Add it under WEB-INF/lib of war file using add option of jar command as follows.
Note: For a method to deploy a war file on application server, refer to the manual of application server to be used.
Here, a method to embed the jar file of env module in war file using jar command is given.
cd C:\work\todo\todo-env
mvn org.apache.maven.plugins:maven-dependency-plugin:2.5:get^
-DgroupId=com.example.todo^
-DartifactId=todo-web^
-Dversion=1.0.0-SNAPSHOT^
-Dpackaging=war^
-Ddest=target/todo-web.war
If the command is run successfully, the specified war file is copied to the target directory of env module.
(Example: C:\work\todo\todo-env\target\todo-web.war)
Note:
Copy the created jar file to working directory (target\WEB-INF\lib) once and add it to the war file.
mkdir target\WEB-INF\lib
copy target\todo-env-1.0.0-SNAPSHOT-test-server.jar target\WEB-INF\lib\.
cd target
jar -uvf todo-web.war WEB-INF\lib
mkdir -p target/WEB-INF/lib
cp target/todo-env-1.0.0-SNAPSHOT-test-server.jar target/WEB-INF/lib/.
cd target
jar -uvf todo-web.war WEB-INF/lib
The problem when jar command is not found can be resolved using either of the following measures.
• Specify the jar command with full path. In case of Windows, %JAVA_HOME%\bin\jar and in case of
Linux, ${JAVA_HOME}/bin/jar can be specified.
Continuous deployment
Continuous deployment is constantly releasing the target software through continuous looping of project (source
code tree) structure, version control, inspection, build operations and lifecycle management.
During development, release the software of SNAPSHOT version in the package repository and development
AP server and execute the test. To release the software officially, tagging to source code tree in VCS needs be
performed after assigning a version number. In this way, the flow of build and deployment slightly differs in the
snapshot release and official release.
To deploy the application on AP server that provides Web service, irrespective of snapshot version or official
release version, a group of environment dependency configuration files and *.war file should be deployed in a set
as per the target release AP server environment.
Separating the operation of registering libraries (jar, war) without environment dependency settings, in Maven
repository and the operation of actually deploying them on AP server facilitates deployment.
Please note that there are 2 types of repositories in Maven package repository i.e. snapshot repository and release
repository with a few limitations.
• Software of SNAPSHOT version cannot be registered in release repository. release repository also cannot
be registered in snapshot repository.
• In release repository, artifact having the same GAV information can be registered only once.
(GAV=groupId, artifactId, version)
• In snapshot repository, artifact having the same GAV information can be re-registered many times.
Operations of SNAPSHOT version A simple delivery flow of SNAPSHOT version software is as shown in
the following figure.
• In case of compilation error, certain violations of code metrics or in case the test fails, the subsequent
operations should be stopped.
3. Upload (mvn deploy) the artifact (jar, war file) on Maven package repository server. |
Operations of RELEASE version In case of official release, since it is necessary to assign the version number,
the flow becomes slightly more complex than the SNAPSHOT release.
2. Check out the source code from development trunk (or release branch).
• In case of compilation error, certain violations of code metrics or in case the test fails, the subsequent
operations should be stopped.
6. Upload (mvn deploy) the artifact (jar, war file) on Maven package repository server.
Todo
Here, should the version tag of pom.xml of trunk source tree be written at the end till it is replaced by the next
SNAPSHOT version and committed?
Release on Application Server To release the application on AP server that provides Web service, release the
*.war file registered in Maven package repository and the group of environment dependency configuration files in
a set according to the target release AP server environment. This has same flow irrespective of snapshot release or
official release.
1. Download war file of the version to be released from Maven package repository.
2. Check out *-resources project (that consolidates environment dependency configuration files) from VCS.
3. Using “profile” function of Maven, replace the contents with group of configuration files according to the
target release environment, package the resources project and create *-resources-x.y.z.jar.
4. Add the created *-resources-x.y.z.jar file under WEB-INF/lib folder of war file.
• In case of Tomcat, instead of adding *-resources-x.y.z.jar to war file, copy it to any path of Tomcat server
and specify that path in the extended class path of VirtualWebappLoader.
Note: War file can be downloaded from Maven package repository with “get goal” of maven-dependency-plugin.
mvn org.apache.maven.plugins:maven-dependency-plugin:2.5:get \
-DgroupId=com.example \
-DartifactId=mywebapp \
-Dversion=0.0.1-SNAPSHOT \
-Dpackaging=war \
-Ddest=${WORKSPACE}/target/mywebapp.war
Package of environment dependency configuration files can be added to mywebapp.war file using the following
commands.
mkdir -p $WORKSPACE/target/WEB-INF/lib
cd $WORKSPACE/target
cp ./mywebapp-resources*.jar WEB-INF/lib
jar -ufv mywebapp.war WEB-INF/lib
4.1.1 Overview
It is mandatory to check whether the value entered by the user is correct. Validation of input value is broadly
classified into
1. Validation to determine whether the input value is valid by just looking at it irrespective of the context such
as size and format.
2. Validation of whether the changes in input value are valid depending on the system status.
1. is an example of mandatory check and number of digits check and 2. is an example of check of whether EMail
is registered and whether order count is within the count of the available stock.
In this section, 1. is explained and this check is called “Input validation”. 2. is called “Business logic check”. For
business logic check, refer to Domain Layer Implementation.
In this guideline, validation check should be performed in application layer whereas business logic check should
be performed in domain layer.
Input validation of Web application is performed at server side and client side (JavaScript). It is mandatory to
check at the Server Side. However, if the same check is performed at the client side also, usability improves since
validation results can be analyzed without communicating with the server.
Warning: Input validation should be performed at the server side as the process at client side may be altered
using JavaScript. If the validation is performed only at the client side without performing at the server side,
the system may be exposed to danger.
TERASOLUNA Server Framework for Java (5.x) Development Guideline Documentation,
Release 5.2.0.RELEASE
Todo
Input validation at client side will be explained later. Only input validation at the server side is mentioned in the
first version.
Input validation is classified into single item check and correlation item check.
Correlation item
check
Check comparing multiple fields Password and confirm Bean Validation or Validation
password check class implementing
org.springframework.validation.Validator
interface
Spring supports Bean Validation which is a Java standard. This Bean Validation is used for single item check.
Bean Validation or org.springframework.validation.Validator interface provided by Spring is
used for correlation item check.
When Bean validation 1.1 (Hibernate Validator 5.x) or higher version is to be used, in addition to jar file of
Hibernate Validator and jar file storing API specifications class (javax.validation package class) of Bean
Validation, libraries that store the following classes are required.
• API specifications class of Expression Language 2.2 or higher version (javax.el package class)
If the file is run by deploying on application server, dependent libraries need not be added; since these libraries
are provided by application server. However, when it is run in standalone environment (JUnit etc.), these libraries
need to be added as dependent libraries.
An example of adding libraries which are required when running Bean Validation 1.1 or higher version in stan-
dalone environment is given below.
(2)
Note: In the above example of settings, it is a prerequisite that version of dependent libraries should be stored in
the parent project. Therefore, <version> element is not specified.
Note: <mvc:annotation-driven> settings are carried out in spring-mvc.xml, Bean Validation is enabled.
Implementation method is explained using “New user registration” process as an example. Rules for checking
“New user registration” form are provided below.
• Form class
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
@NotNull // (1)
@Size(min = 1, max = 20) // (2)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email // (3)
private String email;
@NotNull // (4)
@Min(0) // (5)
@Max(200) // (6)
private Integer age;
// omitted setter/getter
}
In Spring MVC, when form is sent with input fields left blank,
empty string instead of null bindsto form object by default.
This @NotNull checks that name exists as request parameter.
Since empty string binds to the field where string is left blank by default in Spring MVC,
‘1 or more character’ rule indicates Mandatory input.
(4) When form is sent without entering any number in input field, null binds to form object so
@NotNull indicates mandatory input of age.
Tip: Refer to Bean Validation check rules and Hibernate Validator check rules for standard annotations of
Tip: Refer to Binding null to blank string field for the method of binding null when input field is left
blank.
• Controller class
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
return new UserForm();
}
(4) Use BindingResult.hasErrors() method to determine the check result of step (2).
When the result of hasErrors() is true, return to form display screen as there is an error
in input value.
(5) Input validation should be executed againeven at the time of submission on the confirmation
screen.
There is a possibility of data tempering and hence Input validation must be performed just
before entering business logic.
Note: @Validated is not a standard Bean Validation annotation. It is an independent annotation pro-
vided by Spring. Bean Validation standard javax.validation.Valid annotation can also be used.
However, @Validated is better as compared to @Valid annotation. Validation group can be specified
in case of @Validated and hence @Validated is recommended in this guideline.
• JSP
<!DOCTYPE html>
<html>
<%-- WEB-INF/views/user/createForm.jsp --%>
<body>
<form:form modelAttribute="userForm" method="post"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name">Name:</form:label>
<form:input path="name" />
<form:errors path="name" /><%--(1) --%>
<br>
<form:label path="email">Email:</form:label>
<form:input path="email" />
<form:errors path="email" />
<br>
<form:label path="age">Age:</form:label>
<form:input path="age" />
<form:errors path="age" />
<br>
<form:button name="confirm">Confirm</form:button>
</form:form>
</body>
</html>
Error message is displayed as follows if this form is sent with all the input fields left blank.
Error messages state that Name and Email are blank and Age is null.
Note: In Bean Validation, null is a valid input value except the following annotations.
• javax.validation.constraints.NotNull
• org.hibernate.validator.constraints.NotEmpty
• org.hibernate.validator.constraints.NotBlank
In the above example, error messages related to @Min and @Max annotations are not displayed. This is because
null is a valid value for @Min and @Max annotations.
Error message is not displayed since input value of Name fulfills validation conditions.
Error message is displayed since input value of Email is not in Email format though it fulfills the conditions
related to string length.
Error message is displayed since input value of Age exceeds maximum value.
Change the form as follows to change the style at the time of error.
<form:button name="confirm">Confirm</form:button>
</form:form>
(1) Specify class name for <label> tag in cssErrorClass attribute at the time of error.
(2) Specify class name for <input> tag in cssErrorClass attribute at the time of error.
For example, if the following CSS is applied to this JSP, error screen is displayed as follows.
.form-horizontal input {
display: block;
float: left;
}
.form-horizontal label {
display: block;
float: left;
text-align: right;
float: left;
}
.form-horizontal br {
clear: left;
}
.error-label {
color: #b94a48;
}
.error-input {
border-color: #b94a48;
margin-left: 5px;
}
.error-messages {
color: #b94a48;
display: block;
padding-left: 5px;
overflow-x: auto;
}
Instead of displaying the error messages next to each input field, output them collectively.
An example of error message is shown when the following CSS class is applied.
.form-horizontal input {
display: block;
float: left;
}
.form-horizontal label {
display: block;
float: left;
text-align: right;
float: left;
}
.form-horizontal br {
clear: left;
}
.error-label {
color: #b94a48;
}
.error-input {
border-color: #b94a48;
margin-left: 5px;
}
.error-message-list {
color: #b94a48;
padding:5px 10px;
background-color: #fde9f3;
border:1px solid #c98186;
border-radius:5px;
margin-bottom: 10px;
}
By default, field name is not included in error message, hence it is difficult to understand which error message
corresponds to which field.
Therefore, when an error message is to be displayed in a list, it is necessary to define the message such that field
name is included in the error message.
For method of defining error messages, refer to “Definition of error messages”.
Error messages are output in a random order and the output order cannot be controlled by standard function.
Therefore, when an output order needs to be controlled (to be kept constant), an extended implementation such as
are required. Therefore, the cost is higher as compared to “displaying error messages next to input field”. This
guideline recommends the method of “displaying error messages next to input field” when there are no
constraints due to screen requirements.
A mechanism of @GroupSequence annotation is provided to control the check sequence; however, add a note that
this mechanism is not to control the output order of error message as operations given below are performed.
• If multiple errors (errors in multiple fields) occur in the check of identical groups, then the output order of
error messages would be random.
Note: Use <spring:nestedPath> tag to display error messages collectively outside the <form:form>
tag.
<spring:nestedPath path="userForm">
<form:errors path="*" element="div"
cssClass="error-message-list" />
</spring:nestedPath>
<hr>
<form:form modelAttribute="userForm" method="post"
action="${pageContext.request.contextPath}/user/create">
<form:label path="name" cssErrorClass="error-label">Name:</form:label>
<form:input path="name" cssErrorClass="error-input" />
<br>
<form:label path="email" cssErrorClass="error-label">Email:</form:label>
<form:input path="email" cssErrorClass="error-input" />
<br>
<form:label path="age" cssErrorClass="error-label">Age:</form:label>
<form:input path="age" cssErrorClass="error-input" />
<br>
<form:button name="confirm">Confirm</form:button>
</form:form>
In case of performing the date and time format check, it is recommend the use of @DateTimeFormat
annotation offered by Spring rather than the use of Bean Validation mechanism.
About how to use the @DateTimeFormat annotation, refer Date and time format conversion of fields.
It is also possible to check the date and time format using @Pattern annotation of the Bean Validation.
However, it is necessary to write the date and time format in regular expressions while using @Pattern
annotation. If you want to check the date and time that does not exist, the description is complicated.
Therefore @DateTimeFormat annotation is more simpler than the @Pattern annotation.
Since @DateTimeFormat annotation is one of the type conversion mechanism provided by Spring, instead of
the error messages of Bean Validation, the error message of the type mismatch exception
(TypeMismatchException) has been displayed on screen at the time of input error.
In order to avoid the actual exception message gets displayed on the screen, it is necessary to configure the error
message in the property file which will be displayed at the time of type mismatch is occurred.
For more detail, refer Type mismatch.
The method to validate nested Bean using Bean Validation is explained below.
“Ordering” process of an EC site is considered as an example. Rules for checking “Order” form are provided
below.
Use the same form class since receiverAddress and senderAddress are objects of the same class.
• Form class
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@Size(max = 5)
@Pattern(regexp = "[a-zA-Z0-9]*")
private String coupon;
@NotNull // (1)
@Valid // (2)
private AddressForm receiverAddress;
@NotNull
@Valid
private AddressForm senderAddress;
// omitted setter/getter
}
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@NotNull
@Size(min = 1, max = 50)
private String name;
@NotNull
@Size(min = 1, max = 10)
private String postcode;
@NotNull
@Size(min = 1, max = 100)
// omitted setter/getter
}
• Controller class
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping("order")
@Controller
public class OrderController {
@ModelAttribute
public OrderForm setupForm() {
return new OrderForm();
}
}
}
• JSP
<!DOCTYPE html>
<html>
<%-- WEB-INF/views/order/orderForm.jsp --%>
<head>
<style type="text/css">
/* omitted (same as previous sample) */
</style>
</head>
<body>
<form:form modelAttribute="orderForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/order/order">
<form:label path="coupon" cssErrorClass="error-label">Coupon Code:</form:label>
<form:input path="coupon" cssErrorClass="error-input" />
<form:errors path="coupon" cssClass="error-messages" />
<br>
<fieldset>
<legend>Receiver</legend>
<%-- (1) --%>
<form:errors path="receiverAddress"
cssClass="error-messages" />
<%-- (2) --%>
<form:label path="receiverAddress.name"
cssErrorClass="error-label">Name:</form:label>
<form:input path="receiverAddress.name"
cssErrorClass="error-input" />
<form:errors path="receiverAddress.name"
cssClass="error-messages" />
<br>
<form:label path="receiverAddress.postcode"
cssErrorClass="error-label">Postcode:</form:label>
<form:input path="receiverAddress.postcode"
cssErrorClass="error-input" />
<form:errors path="receiverAddress.postcode"
cssClass="error-messages" />
<br>
<form:label path="receiverAddress.address"
cssErrorClass="error-label">Address:</form:label>
<form:input path="receiverAddress.address"
cssErrorClass="error-input" />
<form:errors path="receiverAddress.address"
cssClass="error-messages" />
</fieldset>
<br>
<fieldset>
<legend>Sender</legend>
<form:errors path="senderAddress"
cssClass="error-messages" />
<form:label path="senderAddress.name"
cssErrorClass="error-label">Name:</form:label>
<form:input path="senderAddress.name"
cssErrorClass="error-input" />
<form:errors path="senderAddress.name"
cssClass="error-messages" />
<br>
<form:label path="senderAddress.postcode"
cssErrorClass="error-label">Postcode:</form:label>
<form:input path="senderAddress.postcode"
cssErrorClass="error-input" />
<form:errors path="senderAddress.postcode"
cssClass="error-messages" />
<br>
<form:label path="senderAddress.address"
cssErrorClass="error-label">Address:</form:label>
<form:input path="senderAddress.address"
cssErrorClass="error-input" />
<form:errors path="senderAddress.address"
cssClass="error-messages" />
</fieldset>
<form:button name="confirm">Confirm</form:button>
</form:form>
</body>
</html>
(2) Fields of nested bean are specified as [parent field name].[child field name].
Error message is displayed as follows if this form is sent with all the input fields left blank.
Add a field such that up to 3 addresses can be registered in “user registration” form explained at the beginning.
package com.example.sample.app.validation;
import java.io.Serializable;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email
private String email;
@NotNull
@Min(0)
@Max(200)
private Integer age;
@NotNull
@Size(min = 1, max = 3) // (1)
@Valid
private List<AddressForm> addresses;
// omitted setter/getter
}
(1) It is possible to use @Size annotation for checking size of collection as well.
• JSP
<!DOCTYPE html>
<html>
<%-- WEB-INF/views/user/createForm.jsp --%>
<head>
<style type="text/css">
/* omitted (same as previous sample) */
</style>
</head>
<body>
<form:input path="addresses[${status.index}].name"
cssErrorClass="error-input" />
<form:errors path="addresses[${status.index}].name"
cssClass="error-messages" />
<br>
<form:label path="addresses[${status.index}].postcode"
cssErrorClass="error-label">Postcode:</form:label>
<form:input path="addresses[${status.index}].postcode"
cssErrorClass="error-input" />
<form:errors path="addresses[${status.index}].postcode"
cssClass="error-messages" />
<br>
<form:label path="addresses[${status.index}].address"
cssErrorClass="error-label">Address:</form:label>
<form:input path="addresses[${status.index}].address"
cssErrorClass="error-input" />
<form:errors path="addresses[${status.index}].address"
cssClass="error-messages" />
<c:if test="${status.index > 0}">
<br>
<button class="remove-address-button">Remove</button>
</c:if>
</fieldset>
<br>
</c:forEach>
<button id="add-address-button">Add address</button>
<br>
<form:button name="confirm">Confirm</form:button>
</form:form>
<script type="text/javascript"
src="${pageContext.request.contextPath}/resources/vendor/js/jquery-1.10.2.min.js"></s
<script type="text/javascript"
src="${pageContext.request.contextPath}/resources/app/js/AddressesView.js"></script>
</body>
</html>
(2) Process the collection of child forms in a loop using <c:forEach> tag.
(3) Inside the loop, Specify the field of child form using [parent field
name][Index].[child field name].
• Controller class
package com.example.sample.app.validation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
UserForm form = new UserForm();
List<AddressForm> addresses = new ArrayList<AddressForm>();
addresses.add(new AddressForm());
form.setAddresses(addresses); // (1)
return form;
}
(1) Edit the form object to display a single address form at the time of initial display of “user
registration” form.
• JavaScript
Below is the JavaScript to dynamically add address input field. However, the explanation of this code is
// webapp/resources/app/js/AddressesView.js
function AddressesView() {
this.addressSize = $('fieldset.address').size();
};
AddressesView.prototype.addAddress = function() {
var $address = $('fieldset.address');
var newHtml = addressTemplate(this.addressSize++);
$address.last().next().after($(newHtml));
};
AddressesView.prototype.removeAddress = function($fieldset) {
$fieldset.next().remove(); // remove <br>
$fieldset.remove(); // remove <fieldset>
};
function addressTemplate(number) {
return '\
<fieldset class="address">\
<legend>Address' + (number + 1) + '</legend>\
<label for="addresses' + number + '.name">Name:</label>\
<input id="addresses' + number + '.name" name="addresses[' + number + '].name" type="text
<label for="addresses' + number + '.postcode">Postcode:</label>\
<input id="addresses' + number + '.postcode" name="addresses[' + number + '].postcode" ty
<label for="addresses' + number + '.address">Address:</label>\
<input id="addresses' + number + '.address" name="addresses[' + number + '].address" type
<button class="remove-address-button">Remove</button>\
</fieldset>\
<br>\
';
}
$(function() {
var addressesView = new AddressesView();
$('#add-address-button').on('click', function(e) {
e.preventDefault();
addressesView.addAddress();
});
});
Error message is displayed as follows if this form is sent with all the input fields left blank.
Grouped validation
By creating validation group, input validation rules for a field can be specified for each group.
In the “new user registration” example, add “Must be an adult” rule for the age field. Add country field also
as “Adult” rules differ with country.
To specify group in Bean Validation, set any java.lang.Class object representing a group in group attribute
of the annotation.
• Form class
package com.example.sample.app.validation;
import java.io.Serializable;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
// (1)
public static interface Chinese {
};
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email
private String email;
@NotNull
@Min.List({ // (2)
@Min(value = 18, groups = Chinese.class), // (3)
@Min(value = 20, groups = Japanese.class),
@Min(value = 21, groups = Singaporean.class)
})
@Max(200)
private Integer age;
@NotNull
@Size(min = 2, max = 2)
private String country; // (4)
// omitted setter/getter
}
(2) @Min.List annotation is used to specify multiple @Min rules on a single field.
It is same even while using other annotations.
(3) Specify corresponding group class in the group attribute, in order to define rules for each
group.
When group attribute is not specified, javax.validation.groups.Default group is
used.
(4) Add a field which will be used to determine which group is to be applied.
• JSP
<form:button name="confirm">Confirm</form:button>
</form:form>
• Controller class
By giving a group name to @Validated annotation, the rules defined for that group will be applied.
package com.example.sample.app.validation;
import javax.validation.groups.Default;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.example.sample.app.validation.UserForm.Chinese;
import com.example.sample.app.validation.UserForm.Japanese;
import com.example.sample.app.validation.UserForm.Singaporean;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
UserForm form = new UserForm();
return form;
}
(1) The parameter which is used as condition to dividing between the groups must be set to
param attribute.
(2) All the annotations, except @Min of age field, belongs to Default group; hence,
specifying Default is mandatory.
In this example, the check result of the combination of each input value is as follows.
18 cn OK
20 cn OK
jp OK
21 cn OK
jp OK
sg OK
Warning: Implementation of this Controller is inadequate; there is no handling when country value is
neither “cn”, “jp” nor “sg”. 400 error is returned when unexpected country value is encountered.
Next, we can think of a condition where the number of countries increase and adult condition of 18 years or more
is be set as a default rule.
• Form class
In order to specify a value to Default group (18 years or more), all groups should be specified explicitly
in other annotations as well.
package com.example.sample.app.validation;
import java.io.Serializable;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.groups.Default;
import org.hibernate.validator.constraints.Email;
// omitted setter/getter
}
(1) Set all groups to annotations other than @Min of age field as well.
• JSP
No change in JSP
• Controller class
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.example.sample.app.validation.UserForm.Japanese;
import com.example.sample.app.validation.UserForm.Singaporean;
@Controller
@RequestMapping("user")
public class UserController {
@ModelAttribute
public UserForm setupForm() {
UserForm form = new UserForm();
return form;
}
(1) When the field country does not have a value, the request is mapped to a method in which
Default group is specified in @Validated annotation.
(2) When the field country has a value, the request is mapped to a method in which Default
group is not included in @Validated annotation.
In the previous pattern, Default group is used in Controller class and in the later one, Default group is used
in form class.
If none of the above decision points are applicable, then using Bean Validation itself might not be a good
idea.After reviewing the design, usage of Spring Validator or implementation of validation in business logic should
be considered.
Note: In the examples explained so far, the switching of group validation is carried out using request parameter
and parameter that can be specified in @RequestMapping annotation. It is not possible to switch between
groups, if switching is to be performed based on permissions in authentication object or any information which
cannot be handled by @RequestMapping annotation.
@Controller
@RequestMapping("user")
public class UserController {
@Inject
SmartValidator smartValidator; // (1)
// omitted
if (result.hasErrors()) {
return "user/createForm";
}
return "user/createConfirm";
}
Since logic should not be written in Controller, if switching is possible using request parameters in
@RequestMappingannotation, SmartValidator must not be used.
Each one of above has been explained below. However, before that, their features and usage have been explained.
Spring Validator It is easy to create input validation for a Input validation implementation of unique
particular class. business requirements depending on specific
It is inconvenient to use in Controller. form
Bean Validation Creation of input validation is not as easy as Common input validation implementation of
Spring Validator. development project not depending on
It is easy to use in Controller. specific form
Implementation method is explained with the help of “reset password” process as an example.
Implement the following rules. Following rules are provided in the “reset password” form.
Check rule “Must be same as confirmPassword” is validation of correlated items as password field and
passwordConfirm field should have the same value.
• Form class
other than validation of correlated items, implement using Bean Validation annotation.
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@NotNull
@Size(min = 8)
private String password;
// omitted setter/getter
}
Note: Password is normally saved in database after hashing it, hence there is no need to check the maxi-
mum number of characters.
• Validator class
package com.example.sample.app.validation;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Component // (1)
public class PasswordEqualsValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return PasswordResetForm.class.isAssignableFrom(clazz); // (2)
}
@Override
public void validate(Object target, Errors errors) {
if (errors.hasFieldErrors("password")) { // (3)
return;
}
if (!password.equals(confirmPassword)) { // (4)
errors.rejectValue(/* (5) */ "password",
/* (6) */ "PasswordEqualsValidator.passwordResetForm.password",
(2) Decide the argument is check target of this validator or not. Here PasswordResetForm
class is the target to be checked.
(3) If an error occurs at the target fields during a single item check, do not perform correlation
check in this Validator.
If it is necessary to perform the correlation check, this determination logic is not required.
(7) Set default message to be used when error message does not get resolved using code.
Note: Spring Validator implementation class should be placed in the same package as the Controller.
• Controller class
package com.example.sample.app.validation;
import javax.inject.Inject;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("password")
public class PasswordResetController {
@Inject
PasswordEqualsValidator passwordEqualsValidator; // (1)
@ModelAttribute
public PasswordResetForm setupForm() {
return new PasswordResetForm();
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(passwordEqualsValidator); // (2)
}
• JSP
<!DOCTYPE html>
<html>
<%-- WEB-INF/views/password/resetForm.jsp --%>
<head>
<style type="text/css">
/* omitted */
</style>
</head>
<body>
<form:form modelAttribute="passwordResetForm" method="post"
class="form-horizontal"
action="${pageContext.request.contextPath}/password/reset">
<form:label path="password" cssErrorClass="error-label">Password:</form:label>
<form:password path="password" cssErrorClass="error-input" />
<form:errors path="password" cssClass="error-messages" />
<br>
<form:label path="confirmPassword" cssErrorClass="error-label">Password (Confirm):</f
<form:password path="confirmPassword"
cssErrorClass="error-input" />
<form:errors path="confirmPassword" cssClass="error-messages" />
<br>
<form:button>Reset</form:button>
</form:form>
</body>
</html>
Error message as shown below is displayed when form is sent by entering different values in password field and
confirmPassword fields.
Note: When <form:password> tag is used, data gets cleared at the time of redisplay.
Note: Error information can be set for multiple fields for correlation check. However, displaying error messages
and applying style must always be performed in a set and only one part of the tasks cannot not be performed.
When you want to apply style to both the fields wherein correlation check error has occurred however you want to
display only one error message, it can be done by setting a null string in the error message. An example is given
below wherein style is applied to password field and confirmPassword field and error message is displayed
only in password field.
package com.example.sample.app.validation;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Component
public class PasswordEqualsValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return PasswordResetForm.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
// omitted
if (!password.equals(confirmPassword)) {
// register a field error for password
errors.rejectValue("password",
"PasswordEqualsValidator.passwordResetForm.password",
"password and confirm password must be same.");
(2) Specify code name of error message. Specify a null string in the corresponding error message
at that time.
For message definition, refer Messages to be defined in application-messages.properties.
(3) Set a default message to be used when error message could not be resolved in the code.
A null string is set in the example above.
Note: When multiple forms are used in a single controller, model name should be specified in
@InitBinder("xxx") in order to limit the target of Validator.
@Controller
@RequestMapping("xxx")
public class XxxController {
// omitted
@ModelAttribute("aaa")
public AaaForm() {
return new AaaForm();
}
@ModelAttribute("bbb")
public BbbForm() {
return new BbbForm();
}
@InitBinder("aaa")
public void initBinderForAaa(WebDataBinder binder) {
// add validators for AaaForm
binder.addValidators(aaaValidator);
}
@InitBinder("bbb")
public void initBinderForBbb(WebDataBinder binder) {
// add validators for BbbForm
binder.addValidators(bbbValidator);
}
// omitted
}
Note: To change the check contents of correlated items check rules in accordance with a
validation group (for example: To implement correlated items check only when specific valida-
tion group is specified, etc.), it is better to switch the process within validate method by imple-
menting org.springframework.validation.SmartValidator interface instead of implementing
org.springframework.validation.Validator interface.
package com.example.sample.app.validation;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
@Component
public class PasswordEqualsValidator implements SmartValidator { // Implements SmartValidator
@Override
public boolean supports(Class<?> clazz) {
return PasswordResetForm.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
validate(target, errors, new Object[] {});
}
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
// Check validationHints(groups) and apply validation logic only when 'Update.class'
if (ArrayUtils.contains(validationHints, Update.class)) {
PasswordResetForm form = (PasswordResetForm) target;
String password = form.getPassword();
String confirmPassword = form.getConfirmPassword();
// omitted...
}
}
}
Independent validation rules should be added to implement validation of correlated items using Bean Validation.
Error messages of Bean Validation in Spring MVC are resolved in the following order.
1. If there is any message which matches with the rule, among the messages defined in
org.springframework.context.MessageSource, then it is to be used as error message
(Spring rule).
For default rules of Spring, refer to “JavaDoc of DefaultMessageCodesResolver of
DefaultMessageCodesResolver”.
2. If message cannot be found as mentioned in step 1, then error message is acquired from the message
attribute of the annotation. (Bean Validation rule)
1. When the value of message attribute is not in “{message key}” format, use that text as error message.
2. When the value of message attribute is in “{message key}” format, search messages corresponding
to message key from ValidationMessages.properties under classpath.
2. When message corresponding to message key is not defined, use “{message key}” as error message
Considering that the following settings are done in applicationContext.xml, former is called as “application-
messages.properties” and latter is called “ValidationMessages.properties”.
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>i18n/application-messages</value>
</list>
</property>
</bean>
Warning: Multiple ValidationMessages.properties files should not exist directly under class
path.
If multiple ValidationMessages.properties files exist directly under class path, an appropriate mes-
sage may not be displayed, as either one file of them is read leaving rest of the files unread.
• When adopting multi project structure, please take care so that
ValidationMessages.properties file is not placed in multiple projects.
• When distributing common parts for Bean Validation as jar file, please take care so that
ValidationMessages.properties file is not included in jar file.
Further, when a project is created from Blank project of version 1.0.2.RELEASE or higher,
ValidationMessages.properties is stored directly under xxx-web/src/main/resources.
When ValidationMessages.properties is not provided, Default messages provided by Hibernate Validator is used.
Define messages for message key specified in message attribute of Bean Validation annotation of Validation-
Messages.properties under class path (normal src/main/resources).
It is explained below using the following form used at the beginning of Basic single item check.
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Size(min = 1, max = 50)
@Email
private String email;
@NotNull
@Min(0)
@Max(200)
private Integer age;
// omitted getter/setter
}
• ValidationMessages.properties
javax.validation.constraints.NotNull.message=is required.
# (1)
javax.validation.constraints.Size.message=size is not in the range {min} through {max}.
# (2)
javax.validation.constraints.Min.message=cannot be less than {value}.
javax.validation.constraints.Max.message=cannot be greater than {value}.
org.hibernate.validator.constraints.Email.message=is an invalid e-mail address.
(1) It is possible to embed the value of attributes specified in the annotation using {Attribute
name}.
When the form is sent with input fields left blank after adding the above settings, changed error messages are
displayed as shown below.
Add {0} to message as shown below when field name is to be included in error message.
• ValidationMessages.properties
javax.validation.constraints.NotNull.message="{0}" is required.
javax.validation.constraints.Size.message=The size of "{0}" is not in the range {min} thr
javax.validation.constraints.Min.message="{0}" cannot be less than {value}.
javax.validation.constraints.Max.message="{0}" cannot be greater than {value}.
org.hibernate.validator.constraints.Email.message="{0}" is an invalid e-mail address.
In this way, property name of form class gets displayed on the screen and so it is not user friendly. To display an
appropriate field name, it should be defined in application-messages.properties in the following format.
• application-messages.properties
name=Name
email=Email
age=Age
Note: Inserting field name in place of {0} is the functionality of Spring and not of Bean
Validation. Therefore, the settings for changing field name should be defined in application-
messages.properties(ResourceBundleMessageSource) which is directly under Spring management.
Tip: In Bean Validation 1.1, it is possible to use Expression Language (hereafter referred to as “EL expression”) in
a message specified in ValidationMessages.properties. Hibernate Validator 5.x supports Expression
Language 2.2 or higher version.
Executable EL expression version differs depending on the version of application server. Therefore when EL
expression is to be used, it should be used after confirming the version of EL expression supported by appli-
cation server.
# ...
# (1)
javax.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'o
# ...
Default messages to be used in system are defined in ValidationMessages.properties however, depending on the
screen, they may have to be changed from the default value.
Apply “Messages to be defined in ValidationMessages.properties” and override the message for email and age
field using the below settings.
• application-messages.properties
# override messages
# for email field
Size.userForm.email=The size of "{0}" must be between {2} and {1}.
# for age field
NotNull.userForm.age="{0}" is compulsory.
Min.userForm.age="{0}" must be greater than or equal to {1}.
Max.userForm.age="{0}" must be less than or equal to {1}.
# filed names
name=Name
email=Email
age=Age
Value of attributes of the annotation gets inserted after {1} onwards. Incidentally, index position of attribute
values are alphabetical ordering(ascending order) of attribute names.
Note: There are other formats as well for the message key format of application-messages.properties; however, if
it is used with the purpose of overwriting some default messages, it should be in [annotation name].[form
attribute name].[property name] format.
Other than standard check rules, bean validation has a mechanism to develop annotations for independent rules .
The method of creating independent rules can be widely classified into the following two broader criteria.
Basically, the below template can be used to create annotation for each rule.
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Xxx {
String message() default "{com.example.common.validation.Xxx.message}";
Xxx[] value();
}
}
Consider the following restrictions at the system level and domain level respectively.
These can be implemented by combining @Pattern, @Size, @Min, @Max of the existing rules.
However, if the same rules are to be used at multiple places, settings get distributed and maintainability worsens.
One rule can be created by combining multiple rules. There is an advantage to be able to have not only common
regular expression pattern and maximum/minimum values but also error message when an independent annotation
is created. By this, reusability and maintainability increases. Even if multiple rules are not combined, it also proves
beneficial if used only to give specific value to an attribute.
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Pattern;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation // (1)
@Pattern(regexp = "[a-zA-Z0-9]*") // (2)
public @interface AlphaNumeric {
String message() default "{com.example.common.validation.AlphaNumeric.message}"; // (3)
(1) This will consolidate error messages and return only the message of this annotation at the time
of error.
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Min;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@Min(value = 0)
public @interface NotNegative {
String message() default "{com.example.common.validation.NotNegative.message}";
• Implementation example of @UserId annotation which regulates the format of “User ID”.
package com.example.sample.domain.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@Size(min = 4, max = 20)
@Pattern(regexp = "[a-z]*")
public @interface UserId {
String message() default "{com.example.sample.domain.validation.UserId.message}";
package com.example.sample.domain.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@Min(1)
@Max(150)
public @interface Age {
String message() default "{com.example.sample.domain.validation.Age.message}";
Note: If multiple rules are set in a single annotation, their AND condition forms the composite anno-
tation. In Hibernate Validator, @ConstraintComposition annotation is provided to implement OR
condition. Refer to Hibernate Validator document for details.
For the rules that cannot be implemented by combining @Pattern, @Size, @Min, @Max, implement
javax.validation.ConstraintValidator.
For example, rules that check ISBN (International Standard Book Number)-13 format are given.
• Annotation
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { ISBN13Validator.class }) // (1)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface ISBN13 {
String message() default "{com.example.common.validation.ISBN13.message}";
(1) Specify the ConstraintValidator implementation class which will get executed when
this annotation is used. Multiple constraints can also be specified.
• Validator
package com.example.common.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@Override
public void initialize(ISBN13 constraintAnnotation) { // (2)
}
@Override
Tip: Example of Bean Validation of file upload is classified in this category. Further, in common library as well,
@ExistInCodeList is implemented in this way.
Check of correlated items, which span over multiple fields, can be done using Bean Validation as explained in
Correlation item check.
It is recommended to target generalized rules, in case of deciding to use Bean Validation for check of correlated
items.
An example of implementing the rule, “Contents of a field should match with its confirmation field” is given
below.
• Annotation
Define such that annotation for validation of correlated items can be used at class level as well.
package com.example.common.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { ConfirmValidator.class })
@Target({ TYPE, ANNOTATION_TYPE }) // (1)
@Retention(RUNTIME)
public @interface Confirm {
String message() default "{com.example.common.validation.Confirm.message}";
/**
* Field name
*/
String field(); // (2)
(1) Narrow down the target of using this annotation, such that it can be added only to class or
annotation.
• Validator
package com.example.common.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
if (matched) {
return true;
} else {
context.disableDefaultConstraintViolation(); // (3)
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(field).addConstraintViolation(); // (4)
return false;
}
}
(2) Acquire property value from the form object through BeanWrapper.
For deprecated API of Bean Validation, refer to Bean Validation API Document (Deprecated API).
Note: As introduced in correlation item check using Spring Validator, even in Bean Validation, Set error infor-
An example is shown below wherein styles are applied to passwordfield and confirmPasswordfield in Bean
Validation and error message is displayed only for passwordfield.
// omitted
public class ConfirmValidator implements ConstraintValidator<Confirm, Object> {
private String field;
return false;
}
}
(1) Register error of confirmPasswordfield. A null string is set in error message at that time.
Check below for the changes, if the “Reset password” is re-implemented using @Confirm annotation.
• Form class
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.example.common.validation.Confirm;
@NotNull
@Size(min = 8)
private String password;
// omitted getter/setter
}
• Controller class
package com.example.sample.app.validation;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("password")
public class PasswordResetController {
@ModelAttribute
public PasswordResetForm setupForm() {
return new PasswordResetForm();
}
Business logic check should implement Implementation in Service of domain layer and store result message in
ResultMessages object.
Accordingly, it is expected that, they will be normally displayed at the top of the screen.
However, there are cases, where business logic error message (such as, “whether the entered user name is already
registered”) of target input field is to be displayed next to the field. In such a case, service class is injected in
Validator class and business logic check is executed in ConstraintValidator.isValid.
An example of implementing, “whether the entered user name is already registered” in Bean Validation is shown
below.
• Service class
package com.example.sample.domain.service.user;
• Annotation
package com.example.sample.domain.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { UnusedUserIdValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface UnusedUserId {
String message() default "{com.example.sample.domain.validation.UnusedUserId.message}";
• Validator class
package com.example.sample.domain.validation;
import javax.inject.Inject;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.stereotype.Component;
import com.example.sample.domain.service.user.UserService;
@Component // (1)
public class UnusedUserIdValidator implements
ConstraintValidator<UnusedUserId, String> {
@Inject // (2)
UserService userService;
@Override
public void initialize(UnusedUserId constraintAnnotation) {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
(3) Return the result of business logic error check. Process should be delegated to service class.
Logic must not be written directly in the isValid method.
Method Validation
A method to check validity of actual argument and return value of method using Bean Validation, is described. In
order to explain, the method is called Method Validation in this chapter. While performing defensive programming
etc, method I/O should be checked in the class other than Controller. If Bean Validation library is used at that time,
constraint annotation of Bean Validation used in Controller can be reused.
Application settings
When Method Validation offered by Spring Framework is used, a bean must be defined for
org.springframework.validation.beanvalidation.MethodValidationPostProcessor
class offered by Spring Framework.
Bean definition file which defines MethodValidationPostProcessor differs depending on where you use
the Method Validation.
Here, a setup example is given wherein Method Validation is used in the multi-project environment recommended
in this guideline.
• projectName-domain/src/main/resources/META-INF/spring/projectName-domain.xml
• projectName-web/src/main/resources/META-INF/spring/spring-mvc.xml
(1)
By using the wrapper Validator generated by this class, message management function (MessageSource)
offered by Spring Framework and DI container can be linked.
Tip: In Spring Framework, Method Validation for calling the method of Bean which is managed by DI container
is executed by using AOP system.
Note: In the example above, an identical Validator object (instance) is set for validator property of each
Bean, but this is not necessarily required. However, it is recommended to set an identical object (instance) unless
there is a reason to do otherwise.
When Method Validation is applied to the method, annotation which indicates inclusion of target method and the
constraint annotation of Bean Validation should be specified in class level and, method and dummy argument
respectively.
AOP which executes Method Validation is not applicable for “Application settings”. It is necessary to assign
@ org.springframework.validation.annotation.Validated annotation to interface or class in
order to apply AOP which executes Method Validation.
package com.example.domain.service;
import org.springframework.validation.annotation.Validated;
@Validated // (1)
public interface HelloService {
// ...
}
Tip: By specifying a group interface in value attribute of @Validated annotation, a validation belonging to
a specified group can alone be executed as well.
Further, the validation groups can be changed for each method by assigning Validated annotation in method
level.
Next, a method is described wherein constraint annotation of Bean Validation is specified in the method and
dummy argument. Basically,
• Method arguments
A basic specification method is described below. A method to specify annotation in the interface is introduced in
the description hereafter.
First, a method that specifies constraint annotation is described for the method using basic types (primitive type or
primitive wrapper type etc) as a signature of the method
package com.example.domain.service;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
@Validated
public interface HelloService {
// (2)
@NotNull
String hello(@NotNull /* (1) */ String message);
Next, a method that specifies constraint annotation of Bean Validation is described for the method using JavaBean
as a signature of method.
Note: The main point is to specify @javax.validation.Valid annotation. The specification method is
Service interface
package com.example.domain.service;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
@Validated
public interface HelloService {
@NotNull // (3)
@Valid // (4)
HelloOutput hello(@NotNull /* (1) */ @Valid /* (2) */ HelloInput input);
package com.example.domain.service;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import java.util.Date;
@NotNull
@Past
private Date visitDate;
@NotNull
private String visitMessage;
// ...
package com.example.domain.service;
import com.example.domain.model.User;
import java.util.Date;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
@NotNull
@Past
private Date acceptDate;
@NotNull
private String acceptMessage;
@Valid // (5)
private User user;
// ...
package com.example.domain.model;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import java.util.Date;
@NotNull
private String userId;
@NotNull
private String userName;
@Past
private Date dateOfBirth;
// ...
When ConstraintViolationException is thrown, the method generated from stack trace can be identi-
fied, however, basic violation details cannot be identified.
package com.example.app;
import javax.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ConstraintViolationExceptionHandler {
// (1)
@ExceptionHandler
public String handleConstraintViolationException(ConstraintViolationException e){
// (2)
if (log.isErrorEnabled()) {
log.error("ConstraintViolations[\n{}\n]", e.getConstraintViolations());
}
return "common/error/systemError";
}
4.1.4 Appendix
Hibernate Validator provides additional validation annotations, in addition to the annotations defined in Bean
Validation.
Refer to Herefor the annotation list that can be used for validation.
@Null Arbitrary
@Null(groups={Update.cla
Validates that the target field is private String id;
null.
(Example: usage in group
validation)
@Pattern String
@Pattern(regexp = "[0-9]
Whether the target field matches private String tel;
with the regular expression
(In case of Hibernate Validator
implementation, it is also
possible to use it with arbitrary
implementation class of
CharSequence interface)
Tip: inclusive attribute of @DecimalMin and @DecimalMax annotation is, an attribute added from Bean
Validation 1.1.
By specifying true (to allow same value of specified threshold) to the default value of inclusive attribute,
compatibility with Bean Validation 1.0 is maintained.
Warning: In @Size annotation, characters represented by char type 2 (32 bits) called as surrogate pair are
not considered.
When a string consisiting of a surrogate pair is excluded from the check, adequate care must be taken since
number of characters that are counted are likely to be more than the actual number of characters.
For length of the string including surrogate pair, refer Fetching string length.
Warning: When following annotations provided by Hibernate Validator are used, if a default message is used,
a bug that the message is not generated correctly HV-881, HV-949) occurs.
• @CreditCardNumber(message is displayed, but WARN log is output)
• @LuhnCheck
• @Mod10Check
• @Mod11Check
• @ModCheck(deprecated API from 5.1.0.Final)
This bug occurs because of the flaws in message definitions provided by default, and it is possible to avoid
them by overwriting the default messages by an appropriate message.
In case of overwriting the default messages, it is advisable to define an appropriate message by creating
ValidationMessages.properties directly under the class path (normal src/main/resources).
For appropriate message definition, refer to: Modifications for Hibernate Validator 5.2 version (next minor
version upgrade).
A common library provides an independent annotation for verification. Here, how to specify input check rules
which use annotation provided by common library is explained.
@ByteMax
@ByteMin(1)
Implementation class of Verify whether byte length value @ByteMax(value = 100,
CharSequence is less than or equal to maximum charset = "Shift
(String, StringBuilder value. private String id;
etc)
[Annotation attribute]
long value - Specify
maximum value for byte length.
String charset - Specify
string character set used while
encoding the value in byte
sequence. Default value is
UTF-8.
For unit item check, whether a value is entered in the input field( should not be null) can be checked by
using @NotNull in combination. However, in correlated item check, the check like “if value is entered in one
field, value is entered forcefully in another field” cannot be implemented by using @NotNull alone. Hence,
@Compare provides requireBoth attribute which controls mandatory input for checking which can then be
used for implementing the check whenever required.
Also, when a value is not entered in the input field, requireBoth attribute can be used only when null is
bound. If a form is sent in Spring MVC when a value is not entered in input field of string, it must be noted that
empty string is bound in the form object by default instead of null. When a value is not entered in the string
field, refer Binding null to blank string field to bind null in form object instead of empty string.
Expected check requirements and configuration example are shown below using “checking whether period start
date is earlier than end date” as an example.
Only from is mandatory, however carry out compar- Assign @NotNullonly in from and use default
ison check when a value is entered in to as well. value ( false)in requireBoth attribute.
@Compare(left = "from", right = "to", operato
public class Period {
@NotNull
LocalDate from;
LocalDate to;
}
from and to are not both required, carry out com- Do not assign @NotNulland use default value
parison check only when values are entered in both ( false)in requireBoth attribute.
from and to. When the value is entered in only one @Compare(left = "from", right = "to", operato
of the fields, comparison check is not carried out. public class Period {
LocalDate from;
LocalDate to;
}
from and to are not both required, when values are Do not assign @NotNulland specify true in
entered in either of the fromor to, carry out compar- requireBoth attribute.
ison check by always entering values in both fields. @Compare(left = "from", right = "to", operato
public class Period {
LocalDate from;
LocalDate to;
}
Add a dependent library for the rules that are to be used. An example for how to add
terasoluna-gfw-validator is shown below.
<dependencies>
<dependency>
<groupId>org.terasoluna.gfw</groupId>
<artifactId>terasoluna-gfw-validator</artifactId>
</dependency>
</dependencies>
# (1)
org.terasoluna.gfw.common.validator.constraints.ByteMin.message = must be greater than or equal to
org.terasoluna.gfw.common.validator.constraints.ByteMax.message = must be less than or equal to {v
org.terasoluna.gfw.common.validator.constraints.Compare.message = not match '{left}' and '{right}'
In the end, assign an annotation to JavaBean property as explained in Basic single item check.
Note: When validation cannot be implemented in Bean Validation due to invalid attribute value of annotation,
javax.validation.ValidationException is thrown. Modify the attribute value to a valid value by
referring the reason that is output in stack trace.
Any rule can be created by using check rules provided by common library.
An example is introduced below wherein @Confirm annotation independently implemented by Check rules for
correlated items is created by using check rules provided by common library.
Create @Confirm annotation by using @Compare as described in Creation of Bean Validation annotation by
combining existing rules.
package com.example.sample.domain.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.OverridesAttribute;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import org.terasoluna.gfw.common.validator.constraints.Compare;
@Documented
@Constraint(validatedBy = {})
@Target({ TYPE, ANNOTATION_TYPE }) // (1)
@Retention(RUNTIME)
@ReportAsSingleViolation // (2)
@Compare(left = "", right = "", operator = Compare.Operator.EQUAL, requireBoth = true) // (3)
public @interface Confirm {
@Documented
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface List {
Confirm[] value();
}
}
(1) Restrict the location where the annotation can be assigned to class or annotation.
(2) Message specified in message attribute of this annotation should be used at the time of error.
(5) Override left attribute of @Compare annotation and change attribute name to field.
(6) Similarly, override right attribute and change attribute name to confirmField.
Use annotation created above instead of annotation implemented in Check rules for correlated items.
package com.example.sample.app.validation;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.example.common.validation.Confirm;
@NotNull // (2)
@Size(min = 8)
private String password;
@NotNull // (3)
private String confirmPassword;