You are on page 1of 115

© coaxial.

ro 2018 - Building RESTful APIs with Spring Boot

Course Objectives
The main objective of this course is to go, in details, through the process of implementing a
realistic RESTful API project using Java, Spring Boot and relational databases.
This course explains the most important development and operational concerns that need to be
addressed when creating a real life application. At the end of the course you should be able to
create your own production grade RESTful API application which is ready for deployment.

Project Objectives
The basis of this course is a completed Eclipse based Java project that contains all
development assets required to create a production ready RESTful API application.
The project is delivered with this course as an archive: ​coursea1.zip

Project Output
The main outcome of this project is a RESTful API that allows for the management of a rental
process for physical assets.

The code is available as an archive with this course and it is heavily commented. We
recommended that you create two Eclipse projects: one with the provided project and one that
you build step by step using the instructions in this course.

Business Requirements
We need to implement three major features:

1. Have an inventory of assets


We need to know at any moment how many assets are available in stock, how
many are rented and how many are marked as unusable (e.g. due to excess
wear and tear).

2. Calculate the price for rentals


The price of rentals is based on the type of asset rented and how many days the
asset is rented for. The customers say when renting for how many days they
want to rent for and pay up front. If the asset is returned late, then rent for the
extra days is charged when returning.

The store charges two type of prices:


© coaxial.ro 2018​ 1
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

1. Premium​ price - 10USD


2. Basic​ price - 5USD

The store has three types of assets.


1. New ​– Price is ​premium price​ times number of days rented.
2. Regular​ – Price is ​basic price​ for the first 3 days and then ​basic price
times the number of days over 3.
3. Old​ - Price is ​basic price​ for the first 5 days and then ​basic price​ times the
number of days over 5

3. Keep track of the customer’s “bonus” points

The RESTful API should expose operations for:

● Renting one or several assets and calculating the price.


● Returning assets and calculating possible surcharges.
● Provide stock summary

Development Environment Setup

Software Versions
These are the versions we are going to use for the major pieces of software:

● Java - 9
● Spring Boot - 2.0.2
● Eclipse Java EE IDE - Oxygen
● MySQL - 8.0.11

Download and Install Java 9


Navigate in your browser to this link: ​http://jdk.java.net/archive/​ and download the latest Java 9
available for your OS.

You can also download directly from this links:


MacOS: ​https://download.java.net/java/GA/jdk9/9.0.4/binaries/openjdk-9.0.4_osx-x64_bin.tar.gz
Linux: ​https://download.java.net/java/GA/jdk9/9.0.4/binaries/openjdk-9.0.4_linux-x64_bin.tar.gz

© coaxial.ro 2018​ 2
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Windows:
https://download.java.net/java/GA/jdk9/9.0.4/binaries/openjdk-9.0.4_windows-x64_bin.tar.gz

MacOS
Decompress the archive into /Library/Java/JavaVirtualMachines

sudo su
cd /Library/Java/JavaVirtualMachines
mv ~/Downloads/openjdk-9.0.4_osx-x64_bin.tar.gz .
tar -zxvf openjdk-9.0.4_osx-x64_bin.tar.gz

Download and Install Eclipse IDE for Java EE


Navigate to ​https://www.eclipse.org/downloads/eclipse-packages/​ and click on the ​64 bit​ link.

You’ll be redirected to a mirror - there click on the Download button.

© coaxial.ro 2018​ 3
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Setup Eclipse for Java 9


This section covers the steps required to setup Eclipse IDE for development with Java 9. There
are two main steps: configuring the JRE for the Eclipse workspace and configuring the compiler
options for our project.

Configure JRE for Workspace


Open Eclipse > Preferences
Click on Installed JREs and then Add

© coaxial.ro 2018​ 4
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Select Standard VM and then click ​Next​.


In the ​Add JRE​ dialog box that opens provide these values:

JRE home: /Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home


JRE name: Java 9

Click ​Finish

© coaxial.ro 2018​ 5
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Your ​Preferences​ dialog should look like this now:

© coaxial.ro 2018​ 6
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Click on ​Apply​.

Change the Compiler compliance level to 9. In the ​Preferences​ dialog box click on ​Compiler​.

© coaxial.ro 2018​ 7
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Select ​Compiler compliance level​ 9 then click on ​Apply and Close​.

Configure Compiler Options for Project


Now set the Compiler compliance level to 9 for our project as well. Right click on the project
name in Eclipse and select ​Properties​.
In the ​Properties for Rentals​ dialog box check ​Enable project specific settings​ and select
Compiler compliance level​ 9 then click on ​Apply and Close​.

© coaxial.ro 2018​ 8
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Import Course Project


We recommend you import the provided and complete course project so that you can use it as
reference when completing your own version using the instructions in this course.

Unzip the ​coursea1.zip ​on your workstation. In Eclipse navigate to File > Import…, select
Existing Maven Projects​ and click ​Next​.

© coaxial.ro 2018​ 9
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

In the next dialog box (​Import Maven Projects​) click on ​Browse​, select the ​rentals​ folder
inside ​coursea1​ then click on ​Finish​. The Eclipse project name is ​rentals​.

Generate Your Own Project Skeleton


To generate your own project go to ​https://start.spring.io/​ and provide these details.

Project Metadata
Group: course.a1
Artifact: rentals2

Dependencies

© coaxial.ro 2018​ 10
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Add Security, Web, JPA, Jersey, REST Docs, H2, JDBC.

Download the generated project, unzip it in ​~/courses/a1​. In Eclipse, go to ​File​ > ​Import…
and select ​“Existing Maven Projects”​ to import the newly generated project
(​~/courses/a1/rentals2​).

Test the generated project by running these commands

cd ~/courses/a1/rentals2
mvnw compile

When run for the first time the ​mvnw​ command will download Maven and all project’s
dependencies so it will take a couple of minutes.

Note​ If this command fails with an error like this: ​“ZipFile invalid LOC header (bad signature)”,​
clear Maven’s cache. This should only happen if you already used Maven for other projects
and already had a Maven cache in place.

cd ~
rm -rf .m2

Then try compiling the project again

mvnw compile

© coaxial.ro 2018​ 11
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Setup Eclipse with Maven

Sometimes it is convenient to use Maven commands from within Eclipse’s UI. Eclipse comes
with a built-in Maven installation but to ensure we use the same version of Maven in and outside
Eclipse we configure Eclipse to use the version of Maven that is used by the Spring Maven
plugin.

Configure Eclipse to use the external maven installation


Go to ​Windows​ > ​Preferences​ > ​Maven​ > ​Installation​, click ​Add​ and provide the path to the
installation.

Select the newly added Maven installation and click ​Apply


© coaxial.ro 2018​ 12
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Debugging

To start a debugging session run the application using the command below.

./mvnw spring-boot:run -Pdev -Dspring-boot.run.jvmArguments="-Xdebug


-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"

That will start the application JVM in debug mode and the JVM will suspend until a debugger
attaches to the specified port. This are the last lines you see when starting the application in
debug mode:

The JMV now waits for the remote debugger. In Eclipse click on ​Run​ > ​Debug
Configurations…​ In the Debug Configurations dialog box right click on ​Remote Java
Application​ then click on ​New​. Fill in the right hand form panel with these values:

© coaxial.ro 2018​ 13
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Name​: RentalsMvn
Project​: rentals
Host​: localhost
Port​: 5005

Click on ​Apply​ then ​Close​ when done.

To attach to the running application click again on ​Run​ > ​Debug Configurations...​, select the
newly added ​Remote Java Application​ entry and click on ​Debug​.

© coaxial.ro 2018​ 14
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Testing During Development

We can test our application during development using ​Postman​ - a tool that makes it easy to
build HTTP requests using a GUI.
In this section we explain how to import in Postman the collection of requests which is provided
in this course and how to build your own requests.

Importing the Provided Postman Request Collection


The course package provide a Postman collection (​CourseA1.postman_collection.json​) file
that is ready to be imported in your Postman application and be used.
In Postman click on the ​Import​ button then in the dialog box that opens select file
CourseA1.postman_collection.json​ from the root of your course package.

You should now have a ​CourseA1​ collection in Postman that contains ready to be used
requests that can be used to test the application during development.

Because our application is secured we need to obtain an access token before we can issue
requests against it.

© coaxial.ro 2018​ 15
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

An access token is obtained by accessing the ​/oauth/token​ URL. In the ​CourseA1​ Postman
collection this is done via the ​OAuth-Token​ request.

Open the OAuth-Token request and provide the ​username​ for the test you want to perform.

When done click on ​Send​.


The response is a JSON file containing the access token (​access_token​).

To issue a request to any of our Rentals REST API you need to provide the access token via
the HTTP Authorization header.

© coaxial.ro 2018​ 16
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Open any request in the provided collection, click on the ​Authorization​ tab and provide the
access token in the ​Token​ field.

Build Your Own Request In Postman


If you are not familiar with Postman, it is useful to learn how to create requests that can be used
with our secured application.

As examples we are going to build the request to get the access token from the application
(OAuth-Token) and a request to search for users (Users Search)

Building OAuth-Token Request

In Postman create a new request. Click on the ​New​ button, provide a ​Request name
(OAuth-Token), select ​CourseA1​ (or any other folder if you have not yet imported the provided
collection) and then click on ​Save to CourseA1​.

© coaxial.ro 2018​ 17
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Click on the newly created request. Set the HTTP method to POST, provide the URL
http://localhost:8080/oauth/token​ ​then click on the ​Authorization​ tab and provide the client id
for ​Username​ (rentals-client-id) and the client secret for ​Password ​(ddkl44#SDsfg)​ ​(see
screenshot below).

© coaxial.ro 2018​ 18
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Next click on the ​Body​ tab, click on ​x-www-form-urlencoded​ and provide the following values:

grant_type​ - password
username​ - ​admin@rentals.io​ (see the S
​ ecurity​ section for the list of users created for testing
during development)
password​ - pass
scope​ - read write

© coaxial.ro 2018​ 19
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Building User Search Request


Create a new request with name ​User Search​ in Postman as shown in the previous section.
Set the HTTP method to POST, provide the URL ​http://localhost:8080/api/rentals/users/search
and in the ​Authorization​ tab provide the ​Token​ that you obtained using the ​OAuth-Token
request.
In the Body tag select ​raw​ and ​JSON (application/json) ​from the drop down list.

© coaxial.ro 2018​ 20
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Maven Build File


Maven uses a project ​pom.xml​ file to describe dependencies and how to build your project.
Let’s have a look at our project’s ​pom.xml​ file. The file is split into sections and comments are
provided for each one. It is worth spending some time analysing each entry in the ​pom.xml​ file
to gain insight into how the project is built and what its dependencies are.

<?​
xml​​
version​
="1.0" ​
encoding​
="UTF-8"?> 
<​
project​​
xmlns​
="http://maven.apache.org/POM/4.0.0" 

xmlns:xsi​
="http://www.w3.org/2001/XMLSchema-instance" 

xsi:schemaLocation​
="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
<​
modelVersion​
>4.0.0</​
modelVersion​

 

<!-- Project information --> 
<​
name​
>rentals</​
name​

<​
description​
>Course A1 project for Spring Boot - Rentals RESTful 
API</​
description​

 

<!-- Project ID: <groupId>:<artifactId>:<version> --> 
<​
groupId​
>course.a1</​
groupId​

<​
artifactId​
>rentals</​
artifactId​

<​
version​
>0.0.1-SNAPSHOT</​
version​

 

© coaxial.ro 2018​ 21
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


<!-- We package our application as a standalone jar file --> 
<​
packaging​
>jar</​
packaging​

 

<!-- We want to inherit the Maven configuration from  
spring-boot-starter-parent as it will provide default configuration for  
Maven plugins and dependency versions --> 
<​
parent​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-starter-parent</​
artifactId​

<​
version​
>2.0.2.RELEASE</​
version​

<​
relativePath​/
​>
​ 
</​
parent​

 

<!-- Project properties --> 
<​
properties​

<​
project.build.sourceEncoding​
>UTF-8</​
project.build.sourceEncoding​
>   
<​
project.reporting.outputEncoding​
>UTF-8 
</​
project.reporting.outputEncoding​

<
​!-- Source and target java set to Java 9 --> 
<​
java.version​
>9</​
java.version​

<
​!-- We need to explicitly set the version of this plugin as earlier  
releases have a bug when using Java 9 and 10: 
https://issues.apache.org/jira/browse/SUREFIRE-1439. 
This plugin is used for running tests with Maven. -->   
<​
maven-surefire-plugin.version​
>2.21.0</​
maven-surefire-plugin.version​

</​
properties​

 

<!-- We are using Maven profiles to build different versions of our  
application depending on the environment in which it runs (development,   
production or test) --> 
<​
profiles​

<
​!-- Development profile --> 
<​
profile​

<​
id​
>dev</​
id​

<​
properties​

<​
activatedProperties​
>dev</​
activatedProperties​

</​
properties​

<
​!-- We use the H2 database for development --> 

© coaxial.ro 2018​ 22
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

<​
dependencies​

<​
dependency​

<​
groupId​
>com.h2database</​
groupId​

<​
artifactId​
>h2</​
artifactId​

<​
scope​
>runtime</​
scope​

</​
dependency​

 
<
​!-- This will restart the application when files are changed  
in your IDE --> 
<
​!-- It will also make the H2 console available in developer  
mode at this location: http://localhost:8080/h2-console --> 
<​
dependency​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-devtools</​
artifactId​

<​
optional​
>true</​
optional​

</​
dependency​

</​
dependencies​

</​
profile​

<
​!-- Production profile --> 
<​
profile​

<​
id​
>prod</​
id​

<​
properties​

<​
activatedProperties​
>prod</​
activatedProperties​

</​
properties​

<​
dependencies​

<
​!-- We use MySQL for production --> 
<​
dependency​

<​
groupId​
>mysql</​
groupId​

<​
artifactId​
>mysql-connector-java</​
artifactId​

<​
scope​
>runtime</​
scope​

</​
dependency​

</​
dependencies​

</​
profile​

<
​!-- Test profile --> 
<​
profile​

<​
id​
>test</​
id​

<​
properties​

<​
activatedProperties​
>test</​
activatedProperties​

© coaxial.ro 2018​ 23
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

</​
properties​

 
<​
dependencies​

<
​!-- We use the H2 database for testing --> 
<​
dependency​

<​
groupId​
>com.h2database</​
groupId​

<​
artifactId​
>h2</​
artifactId​

<​
scope​
>test</​
scope​

</​
dependency​

 
</​
dependencies​

</​
profile​

<
​!-- Integration test profile --> 
<​
profile​

<​
id​
>itest</​
id​

<​
properties​

<​
activatedProperties​
>itest</​
activatedProperties​

</​
properties​

 
<​
dependencies​

<
​!-- We use the H2 database for testing --> 
<​
dependency​

<​
groupId​
>com.h2database</​
groupId​

<​
artifactId​
>h2</​
artifactId​

<​
scope​
>test</​
scope​

</​
dependency​

 
<
​!-- required by TestRestTemplate --> 
<​
dependency​

<​
groupId​
>org.apache.httpcomponents</​
groupId​

<​
artifactId​
>httpclient</​
artifactId​

<​
scope​
>test</​
scope​

</​
dependency​

</​
dependencies​

</​
profile​

</​
profiles​

 
<​
dependencies​

© coaxial.ro 2018​ 24
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

<
​!-- We use Spring Data JPA repositories --> 
<​
dependency​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-starter-data-jpa</​
artifactId​

</​
dependency​

 
<
​!-- Required for building RESTful applications --> 
<​
dependency​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-starter-web</​
artifactId​

</​
dependency​

 
<
​!-- Required for metrics and health check --> 
<​
dependency​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-starter-actuator</​
artifactId​

</​
dependency​

 
<
​!-- Required for exposing a prometheus endpoint for monitoring --> 
<​
dependency​

<​
groupId​
>io.micrometer</​
groupId​

<​
artifactId​
>micrometer-registry-prometheus</​
artifactId​

</​
dependency​

 
<
​!-- Required to provide REST API documentation --> 
<​
dependency​

<​
groupId​
>io.springfox</​
groupId​

<​
artifactId​
>springfox-swagger2</​
artifactId​

<​
version​
>2.8.0</​
version​

</​
dependency​

 
<
​!-- We want to have Swagger UI installed to visualize our REST API  
--> 
<​
dependency​

<​
groupId​
>io.springfox</​
groupId​

<​
artifactId​
>springfox-swagger-ui</​
artifactId​

<​
version​
>2.8.0</​
version​

</​
dependency​

© coaxial.ro 2018​ 25
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

 
<
​!-- Needed to run tests with Maven before building the application  
--> 
<​
dependency​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-starter-test</​
artifactId​

<​
scope​
>test</​
scope​

</​
dependency​

 
<
​!-- Security --> 
<​
dependency​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-starter-security</​
artifactId​

</​
dependency​

<
​!-- OAuth --> 
<​
dependency​

<​
groupId​
>org.springframework.security.oauth</​
groupId​

<​
artifactId​
>spring-security-oauth2</​
artifactId​

<​
version​
>2.3.3.RELEASE</​
version​

</​
dependency​

<
​!-- Security testing helpers --> 
<​
dependency​

<​
groupId​
>org.springframework.security</​
groupId​

<​
artifactId​
>spring-security-test</​
artifactId​

<​
scope​
>test</​
scope​

</​
dependency​

 
<
​!-- Jackson/JSON library --> 
<
​!-- We need this to allow conversion of new Date and Time API  
(JSR-310) classes in Java to a human readable format suitable for JSON   
output   
--> 
<​
dependency​

<​
groupId​
>com.fasterxml.jackson.datatype</​
groupId​

<​
artifactId​
>jackson-datatype-jsr310</​
artifactId​

</​
dependency​

<
​!-- Needed to handle Hibernate proxies correctly --> 
<​
dependency​

© coaxial.ro 2018​ 26
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

<​
groupId​
>com.fasterxml.jackson.datatype</​
groupId​

<​
artifactId​
>jackson-datatype-hibernate5</​
artifactId​

</​
dependency​

 
<
​!-- JAXB APIs no longer in Java 9 SE - so they require explicit  
listing. These libraries are needed by the Surefire Maven plugin that  
helps with testing --> 
<​
dependency​

<​
groupId​
>javax.xml.bind</​
groupId​

<​
artifactId​
>jaxb-api</​
artifactId​

<​
version​
>2.2.11</​
version​

</​
dependency​

<​
dependency​

<​
groupId​
>com.sun.xml.bind</​
groupId​

<​
artifactId​
>jaxb-core</​
artifactId​

<​
version​
>2.2.11</​
version​

</​
dependency​

<​
dependency​

