This is a continuation of the following article.
Minimum configuration sample of RESTful API in Jersey + Spring Framework https://qiita.com/kasa_le/items/59ebd6b5490945dd5580
Multi-module configuration with Maven (Jersey RESTful) https://qiita.com/kasa_le/items/db0d84e3e868ff14bc2b
This time, it's like merging the projects created in the above two articles. However, I will explain by creating a project from scratch.
Implement RESTful API with ** Jersey ** and ** Spring Framework ** (not Boot).
--Jersey + Spring Framework works RESTful API (CRUD). --DI can be done with a multi-module configuration.
Tools etc. | Version etc. |
---|---|
MacbookPro | macOS Mojave 10.14.5 |
IntelliJ IDEA | Ultimate 2019.3.3 |
Java | AdoptOpenJDK 11 |
apache maven | 3.6.3 |
Jersey | 2.30.1 |
JUnit | 5.6.0 |
Tomcat | apache-tomcat-8.5.51 |
Postman | 7.19.1 |
Spring Framework | 5.2.4-RELEASE |
In IntelliJ, create a new Maven project
.
Basically, you can copy the dependency and plugin settings from the Jersey + Spring Framework minimum configuration project.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.example.jerseyspring</groupId>
<artifactId>jersey-spring-restful</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Jersey and Spring Framework RESTfulAPI Sample</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<inherited>true</inherited>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>src/test/java/</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Jersey -->
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Jersey + Spring -->
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.ext/jersey-spring5 -->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring5</artifactId>
<version>${jersey.version}</version>
</dependency>
<!--JAXB removed from JDK9-->
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
<!--test-->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.test-framework.providers/jersey-test-framework-provider-grizzly2 -->
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.30.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.15.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<spring.version>5.2.4.RELEASE</spring.version>
<jersey.version>2.30.1</jersey.version>
<junit.jupiter.version>5.6.0</junit.jupiter.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
src
folderDelete it manually.
From the IntelliJ menu [File]-[New]-[Module ...], add the following two in the Maven project.
The name of the submodule is arbitrary. Don't forget to set ** Parent **. (Please select the root project.)
Then the <packaging>
of the parent pom.xml
should change to pom
.
If it has not changed, fix it manually.
root/pom.xml
<groupId>my.example.jerseyspring</groupId>
<artifactId>jersey-spring-restful</artifactId>
<packaging>pom</packaging>
Each pom.xml
in the submodule should look like this:
repository/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jersey-spring-restful</artifactId>
<groupId>my.example.jerseyspring</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>repository</artifactId>
<packaging>jar</packaging>
</project>
serverapi/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jersey-spring-restful</artifactId>
<groupId>my.example.jerseyspring</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>serverapi</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>my.example.jerseyspring</groupId>
<artifactId>repository</artifactId>
<version>${project.version}</version>
</dependency>
<!-- for spring test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
The serverapi module is included in the <test>
scope because Spring Test is required for Junit test.
However, if you need it in other submodules, you can put it in the root pom.xml
.
Neither of them has specified their own version. I want to use the parent version as it is.
It is a policy not to manage the version for each submodule. If you need to, specify the version for each with the <version>
tag.
The current folder structure should look like this.
$ tree
.
├── SpringJerseyRest.iml
├── pom.xml
├── repository
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ └── resources
│ └── test
│ └── java
└── serverapi
├── pom.xml
└── src
├── main
│ ├── java
│ └── resources
└── test
└── java
Create the following classes in the package my.example.jerseyspring.repository.models
.
Employee.java
package my.example.jerseyspring.repository.models;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Employee {
private int id;
private String firstName;
public Employee() {
}
public Employee(int id, String firstName) {
this.id = id;
this.firstName = firstName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Create the ʻEmployeeRepository class in the package
my.example.jerseyspring.repository`.
EmployeeRepository.java
package my.example.jerseyspring.repository;
import my.example.jerseyspring.repository.exceptions.DuplicateIdException;
import my.example.jerseyspring.repository.exceptions.EmployeeNameNotFoundException;
import my.example.jerseyspring.repository.exceptions.EmployeeNotFoundException;
import my.example.jerseyspring.repository.models.Employee;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@Repository
public class EmployeeRepository {
private List<Employee> employeeList;
public EmployeeRepository() {
employeeList = new ArrayList<>();
employeeList.add(new Employee(3, "Cupcake"));
employeeList.add(new Employee(4, "Donuts"));
employeeList.add(new Employee(5, "Eclair"));
employeeList.add(new Employee(8, "Froyo"));
employeeList.add(new Employee(9, "Gingerbread"));
}
public List<Employee> selectAll() {
return employeeList;
}
public Employee select(int id) {
for (Employee employee : employeeList) {
if (employee.getId() == id) {
return employee;
}
}
throw new EmployeeNotFoundException();
}
public synchronized void insert(int id, String firstName) {
try {
select(id);
} catch (EmployeeNotFoundException e) {
//If not, you can add
employeeList.add(new Employee(id, firstName));
return;
}
//Cannot be added if the same ID exists
throw new DuplicateIdException();
}
public synchronized void update(int id, String firstName) {
Employee employee = select(id);
employee.setFirstName(firstName);
}
public synchronized void delete(int id) {
Employee employee = select(id);
employeeList.remove(employee);
}
public List<Employee> search(String name) {
List<Employee> list = new ArrayList<>();
for (Employee employee : employeeList) {
if (employee.getFirstName().contains(name)) {
list.add(employee);
}
}
if (list.size() > 0) return list;
throw new EmployeeNameNotFoundException(name);
}
}
In the previous Multi-module sample, I put the singleton implementation by myself, but the DI of Spring Framework is basically singleton. , Removing its implementation.
ʻCreate each exception class used in the EmployeeRepositoryclass. The package is
repository.exceptions`.
Example) ʻEmployeeNotFoundException` class
EmployeeNotFoundException.java
package my.example.jerseyspring.repository.exceptions;
public class EmployeeNotFoundException extends RuntimeException {
public EmployeeNotFoundException() {
super("The Employee with that ID cannot be found.");
}
}
Create other classes in the same way.
Also, I will bring the transacation
package used in the Jersey + Spring minimum configuration project.
The repository.transaction
package is fine.
Create a test class for the exception class.
As an example, let's take the case of ʻEmployeeNotFoundException`.
EmployeeNotFoundExceptionTest.java
package my.example.jerseyspring.repository.exceptions;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class EmployeeNotFoundExceptionTest {
@Test
void getMessage() {
EmployeeNotFoundException e = new EmployeeNotFoundException();
assertThat(e.getMessage()).isEqualTo("The Employee with that ID cannot be found.");
}
}
Let's run the Junit test from IntelliJ and check that it passes.
In the end, you should have a folder structure like this:
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── my
│ │ └── example
│ │ └── jerseyspring
│ │ └── repository
│ │ ├── EmployeeRepository.java
│ │ ├── exceptions
│ │ │ ├── DuplicateIdException.java
│ │ │ ├── EmployeeNameNotFoundException.java
│ │ │ └── EmployeeNotFoundException.java
│ │ ├── models
│ │ │ └── Employee.java
│ │ └── transaction
│ │ ├── TransactionBo.java
│ │ └── impl
│ │ └── TransactionBoImpl.java
│ └── resources
└── test
└── java
└── my
└── example
└── jerseyspring
└── repository
└── exceptions
├── DuplicateIdExceptionTest.java
├── EmployeeNameNotFoundExceptionTest.java
└── EmployeeNotFoundExceptionTest.java
19 directories, 11 files
Create a ʻEmployeeServiceclass in the package
my.example.jerseyspring.rest`.
EmployeeService.java
package my.example.jerseyspring.rest;
import my.example.jerseyspring.repository.EmployeeRepository;
import my.example.jerseyspring.repository.models.Employee;
import org.springframework.stereotype.Service;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.util.List;
@Service
@Path("/employees")
public class EmployeeService {
final EmployeeRepository employeeRepository;
public EmployeeService(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
@Context
UriInfo uriInfo;
@GET
@Path("/all")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public List<Employee> getAll() {
return employeeRepository.selectAll();
}
@GET
@Path("/{id}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Employee getEmployee(@PathParam("id") int id) {
return employeeRepository.select(id);
}
@GET
@Path("/search")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public List<Employee> searchEmployee(@QueryParam("name") String name) {
return employeeRepository.search(name);
}
@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response addEmployee(Employee employee) {
employeeRepository.insert(employee.getId(), employee.getFirstName());
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.path(String.valueOf(employee.getId()));
return Response.created(builder.build()).build();
}
@PUT
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response updateEmployee(Employee employee) {
employeeRepository.update(employee.getId(), employee.getFirstName());
//If you create a new one, you need to return created, but in this sample it is an error, so it always returns ok.
return Response.ok().build();
}
@DELETE
@Path("/{id}")
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response deleteEmployee(@PathParam("id") int id) {
employeeRepository.delete(id);
//To return the status of Entity, return ok.
//If it is accepted but the process is not finished(Just got on the queue, etc.)Returns accepted
//In this sample, only the deletion is completed and the corresponding content is exhausted.
return Response.noContent().build();
}
}
Also, I will bring the PaymentService
class used in the Jersey + Spring minimum configuration project.
Create a class for exception mapping in the rest.handlers
package.
Required for each exception class created in the repository module.
For exception mapping, click here (https://qiita.com/kasa_le/items/3c7e7a426acf846ee64e#%E4%BE%8B%E5%A4%96%E3%83%8F%E3%83%B3%E3 Please refer to% 83% 89% E3% 83% AA% E3% 83% B3% E3% 82% B0).
Example) ʻEmployeeNotFoundException` class mapper
NotFoundExceptionHandler.java
package my.example.jerseyspring.rest.handler;
import my.example.jerseyspring.repository.exceptions.EmployeeNotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class NotFoundExceptionHandler implements ExceptionMapper<EmployeeNotFoundException> {
public Response toResponse(EmployeeNotFoundException ex) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Create ʻapplicationContext.xml under
src / main / resources` and make it as follows.
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="my.example.jerseyspring"/>
<bean id="transactionBo" class="my.example.jerseyspring.repository.transaction.impl.TransactionBoImpl"/>
<bean id="employeeRepository" class="my.example.jerseyspring.repository.EmployeeRepository"/>
</beans>
webapp
folder settingsCreate a src / main / webapp
folder.
--Create web.xml
under webapp / WEB-INF
and make it the following content
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Restful Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>my.example.jerseyspring</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
--Create ʻindex.jsp under
webapp / `and make it as follows
index.jsp
<html>
<body>
<h2>Jersey + Spring RESTful Web Application!</h2>
<p><a href="rest/payment/mkyong">Jersey resource</a>
<br>
<p><a href="rest/employees/all">All Employee List</a>
<p><a href="rest/employees/3">get id=3 employee</a>
</body>
</html>
ʻCreate a EmployeeServiceTestclass and a
PaymentServiceTest` class to write a test.
EmployeeServiceTest.java
package my.example.jerseyspring.rest;
import my.example.jerseyspring.repository.EmployeeRepository;
import my.example.jerseyspring.repository.models.Employee;
import my.example.jerseyspring.rest.handler.DuplicateExceptionHandler;
import my.example.jerseyspring.rest.handler.NameNotFoundExceptionHandler;
import my.example.jerseyspring.rest.handler.NotFoundExceptionHandler;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:/applicationContext.xml")
public class EmployeeServiceTest extends JerseyTest {
@Autowired
EmployeeRepository employeeRepository;
@Override
protected Application configure() {
return new ResourceConfig(EmployeeService.class)
.register(DuplicateExceptionHandler.class)
.register(NameNotFoundExceptionHandler.class)
.register(NotFoundExceptionHandler.class)
.register(this);
}
@BeforeEach
@Override
public void setUp() throws Exception {
super.setUp();
}
@AfterEach
@Override
public void tearDown() throws Exception {
super.tearDown();
}
@ParameterizedTest
@ValueSource(strings = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public void getAll(String mediaType) {
final Response response = target("/employees/all").request().accept(mediaType).get();
assertThat(response.getHeaderString("Content-Type"))
.isEqualTo(mediaType);
List<Employee> content = response.readEntity(new GenericType<>() {
});
assertThat(content.size()).isEqualTo(5);
assertThat(content.get(0)).isEqualToComparingFieldByField(new Employee(3, "Cupcake"));
assertThat(content.get(1)).isEqualToComparingFieldByField(new Employee(4, "Donuts"));
assertThat(content.get(2)).isEqualToComparingFieldByField(new Employee(5, "Eclair"));
assertThat(content.get(3)).isEqualToComparingFieldByField(new Employee(8, "Froyo"));
assertThat(content.get(4)).isEqualToComparingFieldByField(new Employee(9, "Gingerbread"));
}
@ParameterizedTest
@MethodSource("getParamProvider")
public void getEmployee(int id, String mediaType) {
String urlPath = String.format("/employees/%d", id);
final Response response = target(urlPath).request().accept(mediaType).get();
assertThat(response.getHeaderString("Content-Type"))
.isEqualTo(mediaType);
Employee employee = response.readEntity(Employee.class);
Employee expect = employeeRepository.select(id);
assertThat(employee).isEqualToComparingFieldByField(expect);
}
static Stream<Arguments> getParamProvider() {
return Stream.of(
Arguments.of(3, MediaType.APPLICATION_JSON),
Arguments.of(4, MediaType.APPLICATION_JSON),
Arguments.of(5, MediaType.APPLICATION_JSON),
Arguments.of(8, MediaType.APPLICATION_JSON),
Arguments.of(9, MediaType.APPLICATION_JSON),
Arguments.of(3, MediaType.APPLICATION_XML),
Arguments.of(4, MediaType.APPLICATION_XML),
Arguments.of(5, MediaType.APPLICATION_XML),
Arguments.of(8, MediaType.APPLICATION_XML),
Arguments.of(9, MediaType.APPLICATION_XML)
);
}
@ParameterizedTest
@ValueSource(strings = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public void searchEmployee(String mediaType) {
final Response response = target("/employees/search")
.queryParam("name", "a")
.request()
.accept(mediaType)
.get();
assertThat(response.getHeaderString("Content-Type"))
.isEqualTo(mediaType);
List<Employee> content = response.readEntity(new GenericType<>() {
});
assertThat(content.size()).isEqualTo(3);
assertThat(content.get(0)).isEqualToComparingFieldByField(new Employee(3, "Cupcake"));
assertThat(content.get(1)).isEqualToComparingFieldByField(new Employee(5, "Eclair"));
assertThat(content.get(2)).isEqualToComparingFieldByField(new Employee(9, "Gingerbread"));
}
@ParameterizedTest
@MethodSource("postRawProvider")
public void addEmployee(int id, String bodyRaw, String mediaType) {
final Response response = target("/employees").request()
.post(Entity.entity(bodyRaw, mediaType));
assertThat(response.getStatus()).isEqualTo(201);
assertThat(response.getHeaderString("Location"))
.isEqualTo("http://localhost:9998/employees/" + id);
}
static Stream<Arguments> postRawProvider() {
final String json = "{\"firstName\":\"Honeycomb\",\"id\":11}";
final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<employee><firstName>KitKat</firstName><id>19</id></employee>";
return Stream.of(
Arguments.of(11, json, MediaType.APPLICATION_JSON),
Arguments.of(19, xml, MediaType.APPLICATION_XML)
);
}
@ParameterizedTest
@MethodSource("putRawProvider")
public void updateEmployee(int id, String bodyRaw, String mediaType) {
final Response response = target("/employees").request()
.put(Entity.entity(bodyRaw, mediaType));
assertThat(response.getStatus()).isEqualTo(200);
Employee employee = target("/employees/" + id).request().get(Employee.class);
Employee expected = employeeRepository.select(id);
assertThat(employee).isEqualToComparingFieldByField(expected);
}
static Stream<Arguments> putRawProvider() {
final String json = "{\"firstName\":\"Frozen yogurt\",\"id\":8}";
final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<employee><firstName>Cup Cake</firstName><id>3</id></employee>";
return Stream.of(
Arguments.of(8, json, MediaType.APPLICATION_JSON),
Arguments.of(3, xml, MediaType.APPLICATION_XML)
);
}
@Test
public void deleteEmployee() {
final Response response = target("/employees/9")
.request().delete();
assertThat(response.getStatus()).isEqualTo(204);
}
@Test
public void exception_selectEmployee() {
final Response response = target("/employees/1").request().get();
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
public void exception_searchEmployee() {
final Response response = target("/employees/search?name=android").request().get();
assertThat(response.getStatus()).isEqualTo(404);
}
@ParameterizedTest
@MethodSource("putRawProvider")
public void exception_addEmployee(int id, String bodyRaw, String mediaType) {
final Response response = target("/employees").request()
.post(Entity.entity(bodyRaw, mediaType));
assertThat(response.getStatus()).isEqualTo(409);
}
@ParameterizedTest
@MethodSource("putExceptionProvider")
public void exception_updateEmployee(int id, String bodyRaw, String mediaType) {
final Response response = target("/employees").request()
.put(Entity.entity(bodyRaw, mediaType));
assertThat(response.getStatus()).isEqualTo(404);
}
static Stream<Arguments> putExceptionProvider() {
final String json = "{\"firstName\":\"Lollipop\",\"id\":21}";
final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<employee><firstName>Jelly Bean</firstName><id>17</id></employee>";
return Stream.of(
Arguments.of(21, json, MediaType.APPLICATION_JSON),
Arguments.of(3, xml, MediaType.APPLICATION_XML)
);
}
@Test
public void exception_deleteEmployee() {
final Response response = target("/employees/1").request().get();
assertThat(response.getStatus()).isEqualTo(404);
}
}
In Multi-module sample, the part where the Sington object was acquired as ʻEmployeeRepository.getInstanceis to the member variable declared by
@ Autowired. Access and change. With the
@Autowiredannotation, Spring will DI for you, but in fact it seems that different instances will be created for the person who actually works with the service and the person who tests (it should be a singleton, but probably the test). I've seen an article saying that it's okay to do
ResourceConfig # register (this)` (because the context is different from the application context).
However, isn't it a bug that works with this? Because there is a warning message? I don't know if it's correct or not.
https://stackoverflow.com/questions/34453448/how-to-access-spring-bean-from-jerseytest-subclass
This time, the repository class isn't the main subject in the first place, it's just providing temporary data access, so I'm happy with this. However, please note that it may stop working when the version of Spring or Jersey goes up.
The PaymentServiceTest
class is also brought from Minimum configuration sample.
Also, bring in the TransactionBoMock
class created for the DI mock replacement test.
Then put ʻapplicationContext.xml in the
resourcesfolder of the
test` folder so that you can replace the bean, and do as follows.
test/resources/applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="my.example.jerseyspring"/>
<bean id="transactionBo" class="my.example.jerseyspring.repository.transaction.impl.TransactionBoMock"/>
<bean id="employeeRepository" class="my.example.jerseyspring.repository.EmployeeRepository"/>
</beans>
Since EmployeeRepository does not have an interface, I will not replace it this time, but originally, I should use a Mocked class for testing for the original implementation that acquires data from DB etc.
The structure of the serverapi
folder should look like this:
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── my
│ │ └── example
│ │ └── jerseyspring
│ │ └── rest
│ │ ├── EmployeeService.java
│ │ ├── PaymentService.java
│ │ └── handler
│ │ ├── DuplicateExceptionHandler.java
│ │ ├── NameNotFoundExceptionHandler.java
│ │ └── NotFoundExceptionHandler.java
│ ├── resources
│ │ └── applicationContext.xml
│ └── webapp
│ ├── WEB-INF
│ │ └── web.xml
│ └── index.jsp
└── test
├── java
│ └── my
│ └── example
│ └── jerseyspring
│ ├── repository
│ │ └── transaction
│ │ └── impl
│ │ └── TransactionBoMock.java
│ └── rest
│ ├── EmployeeServiceTest.java
│ └── PaymentServiceTest.java
└── resources
└── applicationContext.xml
21 directories, 13 files
(1)JUnitTest Run JUnitTest to see if all the tests pass.
$ mvn clean test
Set up and deploy Tomcat.
You should see a page like the one below, click the link and you should see the API return value.
If you want to check the input / output of POST system or Json / Xml, please check using Curl or Postman.
The projects so far are uploaded below. https://github.com/le-kamba/spring-jersey-sample/tree/spring_jersey
I was able to do it smoothly without getting hooked except for the singleton part.
By the way, the folder structure was put out by putting the ** tree ** command with brew on mac. I had to do it from the installation of Homebrew in the first place. (Did you not put it in?)
#Homebrew installation
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
#installation of tree
$ brew install tree
What's the difference between Spring DI and new? https://qiita.com/uqichi/items/5f59817beb3dff9c0c1e
Recommended Posts