<​
groupId​
>com.sun.xml.bind</​
groupId​

<​
artifactId​
>jaxb-impl</​
artifactId​

<​
version​
>2.2.11</​
version​

</​
dependency​

<​
dependency​

<​
groupId​
>javax.activation</​
groupId​

<​
artifactId​
>activation</​
artifactId​

<​
version​
>1.1.1</​
version​

</​
dependency​

 
<
​!-- Apache Commons utilities --> 
<​
dependency​

<​
groupId​
>org.apache.commons</​
groupId​

<​
artifactId​
>commons-lang3</​
artifactId​

</​
dependency​

   
<​
dependency​

<​
groupId​
>org.apache.commons</​
groupId​

<​
artifactId​
>commons-collections4</​
artifactId​

<​
version​
>4.1</​
version​

© coaxial.ro 2018​ 27
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

</​
dependency​

 
<
​!-- We need to deal with money amounts in our business code --> 
<​
dependency​

<​
groupId​
>javax.money</​
groupId​

<​
artifactId​
>money-api</​
artifactId​

</​
dependency​

 
<​
dependency​

<​
groupId​
>org.javamoney</​
groupId​

<​
artifactId​
>moneta</​
artifactId​

<​
version​
>1.1</​
version​

</​
dependency​

 
<
​!-- Get H2 jars added to the classpath when running the tests --> 
<​
dependency​

<​
groupId​
>com.h2database</​
groupId​

<​
artifactId​
>h2</​
artifactId​

<​
scope​
>test</​
scope​

</​
dependency​

</​
dependencies​

 
<​
build​

<
​!-- Process application.properties file to activate the current  
profile --> 
<​
resources​

<​
resource​

<​
directory​
>src/main/resources</​
directory​

<​
filtering​
>true</​
filtering​

</​
resource​

</​
resources​

 
<​
plugins​

<​
plugin​

<​
groupId​
>org.springframework.boot</​
groupId​

<​
artifactId​
>spring-boot-maven-plugin</​
artifactId​

<​
executions​

<​
execution​

© coaxial.ro 2018​ 28
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

<​
goals​

<
​!-- This execution goal allows the application  
to be built using 'mvnw package' command.  
Without it you can still build the application  
Using the 'mvnw package spring-boot:repackage'  
--> 
<​
goal​
>repackage</​
goal​

</​
goals​

</​
execution​

</​
executions​

</​
plugin​

 
<
​!-- We need to customize the Surefire plugin - responsible for  
running unit tests; specify the Spring profile to be used when  
running the unit tests --> 
<​
plugin​

<​
groupId​
>org.apache.maven.plugins</​
groupId​

<​
artifactId​
>maven-surefire-plugin</​
artifactId​

<​
configuration​

<​
argLine​
>-Dspring.profiles.active=test</​
argLine​

<​
forkCount​
>1</​
forkCount​

<​
reuseForks​
>true</​
reuseForks​

</​
configuration​

</​
plugin​

 
<
​!-- We need to customize the Failsafe plugin - responsible for   
running integration tests; specify the Spring profile to be used  
when running the tests --> 
<​
plugin​

<​
groupId​
>org.apache.maven.plugins</​
groupId​

<​
artifactId​
>maven-failsafe-plugin</​
artifactId​

<​
configuration​

<​
argLine​
>-Dspring.profiles.active=itest</​
argLine​

<​
forkCount​
>1</​
forkCount​

<​
reuseForks​
>true</​
reuseForks​

</​
configuration​

</​
plugin​

</​
plugins​

© coaxial.ro 2018​ 29
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

</​
build​

</​
project​

To summarize what this pom.xml file does:

● configure our project as a Spring Boot project


● configure Java 9 support
● project is to be packaged as a standalone jar file
● configures required dependencies since we want to use Spring Data JPA, Spring REST
controllers, H2 database for development (​dev​ profile) and MySQL database for
production (​prod​ profile)
● configures Maven to build the project using the the ​package​ phase
● activate Maven profiles and replace the active profile in the ​application.properties
source file
● setup separate profiles for running unit and integration tests
● pass Spring profile information to the Maven plugins running unit and integration tests

To build the application run this command:

./mvnw package

Note that this command builds the application after running the unit tests.

Design
Before we start coding our application we need to think about a design that will drive the actual
work. This step is essential in producing consistent, easily readable code. Once you decided on
a design it becomes straightforward to organize your code and it reduces the amount of
redundant decision making that occurs during development.

We structure our application in several natural layers, each layer being accessible from other
layers only via clearly defined interfaces. These layers are:

● Rest - deals with implementing the RESTful API offered to clients


● Services - implements the business/application logic
● Repository - handles data persistence

© coaxial.ro 2018​ 30
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

The diagram above shows the basic interactions between components in different layers. A
client invokes a Rest controller via the Rest API, the Rest controller invokes zero or more
services via service interfaces, these services invoke one or more components inside the
service layer and zero or more repositories via Repository interfaces. The components in the
service layer are reusable bits of code that are only accessible to services in the service layer.
Other layers could have their own components but most of them will reside in the service layer.

During this phase we also need to think about how we manage our transactions. This requires
some knowledge on how our technology stack (Spring + Hibernate) handles transactions. The
main transaction is handled in the service layer since all operations that manipulate our
application’s data take place there. However this technology stack modifies/enhances some of
the entity instances that might be returned by the service layer so that it can support lazyly
loading the associated entities; these changes are done via bytecode manipulation. That means
that some of the entity’s get methods will return an instance of an object that cannot be used
outside a persistence session.

Spring provides a mechanism called Open Session In View (OSIV) that allows accessing these
getter methods on entities in the Rest layer, outside the service layer where a persistence
session is normally available. This mechanism makes it very easy to work with Spring and
Hibernate but it has some drawbacks that you need to be aware of. A persistence session is
attached to the thread handling the HTTP request and is available until that request is fully
processed. That allows you to transparently invoke the entity getter operations returning
enhanced entity objects in the Rest layer. Each time an entity getter operation that returns a
lazily loaded object is invoked in the Rest layer a new transaction is started and committed. You

© coaxial.ro 2018​ 31
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

can also end up loading a lot of objects from the database when invoking such method that
returns collections of objects. In order to develop a well behaving application you have to keep
this in mind and pay attention to how you access the entities in the Rest layer.

There are ways to do this differently but none are hassle free - you need to process your entities
in the service layer before returning them to the Rest controllers; this can be done manually or
you can write code to automatically strip off any uninitialized Hibernate proxies in the entities.

Using the OSIV mechanism is very convenient as long as you understand how it works and pay
attention when writing your code. OSIV is used in this course’s project.

Data Modeling
We start this project from the bottom up by modelling the basic data entities.An entity is the
basic building block that describes the structure of our data. It maps directly to both a database
table and a Java object.
Below is a diagram describing each entity and its relationships with other entities.

Note​: You can use an online SQL data modelling tool such as ​sqldbm.com​. For basic usage
it was free at the time this course material was prepared. There are other similar services that
offer a free tier.
.

Entities
.

© coaxial.ro 2018​ 32
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Each entity in this case has a simple primary key. A CHAR(40) data type was used for columns
representing finite sets of values(enumerations) such as state and type.

The naming used for the design of the data model is relatively common to MySQL. There’s no
standard for naming things in SQL - not even for MySQL specifically but these rules do work
well:
● table and column names are in snake case (e.g. asset_item) - it is a good idea to avoid
using a mixture of lower and upper case as some databases display platform specific
case sensitivity
● table names are singular - it is a better match for the naming convention of Java objects;
some SQL developers prefer plurals for table names but I believe singular works better
in this case
● column names are singular - most commonly used convention for SQL/database
development
● no prefixes used unless necessary - to keep things simple and readable

The following entities were created:

● User - represents a user accessing the system


○ Id - primary key

© coaxial.ro 2018​ 33
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

○ Name - user name


○ Email - email address
● Role
○ Id - primary key
○ Name - role name (​admin, client, clerk,​ etc.)
● Asset - represents a type of physical asset for renting (e.g. a type of bicycle)
○ Id - primary key
○ Name - the name of each asset - a string of up to 100 characters long
○ Type - the type of this asset - it can be ​premium,​ ​regular​ or ​old
● Asset_item - represents a physical, stockable item for an asset (e.g. a bicycle)
○ Id - primary key
○ Time_added - the time when this asset item was added to the rental stock
○ State - the state of this item (e.g. ​damaged​, ​lost​, ​available​, etc.)
○ Rental_count - the number of time this asset was rented
○ Asset_id - link to the asset to which it belongs
● Rental - represents a rental process
○ Id - primary key
○ State - the state of this rental (e.g. ​pending​, ​active,​ ​closed,​ etc.)
○ Time_created - the time this rental process started
○ Time_activated - the time when this rental became active and billing started
○ Time_closed - the time this rental was closed
○ User_id - link to the user involved in this rental process
● Rental_item - represents an item in the rental process
○ Id - primary key
○ State - the state of this rental item
○ Asset_item_id - link to the asset item
○ Rental_id - link to the parent rental item
● User_bonus_points - represents the bonus points allocated to users
○ Id - primary key
○ Points - the number of bonus points
○ Source - the source of the bonus points (e.g. ​rentals,​ ​vouchers,​ etc.)
● Asset_price - a configuration table that stores prices for each type of asset

We also modelled a link table ​user_role​ - this will be handled automatically for us by the JPA
provider.

DDL Generation
For the production database we are going to use MySQL. In order to setup the database we
need to produce the DDL to be run against the MySQL database. There are two options to
generate the DDL from the data model we designed earlier:
● use the modelling tool to generate the DDL and then complete manually any missing
items

© coaxial.ro 2018​ 34
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

● temporarily change the production properties file to configure Hibernate, our JPA
provider, to generate the DDL, then export the database and extract the DDL

Generate DLL from Modelling Tool


Most of these online tools allow you to generate MySQL SQL from the diagrams.
Since we are going to use MySQL for our production application you can go ahead and
generate the SQL file and save it to ​prod.sql​ in the project’s root folder.

At the top of the generated file add these the following lines to create and use the RENTALS
database and to create the sequence table that is going to be used by Hibernate, our JPA
provider:

© coaxial.ro 2018​ 35
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

CREATE DATABASE RENTALS;

USE RENTALS;

DROP TABLE IF EXISTS `hibernate_sequence`;


CREATE TABLE `hibernate_sequence` (
`next_val` bigint(20) DEFAULT NULL
);

At the bottom of the file add SQL statements to populate the database with application
configuration data - in our case that is asset rental pricing information.

We finish by re-adjust the ​hibernate_sequence​ table.

insert into asset_price (id, basic_price, asset_type,


day_threshold) values (0, 'NEW', 10.00, 0);
insert into asset_price (id, basic_price, asset_type,
day_threshold) values (1, 'REGULAR', 5.00, 3);
insert into asset_price (id, basic_price, asset_type,
day_threshold) values (2, 'OLD', 5.00, 5);

insert into hibernate_sequence (next_val) values (3);

You can execute this SQL file against a MySQL database using the following command:

./mysql -u root -p -f < path_to_project_folder/prod.sql

Generate DDL from the Application


An alternative way to generate the DDL used to initialize the production database is to
temporarily add this line to the ​application-prod.properties​ file:

spring.jpa.hibernate.ddl-​
auto​
=create-drop

© coaxial.ro 2018​ 36
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Then run the application using

./mvnw spring-boot:run -Pprod

This will configure the JPA provider to run the DDL to create all the required database artefacts.

Then you can export the SQL using the ​mysqdump​ utility:

./mysqldump -u root -p -d RENTALS > ~/prod.sql

Spring Boot Configuration File


Spring Boot has a main configuration file (​application.properties​) located in the
src/main/resources​ folder. Because, in most cases, there are different configuration
requirements for different environment types such as production and development we are using
Spring profiles - a feature that allows you to provide different application properties for each type
of environment.

In the ​application.properties​ file we have the properties that are common across all
environments and properties specific to each environment are stored in dedicated files:

● application-dev.properties​ for development


● application-prod.propertie​s for production

© coaxial.ro 2018​ 37
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

In the ​application.properties​ file we use the s


​ pring.profiles.active​ property to specify which
profile to activate.

To activate a specific profile we need to change the ​application.properties​ source file. In order
to avoid that we can pass this property in the command line using the Spring command line
switch syntax (​--spring.profiles.active=dev​) when you run your application. However
if you want to minimize the risk of accidentally turning on the wrong profile we can use Maven
profiles so that we can build the application with the correct profile before it is deployed in its
execution environment.

The special token @activatedProperties@ provided as the value of the active Spring profile is
replaced during building by the active Maven profile (​dev​ or ​prod​).

Now we can build the application with the following style of command, where the -P Maven
switch specifies the profile name.

./mvnw package -P​dev

You can also pass the -P switch when executing other Maven phases, such as when starting up
the application with spring-boot:run

./mvnw spring-boot:run -P​dev

Let’s go in details through the application properties files. Each section and each entry in these
files is commented and we recommend going through the content of each of these files in order
to understand how the application is configured.

application.properties
As mentioned earlier this file stores application properties that are common across all
environment types.

© coaxial.ro 2018​ 38
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

######################################### 
### Main Spring configuration file for the application 
### It contains properties that are common to all profiles 
######################################### 
 
# This value needs to be changed to 'prod' when deploying to production. 
# and to 'dev' during development. We are using Maven profiles to 
# determine the actual value 
spring.profiles.​
active​
=@activatedProperties@ 
 
######################################### 
### Spring JPA configurations 
######################################### 
 
# tell Hibernate to use MySQL SQL 
spring.jpa.database-​
platform​
=org.hibernate.dialect.MySQL​
5D
​ialect 
 
# this instructs Hibernate to use quotes around table and columns to avoid 
conflicts 
# with keywords 
spring.jpa.properties.hibernate.​
globally_quoted_identifiers​
=true 
 
# this is true by default but it is very important 
# so it is explicitly enabled here as well 
# this property allows the Rest controllers to access the 
# database when encountering unitilialized proxy objects in the 
# objects returned from the service layer 
spring.jpa.open-in-​
view​
=true 
 
########################################## 
### Spring boot actuator properties - Spring actuator is a module 
### that helps exposing operational information about your running 
### application (metrics, health indicator, etc) 
########################################## 
 

© coaxial.ro 2018​ 39
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

# we run the management server on a separate port 


management.server.​
port​
=​
8081 
 
# enable endpoints explicitly 
management.endpoints.enabled-by-​
default​
=false 
 
# enable REST access for all actuator endpoints 
management.endpoints.web.exposure.​
include​
=* 
 
# change the default /actuator path to a custom value 
management.endpoints.web.base-​
path​
=/mng​

 
# enable health and metrics actuator metrics 
management.endpoint.health.​
enabled​
=true 
management.endpoint.metrics.​
enabled​
=true 
management.endpoint.prometheus.​
enabled​
=true 
 
############################################ 
### Tomcat settings 
############################################ 
# enable Tomcat access logs 
server.tomcat.accesslog.​
directory​
=/tmp/rentals/logs 
server.tomcat.accesslog.​
enabled​
=true 
server.tomcat.accesslog.​
pattern​
=common 
 
################## OAuth2 ######################### 
# Client credentials for one oauth2 client application (e.g. a mobile 
# application accompanying our Rentals REST API 
rentals.security.oauth​
2​
.client​
1​
.client-​
id​
=rentals-client-id 
rentals.security.oauth​
2​
.client​
1​
.client-​
secret​
=$​
2​
a$​
10​
$​
7​
wQl​
0​
vta​
4​
a​
5​
VL​
7​
eMSrKXm.L
Kps​
0z
​y/gzyDpZpiG​
5L
​p​
23​
iTV.ljj/. 
# OAuth2 resource ID for our REST API 
rentals.security.oauth​
2​
.resource-​
id​
=rentals-api 
# Number of seconds that the access token is valid for 
rentals.security.oauth​
2​
.access-token-valid-for-​
seconds​
=​
3600 

© coaxial.ro 2018​ 40
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

application-dev.properties
This file contains application properties which are only available in the ​dev​ profile (during
development).

For development we are using an H2 embedded database, we drop and create the application
database tables every time we restart the application and we enable SQL tracing in JPA to help
with troubleshooting issues.

######################################### 
### Spring configuration file used for development 
### 
### Properties set in this file override properties 
### in the application.properties file 
######################################### 
 
######################################### 
### Spring datasource configurations 
### We want to use H2 for development instead of MySQL 
######################################### 
 
# JDBC URL for the H2 database 
spring.datasource.​
url​
=jdbc:h​
2​
:file:~/rentals 
 
# H2 database credentials 
spring.datasource.​
username​
=sa 
spring.datasource.​
password​

 
# H2 JDBC driver class 
spring.datasource.driver-class-​
name​
=org.h​
2​
.Driver 
 
######################################### 
### Spring JPA configurations 
### Setting useful properties for a development environment 

© coaxial.ro 2018​ 41
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

######################################### 
 
# This creates Hibernate to create the tables based on the entity definitions 
# every time the application is started 
# For a production environment turn this off 
spring.jpa.hibernate.ddl-​
auto​
=create-drop 
 
# it helps with troubleshooting SQL issues; all SQL statements issued 
# are logged to output 
spring.jpa.show-​
sql​
=true 
 
# this is useful for troubleshooting issues with more complicated SQLs 
spring.jpa.properties.hibernate.​
format_sql​
=true 
 
# point to Logback's configuration file for development 
logging.​
config​
=classpath:logback-dev.xml 

application-prod.properties
This file contains application properties for the ​prod​ (production) profile.
For production we are using a MySQL database, we use a dedicated logging configuration file
and we enable SSL for our embedded Tomcat server serving our RESTful API.

######################################### 
### Spring configuration file for production 
######################################### 
 
######################################### 
### Spring datasource configurations 
######################################### 
 
# JDBC URL for the MySQL database 
spring.datasource.​
url​
=jdbc:mysql://localhost:​
3306​
/RENTALS?​
useSSL​
=false 
 

© coaxial.ro 2018​ 42
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

# MySQL database credentials 


spring.datasource.​
username​
=root 
spring.datasource.​
password​
=rentalspass 
 
# MySQL JDBC driver class 
spring.datasource.driver-class-​
name​
=com.mysql.jdbc.Driver 
 
# Use this just to create the MySQL DDL for 
# initializing the production database 
#spring.jpa.hibernate.ddl-auto=create-drop 
 
######################################### 
### Logging 
######################################### 
 
# point to Logback's configuration file for production 
logging.​
config​
=classpath:logback-prod.xml 
 
########################################## 
### Security settings 
########################################## 
# enable SSL 
server.​
port​
=​
8443 
# path to the Tomcat keystore; Tomcat cannot read this file 
# from the class path so we need to specify a file system location 
# update to a suitable location for your production environment 
server.ssl.key-​
store​
=./tomcat.keystore 
server.ssl.key-store-​
password​
=kspassword 
server.ssl.key-​
password​
=kspassword 
server.ssl.keyStoreType: PKCS​
12 
server.ssl.keyAlias: rentals 

As you might have noticed in the application-*.properties files so far we only provided
infrastructure and operational parameters.
For business level application properties we define a separate configuration file:
application-configuration.properties​. This separation is not required but it is done purely for

© coaxial.ro 2018​ 43
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

organizational reasons. If you have business level properties that are profile dependent you
would need to add those properties to the corresponding application-*.properties file for the
respective profile.
For details on how these business level application properties are managed see section
Application Configuration​.

Cross Cutting Concerns


Cross cutting concerns represents functionality that needs to be addressed across the entire
application. Examples are validation, caching, error handling, logging, security, monitoring, etc.
Since the implementations of these concerns have an impact on the rest of the code it is
important to address them early on in this tutorial.

Validation
Validation of data for our data model entities is performed by Spring Boot using a set of
annotations defined in Java Bean Validation API (JSR-380). These annotation allows you to set
constraints on the value for each of the fields of an entity class.

Sometimes a single entity has a different set of constraints for its fields depending on the
application layer at which the entity is processed. For instance an entity might have a timestamp
field ​createdAt​ that can be null in the REST layer but it has to be not null in the persistence
layer.

To distinguish the application of constraints at various layers in the application we create two
empty interface classes called ​Rest​ and P
​ ersistence​ in the ​course.a1.rentals.validation
package. These interfaces will be used in the validation annotations to specify what type of
validation needs to be done (validation groups).

© coaxial.ro 2018​ 44
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Caching
Caching is another cross cutting concern that needs to be addressed in most applications. We
use in our project Spring’s facilities for managing caches which makes it very easy to cache
data and avoid repetitive request to the persistence layer.

In order to enable Spring’s annotation based cache management you need to annotate the
application startup class​ with the aptly named ​@EnableCaching​ annotation. That instructs
Spring to search for method names annotated with the ​@Cacheable​ annotation and cache the
returned data.

The default cache manager that Spring uses is very simple and based on Java’s
ConcurrentHashMap - that is acceptable for some applications and some types of data.
If a more complex cache manager is required, it can be configured via Spring - Spring has
support for a large number of commonly used cache managers (see this ​Spring Boot Caching
documentation​ for the full list).
For instance, if you want to use Ehcache, you need to add the ​spring-boot-starter-cache
dependency in your pom.xml file and provide in the ​src/main/resources​ folder the
ehcache.xml ​cache configuration file.

© coaxial.ro 2018​ 45
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

In our application we cache the asset prices. In the ​AssetPriceComponentImpl​ class we


annotated the ​getAssetPrice​ method with the ​@Cacheable​ annotation. We provided the
annotation with the ​assetPrice​ string which indicates the name of the cache to be used for
storing the results.

Spring will create a cache with the key being formed by the method’s parameters, in our case
that is just the asset type. The value to be cached is the data returned by the annotated method.

To refresh a value in the cache you need to mark a method with the​ @CachePut​ annotation -
that will cause the value returned from that method to be placed into the cache and replace the
existing entry with the provided key (​assetType​ in our case).

Exception Handling
There are two important aspects to exception handling:
● It has to provide the best possible error message in the application logs so that
troubleshooting issues is easier
● It has to provide the client with clear error message when an error is encountered.

We address both these concerns by creating a class


(​course.a1.rentals.rest.RestExceptionHandler​) used to handle exceptions thrown in all the
REST controllers in the application (a global exception handler for controllers). Spring ensures
that every exception thrown in one of our REST controllers is passed to our exception handler.
© coaxial.ro 2018​ 46
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Spring will select the right method in the class for handling a particular exception based on the
@ExceptionHandler​ annotation defined for methods.

Below is the method in our exception handler class used to handle all application exceptions
(instances of ​RentalsException​). The method name is not important; the important bits are the
@ExceptionHandler(RentalsException.class)​ annotation and the method signature which
can be very flexible; we use a ​RentalsException​ parameter and a ​Locale​ parameter.
Spring will pass the actual exception to be handled in the ​RentalsException​ parameter and the
locale for the request that triggered the exception in the ​Locale​ parameter. These are used to
generate a localized error message to be sent to the client.

// This Spring annotation marks this method as a handler for RentalsException  


// exceptions 
@ExceptionHandler​
(R
​entalsException​
.c
​lass​


// Since RentalsExceptions are designed to be localizable this handler will  
// convert the messageId provided by the application exception into a  
// localized message suitable to be presented to the REST client 

public​ResponseEntity​
<R​estErrorMessage​
>   
handleRentalsRestException​
(R
​entalsException ex​
,​Locale locale​
)​{
​ 
/
​/ The RentalsException getMessage() method returns the messageId; use  
// that and the provided Locale to generate a localized message 
  String errorMessage ​
=​messageSource​
.g​etMessage​
(e​x​
.g
​etMessage​
(),​ex​
.​getArgs​
(), 
locale​
); 
  // Return a response with a RestErrorMessage providing the localized  
// error and the messageId 
r
​eturn​​
new​ResponseEntity​
<>(new​RestErrorMessage​
(​ex​
.g
​etMessage​
(), 
errorMessage​
),​HttpStatus​
.B
​AD_REQUEST​
); 

We also define a method to handle any generic exceptions. We send the client a generic,
localized error message instead of the default error message. Note that Spring provides a
decent error message for unhandled exceptions (see below) but it’s still good practice to be in
control of what is being sent to the client in this case - for security reasons you want to ensure
no stack traces or other unfiltered error messages reach the client.

We also check here if the development profile (dev) is activated and in that case we append the
stack trace to error message sent to the client.

© coaxial.ro 2018​ 47
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


// catch all handler; provide to the REST client a generic "unknown error" message 
/
​/ in its own Locale instead of the default, possible more verbose error message 
@
​ExceptionHandler​
(E​xception​
.​class​

p
​ublic​​
ResponseEntity​
<​
RestErrorMessage​
> ​
handleException​
(​Exception​e
​x​
, ​
Locale 
locale​
) { 
// log the exception first 
l
​og​
.​log​
(​
Level​
.S​EVERE​
, "
​Error"​
, e
​x​
); 
  
S
​tring​​
errorMessage​= m
​essageSource​
.​getMessage​
(​
UNKNOWN_ERROR​
, n
​ull​
, ​
locale​
); 
   
// if the development profile is active, add the stacktrace to the error  
// message 
S
​tring​​
stackTrace​= ​
null​

i
​f​
(​rentalsUtils​
.​
isDevProfileActive​
()) { 

stackTrace​= ​
ExceptionUtils​
.​
getStackTrace​
(e​x​
); 

 
r
​eturn​​
new​​
ResponseEntity​
<>( 
n
​ew​​
RestErrorMessage​
(​UNKNOWN_ERROR​
, ​
errorMessage​
, ​
stackTrace​
), 
H
​ttpStatus​
.​INTERNAL_SERVER_ERROR​
); 
}

Default error message Custom error message

We also intercept the security related exception ​org.springframework.security.access.


AccessDeniedException​ which is thrown when a user attempts to invoke an action that is not
allowed for its supplied credentials.
Whenever this exception is thrown we want to log the occurrence and send the client a 403
Forbidden HTTP status code.

@ExceptionHandler​
(A​ccessDeniedException​
.c
​lass​

© coaxial.ro 2018​ 48
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


public​R
​esponseEntity​
<R​estErrorMessage​
>   
handleMethodNotSupportedException​

AccessDeniedException​​
ex​
, L
​ocale​​
locale​
) { 
l
​og​
.l​og​
(L​evel​
.I​NFO​
, "
​Access denied error"​
, e
​x​
); 
r
​eturn​​
new​R
​esponseEntity​
<R​estErrorMessage​
>( 

new​​
RestErrorMessage​
(e
​x​
.g
​etMessage​
()), ​
HttpStatus​
.F​ORBIDDEN​
); 
}

Another exception that we handle in this class is


javax.persistence.EntityNotFoundException​. This error is thrown when the persistence layer
is trying to read an entity using a primary key that does not exist in the database. This is a
common occurrence as the ids of the entity are passed to our REST API via URL parameters or
JSON documents and mistakes can easily be made. Handling this exception explicitly ensures
the client receives a useful error message.


@ExceptionHandler​
(​EntityNotFoundException​
.​class​


public​R
​esponseEntity​
<R​estErrorMessage​
> h
​andleEntityNotFoundException​

EntityNotFoundException​e
​x​
, ​
Locale​l
​ocale​
) { 
r
​eturn​​
new​R
​esponseEntity​
<R​estErrorMessage​
>( 

new​​
RestErrorMessage​
(e
​x​
.g
​etMessage​
()),   
HttpStatus​
.B
​AD_REQUEST​
); 
}

Transaction Management
Transaction management is handled by Spring - service classes and methods are annotated
with the ​@Transactional​ annotations.

A class marked with this annotation is modified by Spring so that every method that does not
override the ​@Transactional​ annotation executes within the boundaries of a transaction.

© coaxial.ro 2018​ 49
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

If we want to provide different transactional characteristics to methods in these classes we can


annotate with ​@Transactional​ the respective method and provide different values for the
annotation attributes. For instance if we want to have one method execute within a read only
transaction we can annotated with ​@Transactional(readOnly = true)

Logging
By default Spring Boot provides support for ​Logback logging framework​. It’s a dependency
inherited from any of the ​spring-boot-starter-*​ projects we listed as dependencies in our
pom.xml​ file.

The default logging configuration writes log messages to console. If we want to change any
logging parameters we need to provide values in our ​application*.properties​ files.

In practice the logging requirements for the ​dev​ and ​prod​ profiles are different so we provide
specific configuration in the respective properties files: ​application-dev.properties ​or
application-prod.properties.

If you need to change the logging levels for some loggers you need to add entries of the form
logging.level.<logger_name>​ in the corresponding ​application*.properties​ file (see
example below).

In order to enable logging to a log file you can to provide the name of the file in the properties
file using the ​logging.file​ property.

logging.file=rentals.log

© coaxial.ro 2018​ 50
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

logging.level.org.springframework.security=DEBUG
logging.level.org.hibernate=ERROR

Note that only basic logging configuration can be provided via the ​application*.properties​ files.
For finer grained configuration such as log file rolling - which is a requirement for a production
environment - we need to use the logging framework’s native configuration mechanism.

In our Eclipse project we created a ​logback-prod.xml ​file in src/main/resources with the content
below. This xml file contains native Logback configuration data.

<configuration>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>rentals.log</file>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
​ <!-- daily rollover based on size - it contains the date and the file
index -->
<fileNamePattern>rentals.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
​<!-- keep 30 days of history and cap it at 10GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
​<!-- cap each daily file at 100MB - a day can have more than one log
file -->
<maxFileSize>100MB</maxFileSize>
</rollingPolicy>

<encoder>
<pattern>%date{ISO8601} [%thread] %-5level %logger{35} -
%msg%n</pattern>
</encoder>
</appender>

<root level="ERROR">
<appender-ref ref="FILE" />
</root>
</configuration>

© coaxial.ro 2018​ 51
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

This Logback configuration file that’s used in production for our application is rolling log files
daily while respecting a few size constraints: each file can be up to 100MB, it keeps 30 days
worth of log files and the total log file is capped at 10GB.

In the ​application-prod.properties​ file we specify the log configuration file to be used via the
logging.config​ property; the ​classpath:​ prefix loads the file from the application’s class path.

Similar configuration files are used for development(​logback-dev.xml​), unit


testing(l​ogback-test.xml​) and integration testing(​logback-itest.xml​).

Metrics
A production level application has to provide operational metrics so that its state can be
analyzed when needed.

Spring Boot Actuator is a module that provides an application with production ready features
such as monitoring, auditing and basic operational management tasks.

We configure Spring Actuator in the ​application.properties​ file. We enable metrics exposure


via REST for all metrics using ​management.endpoints.web.exposure.include=*
 
Spring Actuator provides a large number of endpoints - we only enable explicitly health and
metrics endpoints:

management.endpoints.enabled-by-default=false
management.endpoint.health.enabled=true
management.endpoint.metrics.enabled=true
management.endpoints.web.base-path=/mng3
management.server.port=8081

Once you start the application with​ ./mvnw spring-boot:run -Pdev ​you can see the list
of metrics available by accessing this URL: ​http://localhost:8081/mng3/metrics 
 

© coaxial.ro 2018​ 52
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

 
 
To access a single metric use URL ​http://localhost:8081/mng3/metrics/<metric_name>​ (e.g.
http://localhost:8081/mng3/metrics/jvm.memory.committed​).

Getting metrics one REST call at a time is not very useful; Spring actuator has support for a
Prometheus​ endpoint. Prometheus is an open source monitoring solution and the Spring
endpoint for it allows easy integration - a Prometheus server can now scrape all metrics from
your application in one go.

The Prometheus endpoint is available at ​http://localhost:8081/mng3/prometheus​ and the


output looks like this:

# HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be


used for memory management
# TYPE jvm_memory_max_bytes gauge
jvm_memory_max_bytes{area="nonheap",id="CodeHeap 'non-nmethods'",} 5836800.0
jvm_memory_max_bytes{area="nonheap",id="Metaspace",} -1.0
jvm_memory_max_bytes{area="nonheap",id="CodeHeap 'profiled nmethods'",}
1.22908672E8
jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",}

© coaxial.ro 2018​ 53
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

1.073741824E9
jvm_memory_max_bytes{area="heap",id="G1 Eden Space",} -1.0
jvm_memory_max_bytes{area="heap",id="G1 Old Gen",} 4.294967296E9
jvm_memory_max_bytes{area="heap",id="G1 Survivor Space",} -1.0
jvm_memory_max_bytes{area="nonheap",id="CodeHeap 'non-profiled nmethods'",}
1.22912768E8

To enable the Prometheus endpoint we do the following:

1. Add a dependency on ​micrometer-registry-prometheus​ in our pom.xml file

2. Enable the actuator endpoint for Prometheus in the ​application.properties​ file

 
The actuator endpoints are available by default at the ​/actuator​ path and on the same port as
the main application (8080). In most production environments access to the management
endpoints should be restricted so that they are only accessible from the same location where
the main application is deployed.
One option of controlling access is to use a separate port number and a custom path for the
actuator endpoints. We can do this by using the following properties in the
application.properties​ file: ​management.server.port​ and
management.endpoints.web.base-path​. We changed them so that the management
endpoints are run on port 8081 with path ​/mng3

© coaxial.ro 2018​ 54
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Security
Secure access to a REST API is critical for most applications. We build our rentals REST API
for secure consumption by various applications such as a mobile or a SPA web application and
we want different users to have different permissions.

We use three mechanisms for securing our application:

1. Ensure only secure client applications (mobile or SPA) can connect to our REST API
2. Ensure only users registered in our application can connect to our REST API
3. Ensure that certain methods and URLs in our REST API can only be accessed by
certain users

We define three roles for users in our application: ADMIN, CLERK and CLIENT.
A user can have any combination of roles. Roles will dictate the level of access to methods and
URLs in our application.

For testing during development we create 3 users, each with one role associated to it.

Email Password Role

admin@rentals.io pass ADMIN

clerk@rentals.io pass CLERK

client@rentals.io pass CLIENT

OAuth2

We are going to use OAuth2 to control access to our application’s API . OAuth2 is an industry
standard authorization framework. It allows us to ensure that only trusted applications and
registered users can access our REST API.

In order to use OAuth2 in our project we need to understand first a few OAuth2 concepts:

Client​ - a third party application that is trying to get access to information on the user’s behalf
(usually a part of their account information)
Resource Owner ​- the end-user who grants or denies the Client access to the account
information

© coaxial.ro 2018​ 55
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Authorisation Server​ - this is the server that performs the authorization - it usually displays an
interface asking the Resource Owner (end-user) if they agree to provide the Client with the
requested access to their information. If the user accepts an an access token or authorisation
code is issued that can be used to query the Resource Server
Resource Server​ - the server that requires user authorisation; requests to the Resource Server
need to contain the access token issued by the Authorisation Server

The most common use of OAuth2 is in applications requesting access to


Google/Facebook/Twitter/etc (Resource Server) user information. The user (Resource Owner)
navigates to a third party application (Client). They are asked to sign in with their Resource
Server accounts so that the application can gain access to certain APIs exposed by the
Resource Server on behalf of the user.

The diagram below depicts this common OAuth2 flow.

For our project the OAuth2 roles are preserved but they are organized differently:

© coaxial.ro 2018​ 56
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Client​ - this is going to be our own mobile or SPA application that needs to access our Rentals
REST API on behalf of a user
Resource Owner ​- remains the end-user
Authorisation Server​ - this is the server that performs the authorization; it will be just a
component inside our own application
Resource Server​ - this is our Rentals REST API server/application

OAuth2 provides several grant types - for our own project, since the ​Client​ is an application that
we control we are going to use ​Resource Owner Password Credential Grant​ which allows
obtaining an access token using a username and password.
The ​Client​ (our mobile/SPA application) will ask for user (​Resource Owner​)’s credentials and
forward them to our Rentals REST API application. The ​Authorisation Server​ running inside it
takes the credentials, verifies them via Spring Security and generates an access token that is
returned to the ​Client​. The ​Client​ then will use this access token when issuing requests against
our ​Resource Server​ (the Rentals REST API application).

The OAuth2 flow now becomes:

© coaxial.ro 2018​ 57
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Note that for the OAuth2 flows to be secure all data exchanges must be done over secured
channels. That means we need to configure HTTPS for our Rentals REST API application.

SSL
We are going to secure the communication channel between the client and our Rentals REST
API only for the production environment.

© coaxial.ro 2018​ 58
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Start by creating a keystore and a self-signed certificate for the embedded Tomcat server
running in our application. Run the following command from the root of your project and provide
the required details as asked.

keytool -genkey -alias rentals -keyalg RSA -storetype PKCS12 -keysize


2048 -validity 365 -keypass ​kspassword​ -storepass ​kspassword
-keystore ./tomcat.keystore

In the ​application-prod.properties​ file add the following lines:

Because Tomcat cannot read certificates from the classpath we need to specify a file system
location for it.

Method Level Security


Spring security allows us to specify what authorities are required for invoking a method using
method annotations.
To enable this mechanism one ​@Configuration​ class needs to also have the
@EnableGlobalMethodSecurity(prePostEnabled = true) ​annotation present.
In our case we added ​@EnableGlobalMethodSecurity(prePostEnabled = true)​ to our
course.a1.rentals.RentalsApplication​ class.

© coaxial.ro 2018​ 59
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

To secure a method use the ​@PreAuthorize​ annotation as in the example below. The
@PreAuthorize​ annotation takes as parameter expressions that allows us to specify the
required authorities or roles that the current user has to have.
We can use either the​ hasAuthority() ​or ​hasRole()​ expressions. ​hasRole()​ has a peculiarity in
the sense that the roles need to have a ROLE_ prefix. That makes it a bit more difficult to use.
For this reason we prefer to use the newer ​hasAuthority()​ method.

In our case Spring security authority name is the same as the role name as defined in our
application database. Our ​course.a1.rentals.model
Role​ entity implements ​org.springframework.security.core.GrantedAuthority​ and it returns
the role name in the ​getAuthority​ method.

We allow only users with the ADMIN or CLERK authorities to search for users so the
@PreAuthorize​ annotation looks like this:

@Transactional​
(r​eadOnly​= ​
true​

@Override 
@PreAuthorize​
(​"hasAuthority('ADMIN') or hasAuthority(‘CLERK’)"​

public​​
Page​
<U
​ser​
> s
​earchUser​
(U​serSearchFilter​​
filter​
, P
​ageable​p
​ageable​
) { 

if​
(​
filter​== ​
null​|| ​
filter​
.i
​sEmpty​
()) { 

return​t
​his​
.u​serRepository​
.f​indAll​
(p
​ageable​
); 


return​t
​his​
.u​serRepository​
.f​indByEmailContainingIgnoreCase​
(f​ilter​
.g
​etEmail​
(), 
pageable​
); 
}

© coaxial.ro 2018​ 60
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

URL Level Security


Access to our API’s URLs is protected by OAuth2 Resource Server; access control rules are
provided in the ​course.a1.rentals.security.oauth2.ResourceServerSecurityConfiguration
class, in ​configure(HttpSecurity)​ method.

We allow access to the following URLs to all users, authenticated or not


● Our management server URL - because the management server is configured to run on
its dedicated port (8081) we can protect access via networking rules when deploying in a
production environment
● Api documentation
● H2 database console - this is only available in the ​dev​ profile

Access to the actual REST API URLs is restricted to only authenticated users. A finer level of
control is possible - using user authorities/roles we can further restrict access to portions of our
API.

Implementation
Let’s go now through the implementation details of our OAuth2 based security for the Rentals
API project.
All of our code is located in package ​course.a1.rentals.security ​with OAuth2 specific code in
course.a1.rentals.security.oauth2​.

We start by adding these two dependencies in our pom.xml file to enable Spring Security and
OAuth2 in our project:

© coaxial.ro 2018​ 61
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

<!-- Security --> 



<​
dependency​

<
​g
​roupId​
>​
org.springframework.boot​
</​
groupId​

<
​a
​rtifactId​
>​
spring-boot-starter-security​
</​
artifactId​


</​
dependency​


<!-- OAuth --> 

<​
dependency​

<
​g
​roupId​
>​
org.springframework.security.oauth​
</​
groupId​

<
​a
​rtifactId​
>​
spring-security-oauth2​
</​
artifactId​

<
​v
​ersion​
>​
2.3.3.RELEASE​
</​
version​


</​
dependency​
>

We then need to write code to configure the OAuth2 authorization and resource servers running
now in our application and to integrate them with Spring Security so that we when we pass in
the user’s credentials via the ​Resource Owner Password Credential Grant​ flow the
authorization server can verify them and issue an access token.

We are going to analyze each class used for setting up security:

RentalsUserDetails
This class extends Spring Security’s ​UserDetails ​class. It connects our application’s data and
Spring Security’s so that Spring Security can perform security operations using the user
information provided by our application.

© coaxial.ro 2018​ 62
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

import​j
​ava​
.u
​til​
.C
​ollection​

import​​
org​
.s​pringframework​
.s​ecurity​
.c​ore​
.​GrantedAuthority​

import​​
org​
.s​pringframework​
.s​ecurity​
.c​ore​
.​userdetails​
.U
​serDetails​

import​​
course​
.​a1​
.r
​entals​
.m​odel​
.U​ser​

 
// Class needed to implement Spring security. It is a wrapper over 
// our own User entity 
/** 
* Spring security user details implementation. 
*/ 
public​​
class​R
​entalsUserDetails​i
​mplements​U
​serDetails​{ 

private​s
​tatic​​
final​l
​ong​​
serialVersionUID​= 7
​083570663075996165L​


private​U
​ser​u
​ser​

 

public​R
​entalsUserDetails​
(​User​​
user​
) { 
t
​his​
.u
​ser​= ​
user​



@Override 

public​C
​ollection​
<? ​
extends​G
​rantedAuthority​
> g
​etAuthorities​
() { 
r
​eturn​u
​ser​
.g​etRoles​
(); 


@Override 

public​S
​tring​g
​etPassword​
() { 
r
​eturn​t
​his​
.u​ser​
.g​etPassword​
(); 


@Override 

public​S
​tring​g
​etUsername​
() { 
r
​eturn​t
​his​
.u​ser​
.g​etEmail​
(); 


@Override 

public​b
​oolean​​
isAccountNonExpired​
() { 
r
​eturn​t
​rue​



@Override 

public​b
​oolean​​
isAccountNonLocked​
() { 
r
​eturn​t
​rue​



@Override 

© coaxial.ro 2018​ 63
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


public​b
​oolean​​
isCredentialsNonExpired​
() { 
r
​eturn​t
​rue​



@Override 

public​b
​oolean​​
isEnabled​
() { 
r
​eturn​t
​rue​


}

RentalsUserDetailsService
This is a regular ​@Service​ class that implements Spring Security’s
org.springframework.security.core.userdetails.UserDetailsService​ interface. The role of
this class is to look up a ​UserDetails​ instance by username (or, in our case, email address).

/** 
* Service class required by Spring security - it reads user details using 
* an email address. 
*/ 
@Service 
@Transactional​
(r​eadOnly​= ​
true​

public​​
class​R
​entalsUserDetailsService​i
​mplements​U
​serDetailsService​{ 

@Autowired 

private​U
​serRepository​u
​serRepository​

 

@Override 

public​U
​serDetails​l
​oadUserByUsername​
(S​tring​e
​mail​
) ​
throws 
UsernameNotFoundException​{ 
U
​ser​u
​ser​= ​
userRepository​
.​findByEmailIgnoreCase​
(​
email​
).​
orElseThrow​

() -> n
​ew​U
​sernameNotFoundException​
("​User not found"​
)); 
/
​/ initialize roles - we need to return a fully initialized User instance 
u
​ser​
.g
​etRoles​
().​
size​
(); 
r
​eturn​n
​ew​​
RentalsUserDetails​
(u
​ser​
); 

}

© coaxial.ro 2018​ 64
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

SecurityConfiguration
This class is used to setup Spring Security and integrate it with ​OAuth2​. It configures the
authentication manager bean with the ​RentalsUserDetailsService​ so that user information can
be retrieved from the application’s database and it exposes the authentication manager as a
Spring bean so that ​OAuth2​’s authorization server can find it and use for authenticating users
(see ​AuthorizationServerSecurityConfiguration​).

This ​@Configuration​ class is marked with ​@EnableWebSecurity​ - a Spring Security


annotation used to identify configuration classes to be used for configuring web security.

@Configuration 
// Spring Security annotation - it allows us to customize web security 
@EnableWebSecurity 
public​​
class​S
​ecurityConfiguration​​
extends​W
​ebSecurityConfigurerAdapter​{ 

@Autowired 

private​R
​entalsUserDetailsService​u
​serDetailsService​


/** 
* Configures the authentication manager with the user details service. 
*/ 

@Override 

protected​​
void​​
configure​
(A
​uthenticationManagerBuilder​a
​uth​
) t
​hrows​E
​xception​{ 
  auth​
.u​serDetailsService​
(​userDetailsService​
); 


/** 
* Exposes the authentication manager as a bean to be used by OAuth2. This allows 
* OAuth2 to authenticate users when needed using our UserDetailsService. 
*/ 

@Override 

@Bean​
(n​ame​
="​authMgr"​


public​A
​uthenticationManager​a
​uthenticationManagerBean​
() t
​hrows​E
​xception​{ 
r
​eturn​s
​uper​
.a​uthenticationManagerBean​
(); 

}

AuthorizationServerSecurityConfiguration
This class is used to configure OAuth2 authorization server running inside our application.

© coaxial.ro 2018​ 65
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

The class is annotated with ​@EnableAuthorizationServer ​which tells Spring Security OAuth2
to run an authorization server.

The authentication manager bean (​"authMgr"​) created by our ​SecurityClass​ is passed to the
configurer for the authorization server. That allows the authorization server to check the validity
of user’s credentials before issuing an access token.

This class also defines the OAuth2 client details - some of the client details are read from the
application.properties​ file, some are provided directly. More than one client can be registered;
if you have, for instance, a mobile application and a web application and you’d like to configure
each with separate characteristics, such as different access token validity periods, then you can
register a second client with its own details.

In our case we only register one client with the following details:

client-id​: ​rentals-client-id
This is the a client identifier - it can be any string that uniquely identifies a client

client-secret​ :
$2a$10$7wQl0vta4a5VL7eMSrKXm.LKps0zy/gzyDpZpiG5Lp23iTV.ljj/.
This is a secret associated with this client. In our case the string specified in this property is the
encoded value of the actual secret: ​ddkl44#SDsfg​. The value is encoded using the encoder
specified in ​RentalsApplication​ - the startup configuration class for our application
(​BCryptPasswordEncoder​).
To obtain the encrypted password run the application in the ​dev​ profile (​./mvnw
spring-boot:run -Pdev)​; the ​DevConfiguration​ class outputs the encoded value for this
secret - just search the output for “​Client-secret encoded password​”. Note that the
encoded value is different for every run - it does not matter; just pick an encoded value and
update ​application.properties​ file with it.

resource-id​: ​rentals-api
The id of the protected resource

access-token-valid-for-seconds​: 3600
The number of seconds for which an access token is valid

We also configure directly:

grant types​: "password", "refresh_token"


The grant types available for this client:
password​ - for the ​Resource Owner Password Credential Grant​ OAuth grant type
refresh_token​ - to be able to refresh the token after expiration

© coaxial.ro 2018​ 66
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

scopes​: "read", "write"


The scopes available for this client. When a client makes a request to get an access token it
passes a list of scopes it needs. This parameter lists all available scopes for this client.

@Configuration 
@EnableAuthorizationServer 
public​​
class​A
​uthorizationServerSecurityConfiguration​e
​xtends 
AuthorizationServerConfigurerAdapter​{ 

@Autowired 

// Spring annotation specifying the name of the exported authentication 

// manager bean instance that we want to inject in this field 

@Qualifier​
("​authMgr"​


private​A
​uthenticationManager​a
​uthenticationManager​

   

// Spring annotation - allows specifying default values for fields; 

// in this case we use the ${} notation which means that the default 

// value is to be read from the application.properties files 

@Value​
("​${rentals.security.oauth2.client1.client-id}"​


private​S
​tring​​
clientId​


@Value​
("​${rentals.security.oauth2.client1.client-secret}"​


private​S
​tring​​
clientSecret​


@Value​
("​${rentals.security.oauth2.resource-id}"​

​​
private​S
​tring​​
resourceId​
;   

@Value​
("​${rentals.security.oauth2.access-token-valid-for-seconds}"​

​​
private​i
​nt​a
​ccessTokenValidForSeconds​

 

@Override 

public​v
​oid​c
​onfigure​
(A​uthorizationServerEndpointsConfigurer​c
​fg​

t
​hrows​​
Exception​{ 
/
​/ configure the authorization server with the authentication manager 
/
​/ from Spring Security 
c
​fg​
.a
​uthenticationManager​
(t
​his​
.a​uthenticationManager​
); 

   

@Override 

public​v
​oid​c
​onfigure​
(C​lientDetailsServiceConfigurer​​
clients​
) t
​hrows​​
Exception​{ 

© coaxial.ro 2018​ 67
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

/
​/ configure our client 
  ​
clients​
.i
​nMemory​
() 
  .​
withClient​
(​
this​
.​clientId​

  .​
authorizedGrantTypes​
("
​password"​
, ​
"refresh_token"​

  .​
accessTokenValiditySeconds​
(​
this​
.​accessTokenValidForSeconds​

  .​
secret​
(t
​his​
.c​lientSecret​

  .​
scopes​
("
​read"​
, "
​write"​

  .​
resourceIds​
(t
​his​
.r​esourceId​
);   

}

ResourceServerSecurityConfiguration
This class enables and configures the OAuth2 resource server.
The resource server is configured with the resource-id value from the ​application.properties
file - it must be the same value used when configuring the client in
AuthorizationServerSecurityConfiguration.

When Spring Security OAuth2 sees this annotation it installs a filter that authenticates requests
based on OAuth2 tokens - the actual requests to be authenticated are specified in the
configure(HttpSecurity)​ method. We only authenticate /​ api/rentals/** U ​ RLs and leave free
for access the metrics end point, the Swagger URL for API documentation and the H2 console
that is used during development.

Since we have a stateless application we also disable HTTP sessions.

@Configuration 
// Enable OAuth2 resource server 
@EnableResourceServer 
public​​
class​R
​esourceServerSecurityConfiguration​e
​xtends 
ResourceServerConfigurerAdapter​{   

// set the resource id from application properties 

@Value​
("​${rentals.security.oauth2.resource-id}"​


private​S
​tring​​
resourceId​

   

@Override 

public​v
​oid​c
​onfigure​
(​HttpSecurity​h
​ttp​
) t
​hrows​​
Exception​{ 

© coaxial.ro 2018​ 68
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

h
​ttp 
/
​/ disable csrf protection as our REST API should be safe against  
// it 
.​
csrf​
().​
disable​
() 
/
​/ no HTTP session necessary   
.​
sessionManagement​
().​
sessionCreationPolicy​

SessionCreationPolicy​
.S
​TATELESS​
).​
and​
() 
/
​/ secure requests 
.​
authorizeRequests​
() 
/
​/ allow access to the management API - this is running on a  
// separate port and can be protected by disallowing network  
// access to it allow access to the api docs and to the h2  
// console (available only in dev profile) 
.​
antMatchers​
("
​/mng3/**"​
, ​
"/v2/api-docs/**"​
,  
"/h2-console/**"​
).​
permitAll​
() 

// require secure access to the API 
.​
antMatchers​
("
​/api/rentals/**"​).​
authenticated​
(); 
/
​/ required to get H2 consoles to work 
h
​ttp​
.h
​eaders​
().​
frameOptions​
().​
disable​
(); 
}   

@Override 

public​v
​oid​c
​onfigure​
(​ResourceServerSecurityConfigurer​r
​esources​
)  
throws​E
​xception​{ 
/
​/ provide the resource id 
r
​esources​
.r
​esourceId​
(t​his​
.r​esourceId​
); 

}

JPA Data Converters


JPA data converters are classes that help convert data to and from the relational database and
Java. At the time of this writing Hibernate did not support the Java 8 introduced LocalDateTime
type which we use for some of our entities so we defined one such converter class in package
course.a1.rentals.repository.converters​.
LocalDateTimeAttributeConverter converts to and from java.sql.Timestamp and
java.time.LocalDateTime.

© coaxial.ro 2018​ 69
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

package​​
course.a1.rentals.repository.converters​

 
import​​
java.sql.Timestamp​

import​​
java.time.LocalDateTime​

 
import​​
javax.persistence.AttributeConverter​

import​​
javax.persistence.Converter​

 
/** 
* JPA support for new date/time API in Java8 
*/ 
// This JPA Converter annotation marks this class as a converter.  
// The autoPlay set to true ensures that the persistence provider  
// applies this converter for all entities and all fields  
// of the provided types. 
@Converter​
(a​utoApply ​
=​t
​rue) 
public​​
class​L
​ocalDateTimeAttributeConverter​i
​mplements 
AttributeConverter​
<​LocalDateTime​
,​Timestamp​
>​{
​ 
 
@Override 

public​Timestamp c
​onvertToDatabaseColumn​
(L
​ocalDateTime locDateTime​
)​​

  return​(
​​locDateTime =
​=​n
​ull​?
​​​
null​​
:​Timestamp​
.v​alueOf​
(l
​ocDateTime​
)); 


 
@Override 

public​LocalDateTime ​
convertToEntityAttribute​
(T
​imestamp sqlTimestamp​
)​{
​ 
  return​(
​​sqlTimestamp =
​=​​
null​?
​​n
​ull​:
​​sqlTimestamp​
.​toLocalDateTime​
()); 


}

Application Initialization Classes


Our application has a number of application initialization classes (most of them are in package
course.a1.rentals) ​- these are classes marked with Spring’s ​@Configuration​ annotation and
they are used to create important object instances(beans) that are needed to run the
application.

© coaxial.ro 2018​ 70
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

● RentalApplication​ - responsible for starting up the application and creating other beans
required by the application
● JacksonConfiguration​ - used for customizing our JSON serialization mechanism
● SwaggerConfiguration​ - used for setting up the beans responsible for generating the
documentation for our REST API
● DevConfiguration​ - used for setting up the database during development and for
integration testing
● SecurityConfiguration​ - used for setting up Spring security. This class is in package
course.a1.rentals.security

It is helpful, in general, to use a separate configuration class for each type of functionality being
configured, as opposed to using a monolithic configuration class.
That allows us to use a subset of these configuration classes for different contexts (e.g. during
development or when running different types of tests)

RentalApplication
Our application startup class is ​RentalApplication.java​.

A simple Java class with a couple of annotations is all it takes to bootstrap our application.
Because we have a dependency in our pom.xml file on ​spring-boot-starter-web ​the application
starts an embedded web server. The application is packaged in a self sufficient jar file so the
application can be run as a regular java application (​java -jar
rentals-0.0.1-SNAPSHOT.jar​). During development the application is started using
maven (​./mvnw spring-boot:run -Pdev​), specifying the ​dev​ Maven profile.

The following annotations are used for this class:

@SpringBootApplication​ - this Spring annotation marks this class as the entry point for
our application
@EnableCaching​ - Spring annotation - it enables annotation based caching (it detects
the use of @Cacheable annotations)
@EnableGlobalMethodSecurity(prePostEnabled = true)​ - Spring annotation - it allows us to
specify method level security annotation

@SpringBootApplication 
@EnableCaching 
@EnableGlobalMethodSecurity​
(p
​rePostEnabled​= t
​rue​

© coaxial.ro 2018​ 71
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

public​​
class​R
​entalsApplication​{ 
 
// Main method that starts up the application... 

public​s
​tatic​v
​oid​m
​ain​
(S​tring​
[] ​
args​
) { 
// ...using the Spring provided utility method 
S
​pringApplication​
.r​un​
(​RentalsApplication​
.c
​lass​
, ​
args​
); 

   
// Class used for encoding user passwords 

@Bean 

public​P
​asswordEncoder​p
​asswordEncoder​
() { 
  ​
return​n
​ew​​
BCryptPasswordEncoder​
(); 

This class also creates the bean used for encoding user passwords in the application.

JacksonConfiguration
This class is used for customizing our JSON serialization mechanism provided by the Jackson
library. Some Jackson configuration properties can be tweaked using the standard Spring Boot
mechanism for third party libraries - using ​spring.jackson.*​ ​
properties defined in the
application*.properties​ file. However not all Jackson configuration parameters are available
via this mechanism - for maximum flexibility we need create a customized
com.fasterxml.jackson.databind.ObjectMapper​ bean - and this bean will be used by Spring
for JSON serialization operations. 

// Configuration class used to customize Jackson - the JSON serialization 


// library - the class configures behavior beyond of what's possible with 
// the Spring Boot Jackson properties mechanism 
@Configuration 
public​​
class​J
​acksonConfiguration​{ 

@Bean 

public​O
​bjectMapper​o
​bjectMapper​
() { 
O
​bjectMapper​m
​apper​= n
​ew​O
​bjectMapper​
(); 
/
​/ ignore unmapped properties in JSON documents when restoring objects 
m
​apper​
.​configure​
(D
​eserializationFeature​
.F​AIL_ON_UNKNOWN_PROPERTIES​
, f
​alse​
); 

© coaxial.ro 2018​ 72
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

/
​/ make Jackson aware of new Java time classes (e.g. LocalDateTime) 
m
​apper​
.​registerModule​
(n
​ew​J
​avaTimeModule​
()); 
/
​/ make Jackson aware of Hibernate proxies - we want lazy initialization 
/
​/ proxies loaded before the object is serialized 
m
​apper​
.​registerModule​
(n
​ew​H
​ibernate5Module​
().​
configure​

  H
​ibernate5Module​
.​Feature​
.F
​ORCE_LAZY_LOADING​
, ​
true​
)); 
/
​/ represent dates in ISO8601 format (e.g. 2018-07-02T10:49:39.884398) 
m
​apper​
.​setDateFormat​
(n
​ew​S
​tdDateFormat​
()); 
r
​eturn​​
mapper​


}

SwaggerConfiguration
This class is used for setting up the bean responsible for generating our API documentation.
The class is also marked with the ​@Profile​ annotation to only be active during the ​dev​ and
prod​ profiles. That means that the Swagger UI is not available for instance for integration tests.

Spring’s ​@Profile​ annotation can be used on any ​@Configuration​ class to specify the profiles
for which the configuration class is loaded - that makes it easy to use slightly different versions
of the application for each profile as needed.

// Spring annotation - marks this class as a bean generator 


@Configuration 
// Springfox annotation - enables Swagger2 support (to generate Rest API ocumentation) 
@EnableSwagger2 
// we only need API documentation for dev and prod (not test or itest) 
@Profile​
({​
"dev"​
, "
​prod"​
}) 
public​​
class​S
​waggerConfiguration​{ 

// This is required to generate Swagger REST API documentation 

@Bean 

public​D
​ocket​a
​piDocs​
() { 
r
​eturn​​
new​D
​ocket​
(D​ocumentationType​
.S
​WAGGER_2​
)  
.​
select​
().​
apis​
(R​equestHandlerSelectors​
.a​ny​
())   
.​
paths​
(P​athSelectors​
.​any​
()).​
build​
();  

© coaxial.ro 2018​ 73
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

DevConfiguration
This class is used for setting up the data to be used during development. The same data is used
in our case for running the integration tests.

This class creates a ​CommandLineRunner​ bean - Spring Boot runs any


CommandLineRunner​ instances during application startup which allows us to prepare the
database before the application is used during development and integration test runs.

// Class used during development and integration testing to setup 


// application state 
@Configuration 
// This configuration class is only available for development and 
// integration testing 
@Profile​
({​
"itest"​
,"
​dev"​
}) 
public​​
class​D
​evConfiguration​{   
@
​Bean 
p
​ublic​C
​ommandLineRunner​​
loadData​

A
​ssetRepository​a
​ssetRepo​

A
​ssetItemRepository​a
​ssetItemRepo​

A
​ssetPriceRepository​a
​ssetPriceRepo​

R
​oleRepository​r
​oleRepo​

U
​serRepository​u
​serRepo​

U
​serService​​
userService​

R
​entalService​r
​entalService​

A
​uthenticationManager​a
​uthManager​
) { 
   
/
​/ return the instance of CommandLineRunner to be run on application startup 
r
​eturn​(​
args​
) -> { 
/
​/ add roles 
R
​ole​a
​dminRole​= r
​oleRepo​
.s​ave​
(n​ew​R
​ole​
(R​ole​
.N​ame​
.​ADMIN​
)); 
R
​ole​c
​lerkRole​= r
​oleRepo​
.s​ave​
(n​ew​R
​ole​
(R​ole​
.N​ame​
.​CLERK​
)); 
R
​ole​c
​lientRole​= ​
roleRepo​
.s
​ave​
(n​ew​R
​ole​
(R​ole​
.N​ame​
.C​LIENT​
)); 
   
/
​/ add users 

© coaxial.ro 2018​ 74
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

@
​SuppressWarnings​
(​"unused"​

U
​ser​a
​dminUser​= u
​serService​
.a​ddUser​

new​ U
​ser​
("​admin@rentals.io"​
, "
​pass"​


adminRole​
, ​
clerkRole​
, ​
clientRole​
)); 
U
​ser​c
​lerkUser​= u
​serService​
.a​ddUser​

new​U
​ser​
("​clerk@rentals.io"​
, ​
"pass"​

c
​lerkRole​
)); 
U
​ser​c
​lientUser​= ​
userService​
.a
​ddUser​

new​U
​ser​
("​client@rentals.io"​
, "
​pass"​

c
​lientRole​
)); 
   
/
​/ add application configuration data - asset prices 
a
​ssetPriceRepo​
.s​ave​
(n​ew​A
​ssetPrice​
(A​sset​
.T​ype​
.N​EW​

BigDecimal​
.v​alueOf​
(​10​
), 0
​​)); 
a
​ssetPriceRepo​
.s​ave​
(n​ew​A
​ssetPrice​
(A​sset​
.T​ype​
.R​EGULAR​

BigDecimal​
.v​alueOf​
(​5)
​, 3
​)
​); 
a
​ssetPriceRepo​
.s​ave​
(n​ew​A
​ssetPrice​
(A​sset​
.T​ype​
.O​LD​

BigDecimal​
.v​alueOf​
(​5)
​, 5
​)
​); 
   
/
​/ add a few assets to the database (films in this case) 
A
​sset​m
​atrix11​= a
​ssetRepo​
.s​ave​
(n​ew​A
​sset​
("​Matrix 11"​
, ​
Asset​
.T​ype​
.​NEW​
)); 
A
​sset​s
​piderMan​= ​
assetRepo​
.​save​
(n
​ew​A
​sset​
("​Spider Man"​

Asset​
.​
Type​
.R​EGULAR​
)); 
A
​sset​s
​piderMan2​= ​
assetRepo​
.s​ave​
(​new​A
​sset​
(​"Spider Man 2"​

Asset​
.​
Type​
.R​EGULAR​
)); 
A
​sset​o
​utOfAfrica​= ​
assetRepo​
.s​ave​
(n​ew​A
​sset​
(​"Out of Africa"​

Asset​
.​
Type​
.O​LD​
)); 
...

SecurityConfiguration
This class configures Spring Security and links it with OAuth2. See the ​SecurityConfiguration
section in Security for more details.

Application Configuration
The application configuration refers to the set of parameters that drive our application from a
business perspective.
© coaxial.ro 2018​ 75
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

The infrastructure configuration is handled by Spring using the ​application*.properties​ files


(see ​Spring Boot Configuration File​ section).

Spring provides facilities to manage the business level application configuration. We create a
class ​AppConfiguration​ in package ​course.a1.rentals.config​ and annotate it with Spring
annotations ​@Component,​ ​@ConfigurationProperties​ and ​@PropertySource​.

// Spring annotation - make this class a component so that it can be used 


// with auto-wiring 
@Component 
// Spring annotation that marks this class as a provider 
// of externalized properties (e.g. properties read from a text file) 
@ConfigurationProperties​
(​prefix​= "
​rentals"​

// Spring annotation that tells this class to read the properties from 
// the specified text file 
@PropertySource​
("​classpath:application-configuration.properties"​

// Spring annotation to instruct that these object's properties are to be 
// validated using JSR-380 annotations (see below) 
@Validated 
public​​
class​A
​ppConfiguration​{ 

// these are are a set of validation annotations defined in Java Bean  
// Validation API (JSR-380). These annotations are used by Spring Boot to   
// validate objects. 

// the value here should be between 0-10 

@Min​
(​0)
​ @
​Max​
(1​0​


private​i
​nt​u
​serBonusPointsForAnyRentals​


// the value here should be between 0-20 

@Min​
(​0)
​ @
​Max​
(2​0​


private​i
​nt​u
​serBonusPointsForNewRentals​


@Size​
(m​in​
=​3,
​ m
​ax​
=3​)
​ 

private​S
​tring​​
currency​

 

public​i
​nt​g
​etUserBonusPointsForAnyRentals​
() { 
r
​eturn​u
​serBonusPointsForAnyRentals​



public​i
​nt​g
​etUserBonusPointsForNewRentals​
() { 
r
​eturn​u
​serBonusPointsForNewRentals​



public​S
​tring​g
​etCurrency​
() { 

© coaxial.ro 2018​ 76
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

r
​eturn​c
​urrency​


 

// note that setters are required to allow Spring to set the values based 

// on the specified source (application-configuration.properties in our  
// case) 
   

public​v
​oid​s
​etUserBonusPointsForAnyRentals​
(i​nt​u
​serBonusPointsForAnyRentals​
) { 
t
​his​
.u
​serBonusPointsForAnyRentals​= u
​serBonusPointsForAnyRentals​



public​v
​oid​s
​etUserBonusPointsForNewRentals​
(i​nt​u
​serBonusPointsForNewRentals​
) { 
t
​his​
.u
​serBonusPointsForNewRentals​= u
​serBonusPointsForNewRentals​


public​v
​oid​s
​etCurrency​
(S
​tring​​
curr​
) { 
t
​his​
.c
​urrency​= ​
curr​


@Componen​t makes this class usable via Spring’s auto-wiring mechanism

This is how the configuration class is used in ​RentalServiceImpl​ class:

@Service 
@Transactional 
public​​
class​R
​entalServiceImpl​i
​mplements​R
​entalService​{ 
... code excluded for readability ... 
​​
@Autowired 

private​A
​ppConfiguration​a
​ppConfig​

... code excluded for readability ...

if​
(​
rentedFilmType​== ​
Asset​
.T​ype​
.N​EW​
) { 
b
​onusPoints​+= ​
this​
.a​ppConfig​
.g​etUserBonusPointsForNewRentals​
(); 
} e
​lse​{ 
b
​onusPoints​+= ​
this​
.a​ppConfig​
.g​etUserBonusPointsForAnyRentals​
(); 
}

© coaxial.ro 2018​ 77
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

@ConfigurationProperties​ marks this class as a source of application configuration values


and ​@PropertySource​ specifies that these application properties are to be read from a file on
the classpath named ​application-configuration.properties​. We create this file in
src/main/resources​ folder in our project.

Our configuration file ​applicatio-configuration.properties​ contains the following properties:.

# number of bonus points received by a user for any rental 


rentals​
.​
userBonusPointsForAnyRentals​
=​

# for new rental items users earn 2 bonus points 
rentals​
.​
userBonusPointsForNewRentals​
=​

# ISO currency code used for monetary values in the application 
rentals​
.​
currency​
=USD 

Note that we used the optional ​prefix​ attribute on the ​@ConfigurationProperties​ annotation so
that our properties names can start with the ​rentals​ prefix in the
application-configuration.properties​ file. That allows you to define a clear naming convention
for your properties which is especially useful when you have multiple application configuration
classes (e.g. one per application module - for larger applications).

JPA Entities
The Java classes for our data model are build using standard/lightweight Java classes marked
with annotations defined in Java Persistence API (Keep this link handy for reference:
https://docs.oracle.com/javaee/7/api/toc.htm​).

A couple of java classes for entities (​Asset.java​, ​AssetItem.java​) are heavily commented to
explain all the commonly used annotations used. Other entities have comments like this for
certain sections not covered by these two classes.

© coaxial.ro 2018​ 78
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Handling correctly entity associations in JPA is not an easy skill to master. When dealing with
entity associations I prefer to keep things as simple as possible by having the associations
handled only by the child entity. For instance the ​Asset​ entity does not keep a list of ​AssetItem
instances and it does not list this association using a ​@OneToMany​ JPA annotation. I prefer to
have the child entity declare the association with a ​@ManyToOne​ annotation.

That means that the persistence of child entities (​AssetItem​) needs to be handled explicitly.
This approach is fine for most cases and it’s the preferred option for handling associations
where there can be a very large number of child entities for a given parent.

Let’s have a closer look at just two of the entities in our model ​Asset​ and A
​ ssetItem​.

Asset.java

// This is a JPA annotation that marks this class as representing a persistent  


// object 
@Entity 
// This is an annotation from our JSON conversion library (Jackson) that   
// tells the converter to only produce JSON output for object  
// variables/fields that are not null 
@JsonInclude​
(I
​nclude​
.N​ON_NULL​

public​​
class​A
​sset​{ 

// Enumeration with possible values for the "type" 

// field in our data model 

public​e
​num​T
​ype​{ 
N
​EW​
, R
​EGULAR​
, O
​LD 

   

// JPA annotation that marks this field as the primary key 

// for this entity 

@Id 

// JPA annotation that describe how this identity key is to be 

// generated - we are using the default generation type which lets the 

// persistence provider (Hibernate) to select the beset mechanism based 

// on the type of database used (as specified for the  
// spring.jpa.database-platform property in application.properties file).   
// Since we are using MySQL the selected mechanism will be based on  

© coaxial.ro 2018​ 79
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

// sequences. 

@GeneratedValue 

private​I
​nteger​i
​d​

 

// JPA annotation that provides information about this column; in this  
// case we want a maximum length of 100 characters and we do not allow  
// null values 

@Column​
(l
​ength​
=​100​
, ​
nullable​= ​
false​

   

// These are are a set of validation annotations defined in Java Bean  
// Validation API (JSR-380). These annotations are used by Spring Boot to  
// validate objects 
  

// The size of this string field is between 1 and 100... 

@Size​
(m​in​
=​1,
​ m
​ax​
=1​00​


// ...and it cannot be null 

@NotNull 

private​S
​tring​​
name​

   

// JPA annotation that marks this field as an enumeration; the  
// persistence engine will store the values for this field as the string   
// values (that is what EnumType.STRING does) of the underlying  
// enumeration(Type in this case) 

@Enumerated​
(E​numType​
.S​TRING​


// JPA annotation to mark this field as non null 

@Column​
(n
​ullable​= f
​alse​


// Java Bean Validation API annotation to mark this field as non null 

@NotNull 

private​T
​ype​t
​ype​

 

// An empty constructor is required by the persistence engine 

// (Hibernate, the JPA provider used by Spring in this project) 

public​A
​sset​
() { 
s
​uper​
(); 


public​A
​sset​
(S​tring​​
name​
, ​
Type​​
type​
) { 
s
​uper​
(); 
t
​his​
.n
​ame​= ​
name​

© coaxial.ro 2018​ 80
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

t
​his​
.t
​ype​= ​
type​



public​A
​sset​
(I​nteger​i
​d​
) { 
s
​uper​
(); 
t
​his​
.i
​d​= i
​d​


... getters and setters excluded for brevity ... 

Associations

As mentioned in the ​earlier paragraph​ on handling association you notice that there’s no
@OneToMany​ association defined in this parent entity.

Primary Key

As mentioned in the comment for the ​@GeneratedValue​ annotation we are using the default
mechanism for generating the primary keys which in our case (Hibernate + MySQL) results in a
sequence being used.
When you start the application (​./mvnw spring-boot:run​) you can see in the logs the
following fragment with DDL for creating the ​hibernate_sequence​ table which will be used by
Hibernate for generating unique values for the primary keys.

​ drop table if exists `asset`


Hibernate:

​ drop table if exists `hibernate_sequence`


Hibernate:

​...other DDL

​create table `hibernate_sequence` (


`next_val` bigint
) engine=MyISAM

© coaxial.ro 2018​ 81
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Validation
For validation of entity fields we use two type of annotations that handle different aspects: JPA
annotations that provide constraints for data persistence (e.g. ​@Column​(l​ength​=​100​,​nullable ​= 
false)​ and a set of validation rules that apply at various application layers in the application.
) ​
For the latter see the ​Validation​ section.

Note that not using a validation group for the @NotNull annotation we specify that this field
should be not null regardless of the validation group provided in the @Validated annotation (i.e.
in all circumstances).

AssetItem.java 

// JPA annotation to mark this class an Entity 


@Entity 
// JSON conversion library annotation to ensure output is produced only 
// for non null fields 
@JsonInclude​
(I
​nclude​
.N​ON_NULL​

public​​
class​A
​ssetItem​{ 

// possible states for an asset item 

public​e
​num​S
​tate​{ 
P
​ENDING​
, R
​ENTED​
, ​
DAMAGED​
, A
​VAILABLE​
, L
​OST 


// JPA annotation to mark this field as a primary key 

@Id 

// JPA annotation that describe how this identity key is to be 

// generated 

@GeneratedValue 

private​I
​nteger​i
​d​

 

// JPA annotation that marks this field as an enumeration; the  
// persistence engine will store the values for this field as the string   
// values (that is what EnumType.STRING does) of the underlying  
// enumeration(Type in this case) 

@Enumerated​
(E​numType​
.S​TRING​


// JPA annotation that provides information about this column; in this  
// case the value for this database column cannot be null 

© coaxial.ro 2018​ 82
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


@Column​
(n
​ullable​= f
​alse​


// Java Bean Validation API annotation to mark this field as non null 

// for the persistence layer - what this means is that when an instance  
// of this class is validated for the Persistence group an error will be  
// thrown if this value is null 

@NotNull​
(g
​roups​
={​
Persistence​
.c
​lass​
}) 

private​S
​tate​s
​tate​

   

// JPA annotation mapping timeAdded Java field to time_added database  
// column and marking this column as non null 

@Column​
(n
​ame​
="​time_added"​
, ​
nullable​
=​false​


// Java Bean Validation API annotation to mark this field as non null 

// for the persistence layer - what this means is that when an instance  
// of this class is validated for the Persistence group an error will be   
// thrown if this value is null 

@NotNull​
(g
​roups​
={​
Persistence​
.c
​lass​
}) 

private​L
​ocalDateTime​t
​imeAdded​

 

// JPA annotation that establishes a relationship between this entity and 

// the Asset entity. In this case there are many AssetItem instances for  
// every Asset 

// FetchType.LAZY instructs the persistence provider to load the Asset  
// for an AssetItem lazily 

@ManyToOne​
(o​ptional​= ​
false​
, f
​etch​= ​
FetchType​
.​LAZY​


// JPA annotation that specify the column in the AssetItem table to be   
// used for this association 

@JoinColumn​
(n​ame​= "
​asset_id"​


// Java Bean Validation API annotation to mark this field as non null 

@NotNull 

private​A
​sset​a
​sset​

   

/** 
  * Keep track of number of times each item was rented. 
  * This allows for optimal wear and tear of stocked items. 
  */ 

@Column​
(n
​ame​
="​rental_count"​
, n
​ullable​
=f​alse​


@NotNull 

private​L
​ong​r
​entalCount​

© coaxial.ro 2018​ 83
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

   
// An empty constructor is required by the persistence engine 

// (Hibernate, the JPA provider used by Spring in this project) 

public​A
​ssetItem​
() { 
s
​uper​
(); 


public​A
​ssetItem​
(i​nt​i
​d​
) { 
s
​uper​
(); 
t
​his​
.i
​d​= i
​d​



public​A
​ssetItem​
(A​sset​a
​sset​
) { 
s
​uper​
(); 
t
​his​
.a
​sset​= ​
asset​



public​A
​ssetItem​
(A​sset​a
​sset​
, A
​ssetItem​
.S​tate​s
​tate​
) { 
s
​uper​
(); 
t
​his​
.a
​sset​= ​
asset​

t
​his​
.s
​tate​= ​
state​



// JPA annotation - the methods with this annotation are invoked 

// just before the instance of this entity is persisted 

// In this case we want to set some default values for fields when 

// the instance is persisted 

@PrePersist 

protected​​
void​​
onCreate​
() { 
i
​f​
(t​his​
.s​tate​== ​
null​
) { 
t
​his​
.s​tate​= ​
State​
.A​VAILABLE​


i
​f​
(t​his​
.t​imeAdded​== n
​ull​
) { 
t
​his​
.t​imeAdded​= L
​ocalDateTime​
.n​ow​
(); 

i
​f​
(t​his​
.r​entalCount​== ​
null​
) { 
t
​his​
.r​entalCount​= ​
0L​


}   
... getters and setters excluded for brevity ... 

© coaxial.ro 2018​ 84
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Associations
The entity association between ​AssetItem​ and ​Asset​ is maintain by ​AssetItem​ - which is the
child entity.
The association is represented by the ​asset​ field and the ​@ManyToOne​ JPA annotation. This
annotation states that:

● the field is not optional (so every ​AssetItem​ has an ​Asset​ parent entity associated with
it)
● the fetch strategy is LAZY, which means that the Asset instance is not eagerly read from
the database at the same time as the ​AssetItem​ instance
● the column ​asset_id​ in the ​AssetItem​ table maintains the association between these
two entities

Validation
In this entity class we have two fields that must be not null when validated against the
Persistence​ validation group. While we do not explicitly validate instances for this validation
group, specifying it in the validation annotation allows us to ignore this constraint when
validating against the ​Rest​ validation group. That means that when ​AssetItem​ instances are
validated in the Rest controller using ​@Validated(Rest.class) ​the objects will pass validation as
we expect these fields to be empty at that point in time.

These three fields that cannot be null (​state​, ​rentalCount​ and ​timeAdded​) are provided the
default values in the ​AssetItem​ entity just before the instance is persisted to the database. We
do this by using the JPA ​@PrePersisted​ annotation which is invoked when an entity is
inserted/persisted into the database.

DTO (Data Transfer Objects)


DTOs are classes of objects that are used to transfer information between application layers.
They can be classes created to aggregate response data (for efficient remote call invocation), to
filter/hide fields in the response data (ex. to remove a ​password​ field in a User entity). In our
project any other class that is needed to exchange data between application layers and is not
part of the application’s data model (entities) is considered a DTO.
We create our DTOs classes in the ​course.a1.rentals.dto​ package.

While creating DTO objects just to aggregate request parameters might not be a good idea in
general, when using Spring Boot Rest controllers it makes life easier so we are doing this in our
project.

© coaxial.ro 2018​ 85
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Examples of DTO objects:

AddRentalItem​ - request parameter aggregator to hold all parameters required for adding a
rental item
AssetItemInventory​ - holds calculated data on an asset item inventory
AssetItemSearchFilter​ - defines a filter used for searching asset items
RentalCost​ - holds calculated rental cost details

Repositories
Reading and writing data from the database is implemented in our project using Spring Data
JPA. This project removes the need for boilerplate code and provides general facilities needed
when working with data (pagination, auditing, type safe queries, validation of queries, etc).

This section in the Maven’s project file lists Spring Data JPA as a dependency for our project:

​<!-- We use Spring Data JPA repositories --> 


<
​​dependency​

<
​g​roupId​
>o​rg.springframework.boot​
</​
groupId​

<
​a​rtifactId​
>s​pring-boot-starter-data-jpa​
</​
artifactId​

<
​/​
dependency​
>

Repositories are declared as interfaces annotated with ​@Repository​ and are located in the
course.a1.rentals.repository​ package. They also extend Spring’s ​JpaRepository​ interface.

Without any of your own method declarations it provides out of the box the ability to do CRUD
(​C​reate, ​R​ead, ​U​pdate, ​D​elete) operations, it has support for pagination and sorting and for
searching for entities using examples.

Spring Data allows you to define more complex read operations by using method names - it is
able to derive the actual query from the method name; this mechanism supports a large
combination of properties, logical operands and SQL clauses - Spring Data JPA documentation
is very useful and it provides plenty of examples (see, in particular, section ​2.4.2. Query
creation​):
https://docs.spring.io/spring-data/data-commons/docs/2.0.6.RELEASE/reference/html/​.

Let’s have a look at the code of one of our repository interfaces, ​AssetItemRepository​.

© coaxial.ro 2018​ 86
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

// Spring annotation that marks this interface as a repository 


@Repository 
// we need to extend the JpaRepository interface and provide the types for  
// the entity and its primary key as type arguments 
public​​
interface​A
​ssetItemRepository​e
​xtends​J
​paRepository​
<A​ssetItem​
, I
​nteger​
>​{ 
 

// we use the query facilities provided by Spring Data JPA - the method  
// name dictates what data we need (paginated results for all asset items for a 
given asset) 

Page​
<​AssetItem​
> ​
findByAsset​
(A
​sset​a
​sset​
, P
​ageable​p
​ageable​
); 
 

// find asset items by asset and a set of states 

Page​
<​AssetItem​
> ​
findByAssetAndStateIn​
(A​sset​a
​sset​
, L
​ist​
<S​tate​
> ​
states​
, P
​ageable 
pageable​
); 
 

// count all asset items for the given asset and in the given state 

Long​​
countByAssetAndState​
(​
Asset​a
​sset​
, A
​ssetItem​
.S​tate​s
​tate​
); 
   

// select the asset item for an asset that is in a rentable state and has  
// been rented the least amount of times and lock it this method is used 
// with a Pageable parameter so that we can only request the asset 

// item at the top (with the lowest rental count) 
   

// Spring Data JPA annotation to ensure the selected row is locked 

// for the duration of the current transaction 

@Lock​
(L​ockModeType​
.P
​ESSIMISTIC_WRITE​

// Spring Data JPA that specifies the JPQL (Java Persistence  
// Query Language) query to use 

@Query​
("​select ai from AssetItem ai where ai.asset = ?1 and ai.state = ?2 order 
by ai.rentalCount asc"​


List​
<​AssetItem​
> ​
findAllByAssetInStateAndLock​
(A
​sset​a
​sset​
, ​
AssetItem​
.S
​tate​​
state​

Pageable​​
pag​
); 
 
  // default method - we need it in order to process the return value  
// from #findAllByAssetInStateAndLock method 
  default​​
Optional​
<A
​ssetItem​
> f
​indOneByAssetInStateAndLock​
(​Asset​a
​sset​

AssetItem​
.S​tate​s
​tate​
) { 

© coaxial.ro 2018​ 87
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

  List​
<A​ssetItem​
> l
​st​= f
​indAllByAssetInStateAndLock​
(​asset​
, ​
state​

PageRequest​
.o
​f​
(0​,
​ 1
​​)); 
  if​
(​CollectionUtils​
.​
isEmpty​
(l
​st​
)) { 
  return​O
​ptional​
.e​mpty​
(); 
  } 
  return​O
​ptional​
.o​f​
(​lst​
.g
​et​
(0​)
​); 
  } 
}

Components
Components are classes marked with Spring’s ​@Component​ annotation and they are used
within a single application layer.
In our application we have a couple of components in the service layer:
● course.a1.rentals.component.AssetPriceComponentImpl​ - that is responsible for
providing asset prices. Because asset prices do not change often we also cache the
result of the ​getAssetPrice​ method in the ​assetPrice​ cache region. When a price
changes we can invoke the ​refreshAssetPrice​ method and that will refresh the cache
entry.
● course.a1.rentals.component.RentalsUtils​ - utilities used in the project

Below is the code for the ​AssetPriceComponentImpl​ component.

Note that rather than auto-wiring fields we define a constructor to initialize these objects. This
pure Java approach is recommended as it allows us to mark the fields ​final​ and it makes it
easier to provide instances of these classes for testing (there is no need to provide a Spring
configuration class to instantiate them).

Annotations used in ​AssetPriceComponentImpl​ class:

@Component​ - Spring annotation - a generic marker for Spring managed classes; it marks this
class as a candidate for auto-detection of managed classes
@Cacheable("assetPrice")​ - Spring annotation that causes the result of the annotated method
to be cached in a cache with name “assetPrice”
@CachePut("assetPrice") ​- Spring annotation that causes the respective cache entry to be
replaced by the result of the annotated method

© coaxial.ro 2018​ 88
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

// Spring annotation that marks this class as a candidate for 


// injection in Spring's auto-wiring mechanism 
@Component 
public​​
class​A
​ssetPriceComponentImpl​i
​mplements​A
​ssetPriceComponent​{ 

private​f
​inal​A
​ssetPriceRepository​a
​ssetPriceRepo​

   

// Spring uses this constructor to inject dependencies 

public​A
​ssetPriceComponentImpl​
(​AssetPriceRepository​​
assetPriceRepo​
) { 
s
​uper​
(); 
t
​his​
.a
​ssetPriceRepo​= ​
assetPriceRepo​


 

// Spring annotation - it caches the result of this method 

// in the cache with name "assetPrice" 

@Cacheable​
("​assetPrice"​


@Override 

public​A
​ssetPrice​g
​etAssetPrice​
(A​sset​
.T​ype​a
​ssetType​
) { 
r
​eturn​​
this​
.​assetPriceRepo​
.f
​indByAssetType​
(a​ssetType​
); 

 

// Spring annotation - it replaces the cache entry with key assetType 

// with the result of this method; used to refresh the assetPrice cache 

// when data in the database changes 

@CachePut​
(​"assetPrice"​


@Override 

public​A
​ssetPrice​r
​efreshAssetPrice​
(A
​sset​
.​Type​​
assetType​
) { 
r
​eturn​​
this​
.​assetPriceRepo​
.f
​indByAssetType​
(a​ssetType​
); 

Services
Services are classes handling the business/application logic. Services make use of Spring Boot
repository classes and component classes. These classes are invoked by the Rest controllers
via a clearly defined interface.

© coaxial.ro 2018​ 89
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

A service class is marked with Spring’s ​@Service​ annotation. In most cases service classes
operate in a transactional context so they are often marked with the ​@Transactional
annotation.

Services are defined for our project in package ​course.a1.rentals.service​.

Let’s look in more details at one of the service classes, ​AssetInventoryServiceImpl​. 


The class is marked with:

@Service​ - Spring annotation marks this class as a service. A service class holds
business logic and exposes an interface to other application layers
@Transactional - ​Spring annotation marks this class as providing methods that should
be executed in the context of a transaction (a transaction is started and provided to the
persistence provider every time a method of this class is executed; if an exception is
thrown the transaction will be rolled back)

Methods in this class are usually marked with annotations to override the transactional
characteristics (​@Transactional​) or to specify what roles are allowed to execute them
(​@PreAuthorize​).

​ ethod is annotated with:


The ​searchAssets​ m

@Transactional(readOnly = true) ​- used to override here the class level ​Transactional


annotation to instruct Spring to execute this method in a read only transaction. Read
only transactions can be used for processing that do not change the underlying
persistence store; optimizations can be made for read only transactions so use them
whenever possible.
@PreAuthorize("hasAuthority('ADMIN') or hasAuthority('CLERK')") ​- used to specify
security requirements for this method; only users with ADMIN and CLERK roles can
invoke it

// This Spring annotation marks this class as a service. A service class holds 
// business logic and exposes an interface to other application layers 
@Service 
// This Spring annotation marks this class as providing methods that should 
// be executed in the context of a transaction (a transaction is started and  
// provided to the persistence provider every time a method of this class is  
// executed; if an exception is thrown the transaction will be rolled back) 

© coaxial.ro 2018​ 90
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

@Transactional 
public​​
class​A
​ssetInventoryServiceImpl​i
​mplements​A
​ssetInventoryService​{ 

private​f
​inal​A
​ssetRepository​a
​ssetRepository​


private​f
​inal​A
​ssetItemRepository​a
​ssetItemRepository​

   

// Constructor used by Spring to wire dependent object instances 

public​A
​ssetInventoryServiceImpl​
(A​ssetRepository​a
​ssetRepository​

A
​ssetItemRepository​a
​ssetItemRepository​
) { 
s
​uper​
(); 
t
​his​
.a
​ssetRepository​= ​
assetRepository​

t
​his​
.a
​ssetItemRepository​= ​
assetItemRepository​


 

@Override 

// We override here the default Transactional annotation (at the class  
// level) to instruct Spring to execute this method in a read only  
// transaction. Read only transactions can be used for processing that do   
// not change the underlying persistence store; optimizations 

// can be made for read only transactions so use them whenever possible. 

@Transactional​
(​readOnly​= ​
true​


// Only users with ADMIN and CLERK roles have access to this method 

@PreAuthorize​
("
​hasAuthority('ADMIN') or hasAuthority('CLERK') "​


public​P
​age​
<A​sset​
> s
​earchAssets​
(A​ssetSearchFilter​f
​ilter​
, ​
Pageable​p
​ageable​
) { 
... code excluded for brevity ... 

 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN')"​


public​A
​sset​a
​ddAsset​
(A​sset​f
​ilm​
) {   
r
​eturn​a
​ssetRepository​
.s​ave​
(f​ilm​
); 

 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN') or hasAuthority('CLERK') "​


public​A
​sset​u
​pdateAsset​
(A
​sset​​
film​
) { 
r
​eturn​a
​ssetRepository​
.s​ave​
(f​ilm​
); 

 

© coaxial.ro 2018​ 91
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN')"​


public​v
​oid​r
​emoveAsset​
(A
​sset​f
​ilm​
) { 
t
​his​
.a
​ssetRepository​
.d​elete​
(f​ilm​
); 

 

@Override 

@Transactional​
(​readOnly​= ​
true​


@PreAuthorize​
("
​hasAuthority('ADMIN') or hasAuthority('CLERK') "​


public​P
​age​
<A​ssetItem​
> s
​earchAssetItems​
(A
​ssetItemSearchFilter​f
​ilter​
, ​
Pageable 
pageable​
) { 
... code excluded for brevity ... 

 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN')"​


public​A
​ssetItem​a
​ddAssetItem​
(A
​ssetItem​a
​ssetItem​
) { 
r
​eturn​a
​ssetItemRepository​
.​save​
(​assetItem​
); 

 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN')"​


public​A
​ssetItem​u
​pdateAssetItem​
(A​ssetItem​a
​ssetItem​
) { 
r
​eturn​a
​ssetItemRepository​
.​save​
(​assetItem​
); 

 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN')"​


public​v
​oid​r
​emoveAssetItem​
(A​ssetItem​a
​ssetItem​
) { 
a
​ssetItemRepository​
.d
​elete​
(​assetItem​
); 

 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN') or hasAuthority('CLERK') "​


public​A
​sset​g
​etAsset​
(A​sset​a
​sset​
) { 
A
​sset​​
ret​= ​
assetRepository​
.g​etOne​
(a
​sset​
.g​etId​
()); 
H
​ibernate​
.i
​nitialize​
(r​et​
); 
r
​eturn​r
​et​

© coaxial.ro 2018​ 92
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN') or hasAuthority('CLERK') "​


public​A
​ssetItem​g
​etAssetItem​
(A
​ssetItem​a
​ssetItem​
) { 
A
​ssetItem​r
​et​= ​
assetItemRepository​
.g
​etOne​
(​assetItem​
.g
​etId​
()); 
r
​et​
.s
​etAsset​
(n​ew​A
​sset​
(r​et​
.​getAsset​
().​
getId​
())); 
H
​ibernate​
.i
​nitialize​
(r​et​
); 
r
​eturn​r
​et​


 

@Override 

@PreAuthorize​
("
​hasAuthority('ADMIN') or hasAuthority('CLERK') "​


public​A
​ssetItemInventory​​
calculateAssetItemInventory​
(A
​sset​a
​sset​
) {   
M
​ap​
<A
​ssetItem​
.S​tate​
, L
​ong​
> ​
inv​=  
new​H
​ashMap​
<A​ssetItem​
.​State​
, L
​ong​
>(); 
f
​or​
(A
​ssetItem​
.S​tate​s
​tate​: ​
AssetItem​
.​State​
.v​alues​
()) { 
i
​nv​
.p​ut​
(s​tate​
, a
​ssetItemRepository 
.​
countByAssetAndState​
(a​sset​
, s
​tate​
)); 

r
​eturn​n
​ew​​
AssetItemInventory​
(i
​nv​
); 

}

RESTful API Design


A RESTful API is an API designed observing the main principles of REST (Representational
state transfer).
REST is an architectural style that provide a set of rules for building web services, using textual
representation of resources, a uniform interface, stateless operations and self-describing
messages that allows clients to move through application states without prior knowledge of the
application.

© coaxial.ro 2018​ 93
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

REST Principles
The most important characteristics of a RESTful API are the following:

● Uniformity​ - all resources are interacted with in the same way (e.g using URLs to
identify resources and acting on them using HTTP verbs - mainly ​GET​, ​PUT​, ​POST​,
DELETE​)
● Lack of state​ - no state should be maintained between requests
● Cacheability​ - responses should be cacheable using the existing HTTP caching
infrastructure
● Layering​ - communication uses existing standards and messages are self-descriptive;
this allows any number of layers/intermediaries between the client and the server to
access and enhance the message exchange (e.g caching servers, security servers)

Another important aspect of REST, related to Uniformity, is ​Hypermedia As The Engine Of


Application State​ (​HATEOAS​); RESTful applications need to provide a client with enough
information to allow easy navigation/transition to other application states (other resources or
other operations on the current resource). That allows the use of generic user interfaces to
navigate any application that implements HATEOAS.
Depending on the usage pattern for the API you are developing this aspect is more or less
important.

For our own Rentals API we are not going to implement full HATEOAS - the API is built to be
used by a custom mobile or web application, in this case the HATEOAS principles are not an
absolute requirement.

Best Practices
In terms of implementing our RESTful API we are going to respect the following common best
practices:

● URLs identifying resources to act upon contain plural nouns - the nouns are names of
the resources and subresources (e.g.​ /api/rentals/assets/12/items/10​)
● Use kebab-case (dash separated) naming conventions for all the elements in our URL
path (e.g. ​it-looks-like-this)​
● The basic operation to be performed on the resource is transmitted via HTTP verbs
(​GET​, ​POST​, ​PUT​, ​DELETE​, etc). The usage of these HTTP verbs follow, in general,
these rules:
○ GET​ - used for reading resources
■ returns an HTTP status code of 200 most of the time (other status codes
are also acceptable, such as 204 (No Content))
© coaxial.ro 2018​ 94
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


must be idempotent (it leaves the system in the same state if invoked
multiple times)
○ POST​ - for creating a resource
■ returns an HTTP status code of 201 (Created) with the Location header
pointing to the newly created resource; if the server defers the creation of
the resource, the response should be 202 (Accepted)
■ when used for actions (e.g. search) a POST request can return a status
code of 200
○ PUT​ - for updating a resource
■ returns an HTTP status code of 204 (No Content) most of the time;
sometimes if the resource was updated in a significant way it can return a
200 status code with body
■ must be idempotent
○ DELETE​ - for deleting a resource
■ returns an HTTP status code of 204 (No Content) most of the time
■ must be idempotent
● Use ISO 8601 date times in our JSON documents

If you are developing a secured RESTful API (like Rentals API) you should also be familiar with
these two HTTP response status codes - and the difference between them:

● 401 (Unauthorized)​ - the client tried to operate on a protected resource without


providing the required authorization information (usually in the Authorization header) or
providing the wrong authorization information
● 403 (Forbidden)​ - the client tried to operated on a protected resource but the provided
authorization information is not sufficient to access this resource or perform this
operation

Searching and Pagination


Searching and pagination information is usually passed to the server as URL parameters - this
approach is in line with the REST principles of uniformity and cacheability.

An example of a URL used for searching would be:

GET
/api/rentals/users?search=%7B%22email%22%3A%22admin%40rentals.io%22%7
D%0A&page=2&size=5

The advantage of using a GET request is that the URL is bookmarkable and easily cacheable.

© coaxial.ro 2018​ 95
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Downsides are possible limits set in the server on the length of a URL and the relative difficulty
in working with URL parameters during development and testing.

A different approach, used in our Rentals API is to use POST requests, appending the search
token to the URL path identifying the resources to be searched, passing the search filter as a
JSON document in the body of the request and the pagination information as URL parameters.

POST /api/rentals/users/search?page=0&size=2
{
"email":"admin@rentals.io"
}

The downside of this approach is that the search queries are not bookmarkable and the
requests are more difficult to cache (since POST requests are not considered to be idempotent,
they are not automatically cached by all layers in the communication channel).

When designing your own API choose the option that makes most sense in your particular case.
In the case of our Rentals API, considering the client is a custom mobile or web application the
downsides of the POST approach are not very important.

Rentals API Design


Using the principles discussed in the earlier sections we produced the following API endpoints
for our Rentals API application:

● Users
○ GET /api/rentals/users/{id}​ - gets user with id {id}, returns 200 with body
○ POST /api/rentals/users​ - creates a new user, returns 201 Created
○ POST /api/rentals/users/search​ - searches for users, returns 200 with body
○ DELETE /api/rentals/users/{id}​ - deletes user with id {id}, returns 204 No
Content
○ PUT /api/rentals/users/{id}​ - updates user with id {id}, returns 204 No Content
○ GET /api/rentals/users/{id}/bonus-points​ - gets bonus points for user with id
{id}
○ GET /api/rentals/users/{id}/bonus-points-total​ - gets the total number of bonus
points for user with id {id}
● Assets
○ GET /api/rentals/assets/{id}​ - gets asset with id {id}, returns 200 with body
○ POST /api/rentals/assets​ - creates a new asset, returns 201 Created
○ POST /api/rentals/assets/search​ - searches assets, returns 200 with body
○ DELETE /api/rentals/assets/{id}​ - deletes asset with id {id}, returns 204 No
Content

© coaxial.ro 2018​ 96
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

○ PUT /api/rentals/assets/{id} ​- updates asset with id {id}, returns 204 No Content


● AssetItems - a sub-resource of Assets
○ GET /api/rentals/assets/{assetId}/items/{id}​ - gets the asset item with id {id} for
asset with id {assetId}, returns 200 with body
○ POST /api/rentals/assets/{assetId}/items​ - creates an asset item for asset with
id {assetId}, returns 201 Created
○ POST /api/rentals/assets/{assetId}/items/search​ - searches asset items for
asset with id {assetId}, returns 200 with body
○ PUT /api/rentals/assets/{assetId}/items/{id}​ - update the asset item with id {id},
returns 204 No Content
○ DELETE /api/rentals/assets/{assetId}/items/{id}​ - deletes the item with id {id},
returns 204 No Content
○ GET /api/rentals/assets/{assetId}/items/inventory​ - gets the item inventory
data for the asset with id {assetId}, returns 200 with body
● Rentals
○ GET /api/rentals/rentals/{id}​ - gets the rental with id {id}, returns 200 with body
○ POST /api/rentals/rentals​ - creates a new rental, returns 201 Created
○ DELETE /api/rentals/rentals/{id}​ - deletes the rental with id {id}, returns 204 No
Content
○ POST /api/rentals/rentals/{id}/activate​ - activates the rental with id {id}, returns
200 with body
○ POST /api/rentals/rentals/{id}/close​ - closes the rental with id {id}, returns 200
with body
○ POST /api/rentals/rentals/{id}/cost​ - gets the cost of the rental with id {id} at a
given date, returns 200 with body
○ POST /api/rentals/rentals/{id}/balance​ - gets the balance on the rental with id
{id} at a given date, returns 200 with body
● RentalItems - sub-resource of Rental
○ GET /api/rentals/rentals/{rentalId}/items/{id}​ - gets the rental item with id {id}
for rental with id {rentalId}
○ POST /api/rentals/rentals/{rentalId}/items​ - creates a new rental item, returns
201 Created
○ DELETE /api/rentals/rentals/{rentalId}/items/{id}​ - deletes the rental item with
id {id}, returns 204 No Content

As you might have noticed when we are dealing with sub-resources (many to one relationships
in database terms) we chose to build URLs for them to reflect the hierarchy (e.g
/api/rentals/rentals/12/items/10). An alternative approach would have been to treat them as
standalone resources (e.g. /api/rentals/rental-items/10).

© coaxial.ro 2018​ 97
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

Versioning
Versioning is the ability to support multiple versions of an API and it is a very important aspect
of any API design, including RESTful APIs.
There are a few ways to handle versioning for a RESTful API:

● Use a different URL path for different API versions (e.g. /api/​v1​/rentals/users/search)
● Specify the version information in the HTTP request header and route the request to the
correct API server based on this value
● Use DNS/a separate domain name for each version (e.g. ​v1​.rentals-api.io)

The last option (DNS based) seems to be the easiest, least obtrusive approach.

Rest Controllers
Rest controllers are classes responsible for implementing your API endpoints and exchanging
data between the client (JSON) and the business services (Java).

The only required annotation for a REST controller class is @RestController; other common
annotations are listed below:

@RestController​ - Spring annotation that marks this class as a REST controller; amongst other
things Spring knows to bind the return value from methods to the body of the HTTP response
@RequestMapping​ - Spring annotation used to provide information on how to handle HTTP
requests. We use it to set up the root URL path for all methods of this class (e.g.
/api/rentals/assets​ ) and to specify that our endpoints produce and consume JSON content
@Api​ - Swagger annotation; it marks all public methods of this class as being part of the REST
API. It’s used to provide information on the REST API documentation generator (Swagger)

All public methods in a REST controller that define an API endpoint should have the
@ApiOperation​ Swagger annotation to provide information for the documentation generator
about the respective endpoint.

@Api​


consumes​= ​
MediaType​
.A​PPLICATION_JSON_VALUE​


produces​= ​
MediaType​
.A​PPLICATION_JSON_VALUE​

@RestController 
@RequestMapping​

p
​ath​= ​
"/api/rentals/assets"​

© coaxial.ro 2018​ 98
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

c
​onsumes​= ​
MediaType​
.A
​PPLICATION_JSON_VALUE​

p
​roduces​= ​
MediaType​
.A
​PPLICATION_JSON_VALUE​

public​​
class​A
​ssetInventoryRestController​{ 
... 

Let’s look in more detail into each type of methods a REST controller usually needs to define.

POST Request to Create a Resource


A method defining an API endpoint for creating resources has has the following annotation
 
@RequestMapping​
(​
path​= ​
""​
, ​
method​= ​
RequestMethod​
.​
POST​

The Spring ​@RequestMapping​ annotation applied at method level allows the mapping of this
method to a POST HTTP method. The ​path​ attribute specifies the URL mapped to this method
relative to the value specified in the ​@RequestMapping​ annotation at the class level.
In this case it is empty since we are using the same URL path as the one specified at class level
("/api/rentals/assets").

This ​addAsset​ method has a parameter that takes an entity instance and is marked with two
annotations:

● @RequestBody​ - specifies that the Asset object should be read from the JSON
document in the body of the HTTP requests
● @Validated(Rest.class)​ - validates the Asset instance based on the rules defined in the
Rest validation group

For Rentals API we chose to return just the location of the newly created resource rather then
the resource itself. For this reason the return type of the method is ​ResponseEntity​
<​
Void​>​
.
The actual value returned is build using:

ResponseEntity​
<V
​oid​
> r
​et = ResponseEntity​
.​
created​
(​
location​
).​
build​
(); 
 
This will translate into an HTTP response with a status code of 201 Created, with no body and a
Location HTTP header containing the location of the newly created resource. The location is
calculated, relative to the current URL path using the following code:

URI​l
​ocation​= S
​ervletUriComponentsBuilder​
.​
fromCurrentRequest​
() 
.​
path​
(​
"/{id}"​

© coaxial.ro 2018​ 99
© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

  .​
buildAndExpand​
(​
addedAsset​
.​
getId​
()).​
toUri​
(); 


@ApiOperation​
(v
​alue​= ​
"Adds a new asset"​


@RequestMapping​
(p​ath​= "
​"​
, ​
method​= ​
RequestMethod​
.P
​OST​


public​R
​esponseEntity​
<V​oid​
> a
​ddAsset​

@
​Validated​
(R​est​
.c​lass​
) ​
@RequestBody​A
​sset​a
​sset​
) { 
A
​sset​​
addedAsset​= a
​ssetInventoryService​
.a
​ddAsset​
(a​sset​
);   
U
​RI​l
​ocation​= S
​ervletUriComponentsBuilder 
.​
fromCurrentRequest​
().​
path​
("​/{id}"​

.​
buildAndExpand​
(a​ddedAsset​
.g​etId​
()).​
toUri​
(); 
R
​esponseEntity​
<V
​oid​
> r
​et​= ​
ResponseEntity​
.c
​reated​
(l​ocation​
).​
build​
(); 
r
​eturn​r
​et​

}

This is how such a POST request looks like:

POST ​http://localhost:8080/api/rentals/rentals/25/items/
{
"timeExpectedReturn": "2018-07-06T16:50:23",
"asset": {
"id": 11
}
}

POST Requests for Search


These are requests that indicate that an action needs to be performed (e.g.
/api/rentals/users/search).
These requests pass the information required to perform the action in the request body and the
usually return data and an HTTP status code of 200 (for searches for instance).

A POST request for search is usually mapped to a method that uses Spring’s paging facilities -
it takes as a parameter a ​Pageable​ instance specifying the page to be returned and it returns a
Page​).

Spring handles the conversion of paging information which is passed as URL parameters (e.g.
page=0&size=5​) into a Pageable instance.

© coaxial.ro 2018​ 100


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

​/
​/ POST /api/rentals/assets/search 

@ApiOperation​
(v
​alue​= ​
"Searches assets and returns paginated results"​


@RequestMapping​
(p​ath​= "
​search"​
, m
​ethod​= ​
RequestMethod​
.P
​OST​


public​P
​age​
<A​sset​
> s
​earchAssets​

@
​RequestBody​A
​ssetSearchFilter​f
​ilter​
, ​
Pageable​p
​ageable​
) { 
P
​age​
<A
​sset​
> ​
page​= a
​ssetInventoryService​
.s
​earchAssets​
(​filter​
, p
​ageable​
); 
r
​eturn​p
​age​

}

This is how such a POST request looks like:

POST ​http://localhost:8080/api/rentals/assets/search?page=0&size=5
{

"types": [ "NEW" ],
"withItemsInState": [ "AVAILABLE" ]
}

POST Requests for Business Actions


A business action would be something like ​cancel​, ​activate​, ​close​, etc. These are POST
requests similar to those for searching but they usually return either a regular entity or DTO
object or nothing at all (204 No Content).

The example below shows the method that calculates the cost for a rental.
In this method we extract the ​id​ of the rental for which the cost must be calculated from the URL
using the ​path​ attribute of ​@RequestMapping​ and the ​@PathVariable​ annotation of the ​id
method parameter.

This method also takes an optional argument from the request body - the ​CalculateCost​ DTO
instance that contains information on how to calculate the rental’s cost.
The fact that the request body is optional is marked by the ​required​ attribute of the
@RequestBody annotation that is set to ​false​.


// POST /api/rentals/rentals/{id}/cost 

@RequestMapping​
(p​ath​= "
​{id}/cost"​
, ​
method​= R
​equestMethod​
.P​OST​


public​R
​entalCost​g
​etRentalCost​
(@​PathVariable​
("
​id"​
) ​
Integer​i
​d​

© coaxial.ro 2018​ 101


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

  @RequestBody​
(r
​equired​
=f​alse​
) C
​alculateCost​c
​c​
) { 
r
​eturn​t
​his​
.r​entalService​
.g
​etRentalCost​
(n​ew​R
​ental​
(i
​d​
), 
c
​c​== ​
null​? n
​ull​: ​
cc​
.g
​etDayOfCalculation​
()); 
}

DELETE Request to Delete a Resource


Delete requests are only used for deleting resources. They usually return 204 No Content and
read the id of the resource to be deleted from the URL path using the ​@PathVariable​ method
parameter annotation.
Sometimes the delete operation is logical - rather than a hard delete the resource is soft deleted
(e.g. cancelled) as in the the example below that cancels a rental. This is acceptable when the
resource cannot be hard deleted for business reasons.


// DELETE /api/rentals/rentals/{id} 

@RequestMapping​
(p​ath​= "
​{id}"​
, ​
method​= R
​equestMethod​
.D​ELETE​


public​R
​esponseEntity​
<V​oid​
> c
​ancelOrCloseRental​
(@​PathVariable​
("
​id"​

Integer​i
​d​
) { 
t
​his​
.r
​entalService​
.c​ancelOrCloseRental​
(n​ew​​
Rental​
(i
​d​
)); 
r
​eturn​R
​esponseEntity​
.​noContent​
().​
build​
();   

PUT Request to Update a Resource


PUT requests are used for updating resources. They usually return a 204 No Content HTTP
status code or, if the update was significant, it can return a 200 status code with the updated
resource as the body.


// PUT /api/rentals/assets/{assetId}/items/{id} 

@ApiOperation​
(v
​alue​= ​
"Updates the asset item with the given id"​


@RequestMapping​
(p
​ath​= "
​{assetId}/items/{id}"​
,  
method​= ​
RequestMethod​
.P
​UT​


public​R
​esponseEntity​
<V​oid​
> u
​pdateAssetItem​

© coaxial.ro 2018​ 102


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

@
​PathVariable​
("​assetId"​
) I
​nteger​a
​ssetId​

@
​PathVariable​
("​id"​
) I
​nteger​​
id​

@
​Validated​
(R​est​
.c​lass​
) ​
@RequestBody​A
​ssetItem​a
​ssetItem​
) { 
a
​ssetItem​
.s
​etId​
(​id​
); 
a
​ssetItem​
.s
​etAsset​
(n​ew​A
​sset​
(a​ssetId​
)); 
a
​ssetInventoryService​
.​updateAssetItem​
(​
assetItem​
); 
r
​eturn​R
​esponseEntity​
.​noContent​
().​
build​
(); 
}

GET Request to Get a Resource


GET requests are used to retrieve a resource; they can also be used for implementing
searching. It is important to obey the HTTP protocol and use GET requests ​only​ for idempotent
operations.


// GET /api/rentals/assets/{assetId}/items/{id}   

@ApiOperation​
(v
​alue​= ​
"Returns the asset item with the given id"​


@RequestMapping​
(p​ath​= "
​{assetId}/items/{id}"​
,  
method​= ​
RequestMethod​
.G
​ET​


public​A
​ssetItem​g
​etAssetItem​
(@
​PathVariable​
("​id"​
) I
​nteger​​
id​
) { 
r
​eturn​a
​ssetInventoryService​
.g
​etAssetItem​
(n
​ew​A
​ssetItem​
(i​d​
)); 
}

Testing
There are several levels of testing that are usually implemented for a web application; in this
section we’ll cover unit testing and integration testing.

As a strategy we run our unit tests and integration tests in dedicated profiles - that makes it
easier to understand the context under which they are run. The downside of this approach is it
makes it a bit more difficult to run the unit test as part of the maven ​repackage​ phase.

In order to be able to run the tests in their own profiles we need to configure the Surefire and
Failsafe Maven plugins and pass to the forked JVMs the name of the active Spring profile (see
pom.xml fragment below).

© coaxial.ro 2018​ 103


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

<!-- We need to customize the Surefire plugin - responsible for 


running unit tests; specify the Spring profile to be used when 
running the unit tests --> 
<​
plugin​


<​groupId​
>o​rg.apache.maven.plugins​
</​
groupId​


<​artifactId​
>​maven-surefire-plugin​
</​
artifactId​


<​configuration​


<a​rgLine​
>-
​Dspring.profiles.active=test​
</​
argLine​


<f​orkCount​
>1​<
​/​
forkCount​


<r​euseForks​
>t​rue​
</​
reuseForks​


</​
configuration​

</​
plugin​

   
<!-- We need to customize the Failsafe plugin - responsible for 
running integration tests; specify the Spring profile to be used when 
running the tests --> 
<​
plugin​


<​groupId​
>o​rg.apache.maven.plugins​
</​
groupId​


<​artifactId​
>​maven-failsafe-plugin​
</​
artifactId​


<​configuration​


<a​rgLine​
>-
​Dspring.profiles.active=itest​
</​
argLine​


<f​orkCount​
>1​<
​/​
forkCount​


<r​euseForks​
>t​rue​
</​
reuseForks​


</​
configuration​

</​
plugin​
>

This configuration allows us to run the unit tests as part of the build command:

./mvnw package

Because we are not using the actual Maven profile when running this command we also have to
include test dependencies (H2 database) in the main ​dependecies​ section of the Maven
pom.xml​ file.

<!-- Get H2 jars added to the classpath when running the tests --> 
<​
dependency​

© coaxial.ro 2018​ 104


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot


<​groupId​
>c​om.h2database​
</​
groupId​


<​artifactId​
>​h2​
</​
artifactId​


<​scope​
>​test​
</​
scope​

</​
dependency​
>

Unit Testing
Unit tests are targeted at very narrow areas of functionality, usually a method in a class. We
write unit tests to cover classes at every layer of the application. The more unit tests and bigger
the test coverage the better the application is protected against inadvertent errors introduced
during the course of development.

Because we are only interested in testing methods in isolation we are going to rely heavily on
mocking - all external interactions will be mocked. Spring also provides facilities for testing only
slices of your application - when test classes are annotated with specific annotations, Spring will
not load the entire application but only the minimum components required to test that particular
application slice.

The Spring annotations for slice testing used in this course are:

● @WebMvcTest​ - used for testing only the controller slice/layer


● @DataJpaTest ​- used for testing the repository slice/layer

We write all the unit testing code in this pacakge: ​course.a1.rentals.tests​ in the s
​ rc/test/java
folder. All resources required for running the tests are stored in ​src/test/resources​.
Note that the classpath used for testing includes first resources in ​src/test/resources​ and then
src/main/resources​ so we only need to provide for ​test​ only resources that are not available in
the ​main​ folder or that need to be overridden.

© coaxial.ro 2018​ 105


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

To have better control over the configuration used to run the unit test we create a ​test​ maven
and spring profile.

For Maven, we add the ​test​ profile to the pom.xml file as shown below.

We also create an ​application-test.properties​ file in ​src/test/resources​ that contains


application configuration data for this ​test​ profile.

To make it easier to troubleshoot issues we create a separate logging configuration for the ​test
profile; the logback configuration file is ​logback-test.xml i​ n ​src/test/resources​.

We run the unit tests with the following command:

./mvnw test -Ptest -Dtest="/course/a1/rentals/tests/**"

© coaxial.ro 2018​ 106


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

To run a single unit test (e.g. ​UserServiceTest​) use the following command:

./mvnw test -Ptest -Dtest="UserServiceTest"

An alternative way of running only the unit tests is shown below - that will run only tests
matching the Surefire’s Maven plugin naming strategy (our unit tests ending in *Test will match
that)

./mvnw surefire:test -Ptest

Starting with JUnit 4.8, JUnit provides a method of separating our unit tests into related
categories. We can take advantage of this feature and group our tests in three categories:
Repository​, ​Service​ and ​Controller​. The groups are defined as interfaces in package
course.a1.rentals.tests.groups​.​ ​ Test classes or methods can then be annotated with
@Category(Repository.class)​, ​@Category(Service.class)​ or ​@Category(Controller.class)
to be assigned the respective group.

To execute only unit tests belonging to a certain group use the following command:

./mvnw test -Ptest


-Dgroups="course.a1.rentals.tests.groups.Repository"

Or this, to run more categories at the same time:

./mvnw test -Ptest


-Dgroups="course.a1.rentals.tests.groups.Repository,course.a1.rentals
.tests.groups.Service"

Unit Testing of Repository Classes


We are going to write all the code required for testing the repository classes in the
course.a1.rentals.tests​.​repository​ package.

To run the repository unit test we use a dedicated Spring configuration to ensure we only load
the minimum required components that allows us to focus only on testing the repository classes.
The configuration class is ​RepositoryConfiguration​.

package​​
course​
.a​1​
.r
​entals​
.t​ests​
.r​epository​

 
import​​
org​
.s​pringframework​
.b​oot​
.a​utoconfigure​
.​EnableAutoConfiguration​

© coaxial.ro 2018​ 107


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

import​​
org​
.s​pringframework​
.b​oot​
.a​utoconfigure​
.​domain​
.E
​ntityScan​

import​​
org​
.s​pringframework​
.c​ontext​
.​annotation​
.​
Configuration​

import​​
org​
.s​pringframework​
.d​ata​
.j​pa​
.r​epository​
.c​onfig​
.E
​nableJpaRepositories​

 
// Spring configuration class to be used for JPA tests. 
// Spring will not scan for the main application configuration 
(RentalsApplication.class). 
// We want Spring to load only the minimal amount of functionality required 
// for testing JPA repositories 
@Configuration 
// Required in this version of Spring to get the JPA tests working 
@EnableAutoConfiguration 
// Tell Spring the location of entity classes 
@EntityScan​
({ ​
"course.a1.rentals.model"​}) 
// Tell Spring the location of the repository classes 
@EnableJpaRepositories​
({ ​
"course.a1.rentals.repository"​}) 
public​​
class​R
​epositoryConfiguration​{ 
 
}

In this Spring configuration class we enable entity scanning in the ​model​ package and specify
the location of the repository classes with the ​@EnableJpaRepositories​ annotation.

In this version of Spring we also need to enable auto-configuration using the


@EnableAutoConfiguration​ annotation.

We create one test class for each repository class that we need to test. Each test class has a
number of annotations - see example below from ​UserRepositoryTest​ used to test the
UserRepository​ class.

● @RunWith(SpringRunner.class)​ - required to replace the JUnit test runner with a unit


test runner from Spring
● @DataJpaTest​ - this is a Spring annotation used for unit testing JPA repositories. It
enables just enough components/functionality to allow us to test repositories in isolation 
● @ContextConfiguration(classes=RepositoryConfiguration.class) ​- Spring
annotation used to specify the configuration class to be used when resolving
dependencies for this class; if it's not specified Spring will search for a suitable
configuration class and it will find ​RentalsApplication​ configuration class, the main
application configuration class; since we only want to wire the minimum amount of

© coaxial.ro 2018​ 108


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

dependencies required to run these JPA tests we need to point Spring to our custom
configuration class

@RunWith​
(​SpringRunner​
.c
​lass​

@DataJpaTest 
@ContextConfiguration​
(c​lasses​
=​RepositoryConfiguration​
.c
​lass​

public​​
class​U
​serRepositoryTest​{ 

@Autowired 

private​T
​estEntityManager​e
​ntityManager​

...

Spring provides us with a ​TestEntityManager​ class that we can wire into our unit test; this class
can be used to setup the data required for our unit tests. For instance in the ​setUp​ method of
our ​UserRepositoryTest​ class we use this entity manager to setup the role required by the test
class.

​Before 
@

public​v
​oid​s
​etUp​
() { 
r
​oleAdmin​= ​
new​R
​ole​
(R​ole​
.N​ame​
.A​DMIN​
); 
e
​ntityManager​
.p​ersist​
(r
​oleAdmin​
); 
e
​ntityManager​
.f​lush​
(); 
}

Unit Testing of Service and Component Classes


The code for service and component unit tests is in ​course.a1.rentals.tests​.​service ​and
course.a1.rentals.tests​.​component ​respectively​.
The strategy for developing unit tests for services and components is the same as for any other
unit test classes: we isolate the class we need to test by providing mocking for all of its
dependencies. Unlike for the repository tests we are not going to use a dedicated Spring
configuration class, service objects are instantiated and all of its mocked dependencies are
provided in the constructor.

We are going to use as example the code for service unit test classes - they are very similar to
the component unit tests. Below is the code from the unit test for ​UserService​ class
(​UserServiceTest​).

© coaxial.ro 2018​ 109


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

// Run these tests with the Spring test runner rather than 
// Junit's 
@RunWith​
(​SpringRunner​
.c
​lass​

// JUnit category assigned to this unit test 
@Category​
(S​ervice​
.c
​lass​

public​​
class​U
​serServiceTest​{ 

private​U
​serService​​
userService​

 

// Spring annotation used for mocking the marked class; 

// the beans of this class in the application context are 

// replaced by a mocked version 

@MockBean 

private​U
​serRepository​u
​serRepository​

   

@MockBean 

private​U
​serBonusPointsRepository​u
​serBonusPointsRepository​

   

// setup our mock ups required for these tests 

@Before 

public​v
​oid​s
​etUp​
() { 
t
​his​
.u
​serService​= n
​ew​U
​serServiceImpl​

userRepository​
, ​
userBonusPointsRepository​


new​B
​CryptPasswordEncoder​
()); 
   

User​a
​dmin​= n
​ew​​
User​
(​
"admin@rentals.io"​
, ​
"pass"​
,  
new​​
Role​
(​Role​
.​
Name​
.​
ADMIN​
)); 
   
// mock the calls to user repository - note that you must use 
// the same parameters when writing the unit test methods that make 
// use of this mocked calls 
  ​
Mockito​
.​when​
(​
userRepository​
.f​indByEmailIgnoreCase​
(​"admin@rentals.io"​
)) 
  .​
thenReturn​
(​Optional​
.​of​
(​
admin​
)); 
  ​
Page​
<​
User​
> ​
adminPage​= ​
new​​
PageImpl​
<U​ser​
>(​
Arrays​
.​
asList​
(​
admin​
),  
Pageable​
.​unpaged​
(), ​
1L​
); 
  ​
Mockito​
.​when​
(​
userRepository​
.f​indByEmailContainingIgnoreCase​
(​"admin@"​

  ​
Pageable​
.​unpaged​
())) 
  .​
thenReturn​
(​adminPage​
); 

© coaxial.ro 2018​ 110


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

   

@Test 

public​​
void​​
testSearchUserByPartialEmail​
() { 
// this will make use of the mocked user repository - 
// we expect back just the one entry we specified in our mock 
// configuration 
  ​
Page​
<​
User​
> f
​ound​= u
​serService​
.​searchUsers​

  ​
new​​
UserSearchFilter​
(​"admin@"​
), 
  ​
Pageable​
.​unpaged​
()); 
  ​
assertThat​
(​
found​
.g​etTotalElements​
(), ​
is​
(​1L​
)); 
     
  ​
User​​
admin​= ​
found​
.​
getContent​
().​
get​
(​
0​); 
  ​
assertThat​
(​
admin​
.​getEmail​
(), i
​s​
(​"admin@rentals.io"​
)); 
  ​
assertThat​
(​
admin​
.​getRoles​
(), h
​asItem​
(​new​​
Role​
(​
Role​
.​
Name​
.​ADMIN​
)))   
  } 

Unit Testing of Rest Controller Classes

Debugging Unit Tests


The Maven Surefire plugin that runs the unit tests when you invoke ​./mvnw test -Ptest​ by
default forks a new process. To debug this remote process you need to pass the
maven.surefire.debug ​JVM parameter as shown below.

./mvnw test -Dmaven.surefire.debug -Ptest

When you run this command you should see the unit tests stopping and waiting for a connection
from the debugger on port 5005.

© coaxial.ro 2018​ 111


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

You can use the same remote java application configuration described in section ​Debugging​ to
launch the debugger.

Integration Testing

We define our integration tests in a dedicated package: ​course.a1.rentals.itests​ and run them
in a dedicated profile:​ itest​.
We are using the same strategy as for our unit tests: create a different profile and all the
artefacts that allows us to customize and separate the configuration of the integration tests. We
have a dedicated application properties file (​application-itest.properties​), a dedicated log file
(​logback-itest.xml​).

The integration tests are run using the following command, passing the name of the profile
(​itest​):

./mvnw integration-test -Pitest

The ​integration-test ​phase runs the unit tests as well - to only run the integration tests run the
command below. The Failsafe Maven plugin naming strategy will select our integration tests
(ending with *IT)

./mvnw failsafe:integration-test -Pitest

To run a single integration test use this command:

./mvnw failsafe:integration-test -Pitest -Dit.test=<test class name>

(​e.g.​ ./mvnw failsafe:integration-test -Pitest -Dit.test=


"RentalsRestControllerIT")

Integration tests, by definition, need to test all the layers of the application - no mocking is used
so we’ll be using the Spring test facilities to ensure the application is fully started and the
integration test are using a REST client to fire request to the application.

Integration test classes are annotated with:


● @RunWith(SpringRunner.class) ​- ensure the test is run with the Spring test runner
● @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)​ - starts
the entire application and provides testing facilities. One of this is a ​TestRestTemplate
class available via auto-wiring; this class is used to build and execute HTTP request

© coaxial.ro 2018​ 112


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

against our REST API application. We also specify via the ​webEnvironment​ attribute
that we want our application to be started on a random port for these tests. The port is
made available to the test class via the ​@LocalServerPort​ annotation - as shown in the
example below a field of the test class is marked with this annotation and is
automatically initialized with the current port number.

To re-use common functionality we built an abstract class ​RentalsIT​ that is used as the super
class of all integration test classes.

This super class provides the following functionality:

● Stores the port number on which the application is started - using the
@LocalServerPort​ annotation
● Provides access to the TestRestTemplate instance
● Provides a method to get the access token for a user - operation required before
accessing any of our REST API endpoints

This is how ​UserRestControllerIT​ test class looks like:

@RunWith​
(​SpringRunner​
.c
​lass​

// Spring annotation to be used for integration testing 
// Note that if you have more than one SpringBootApplication class in your  
// application you will need to specify it with the classes attribute -  
// otherwise it will be automatically found for you 
// The webEnvironment attribute specifies that the embedded web server should  
// start on a random port - the actual port is available to the test class via  
// the @LocalServerPort annotation 
@SpringBootTest​
(w​ebEnvironment​= W
​ebEnvironment​
.R​ANDOM_PORT​

public​​
class​U
​serRestControllerIT​e
​xtends​R
​entalsIT​{ 
 
// Test that we can get an access token from the application 

@Test 

public​v
​oid​a
​ccessToken​
() ​
throws​E
​xception​{ 
S
​tring​t
​oken​= g
​etAccessToken​
("​admin@rentals.io"​
); 
a
​ssertThat​
(​token​
, i
​s​
(n
​otNullValue​
())); 

© coaxial.ro 2018​ 113


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

 

// Test that if we try to access the REST API without a token we get a  
// 401 HTTP status code returned 

@Test 

public​v
​oid​n
​oAccessTokenExpectError​
() t
​hrows​E
​xception​{ 
H
​ttpHeaders​h
​eaders​= ​
new​H
​ttpHeaders​
(); 
h
​eaders​
.s​etContentType​
(M​ediaType​
.A​PPLICATION_JSON​
); 
   
H
​ttpEntity​
<​String​
> r
​equest​= 
n
​ew​H
​ttpEntity​
<S​tring​
>(​
headers​
); 
H
​ttpStatus​​
result​= g
​etRestTemplate​
() 
.​
postForEntity​
(c​reateURL​
("​/api/rentals/users/search"​
), 

request​
, S
​tring​
.c​lass​
).​
getStatusCode​
(); 
   
a
​ssertThat​
(​result​
.v
​alue​
(), ​
is​
(4
​01​
)); 

 

// Test the user search functionality when searching by full email  
// address 
@Test 

public​v
​oid​s
​earchUserWhenFullEmailIsProvided​
() ​
throws​E
​xception​{ 
/
​/ get an access token to use with the request to test 
S
​tring​a
​ccessToken​= g
​etAccessToken​
("​admin@rentals.io"​
); 
 
H
​ttpHeaders​h
​eaders​= ​
new​H
​ttpHeaders​
(); 
h
​eaders​
.s​etContentType​
(M​ediaType​
.A​PPLICATION_JSON​
); 
h
​eaders​
.s​et​
("​Authorization"​
, "
​Bearer "​+ a
​ccessToken​
); 
   
U
​serSearchFilter​f
​ilter​= n
​ew​U
​serSearchFilter​
("
​admin@rentals.io"​
); 
H
​ttpEntity​
<​UserSearchFilter​
> r
​equest​= 
n
​ew​H
​ttpEntity​
<U​serSearchFilter​
>(​
filter​
, ​
headers​
); 
R
​esponseEntity​
<I
​TPage​
<​User​
>> ​
result​= ​
getRestTemplate​
() 
.​
exchange​
(c​reateURL​
("​/api/rentals/users/search"​
), 

HttpMethod​
.P​OST​
, r
​equest​


new​​
ParameterizedTypeReference​
<​
ITPage​
<U
​ser​
>>() {}); 
   
a
​ssertThat​
(​result​
.g
​etStatusCode​
().​
value​
(), ​
is​
(2
​00​
)); 
   

© coaxial.ro 2018​ 114


© coaxial.ro 2018 - Building RESTful APIs with Spring Boot

P
​age​
<U
​ser​
> ​
page​= ​
result​
.g
​etBody​
(); 
a
​ssertThat​
(​page​
.​getContent​
().​
size​
(), i
​s​
(1​)
​); 
a
​ssertThat​
(​page​
.​getContent​
().​
get​
(0​)
​.​
getEmail​
(), ​
is​
("
​admin@rentals.io"​
)); 

}

Debugging Integration Tests


The Maven Failsafe plugin that runs the integration tests when you invoke ​./mvnw
failsafe:integration-test -Pitest​ by default forks a new process. To debug this
remote process you need to pass the ​maven.failsafe.debug ​JVM parameter as shown
below.

./mvnw failsafe:integration-test -Dmaven.failsafe.debug -Pitest

When you run this command you should see the integration tests stopping and waiting for a
connection from the debugger on port 5005.

You can use the same remote java application configuration described in section ​Debugging​ to
launch the debugger.

© coaxial.ro 2018​ 115

You might also like