Spring Boot + Jersey REST Example

By Arvind Rai, November 24, 2023
On this page we will learn to create REST API using Jersey in Spring Boot application. Jersey is the JAX-RS implementation. JAX-RS is the Java API for RESTful web services. Jersey RESTful web service is the open source provided by SUN Microsystems. Jersey is the reference implementation for JSR 311 and other additional features.
In my demo application, I will use Jersey 2 to create RESTful web service. We will discuss here JAX-RS API that will be used to create Jersey RESTful web service and Jersey client and then we will create demo application for Jersey RESTful web service and Jersey client both. We will perform CRUD operation using Hibernate.
To integrate Spring boot with Jersey, we need to follow given points.
1. Create a Jersey endpoint and annotate it with Spring @Component annotation.
2. Implement a class using ResourceConfig annotated with @Component and register endpoints using register() method.

JAX-RS API for Jersey REST Web Service

Find the JAX-RS API to create Jersey REST web service endpoints.
1. Handle HTTP methods using javax.ws.rs API.
@GET: Responds to HTTP GET method.
@POST: Responds to HTTP POST method.
@PUT: Responds to HTTP PUT method.
@DELETE: Responds to HTTP DELETE method.
@HEAD: Responds to HTTP HEAD method.
@OPTIONS: Responds to HTTP OPTIONS method.

2. Find the javax.ws.rs API to handle paths.
@Path : Defines a URI path for a class or method. It can be annotated on methods and class level with a relative path.
@Path("/article")
public class ArticleEndpoint { 
  @GET
  @Path("/details")
  public Response getArticleDetails() { }
}
@ApplicationPath : Defines the application path that is used as base URI for all resource URIs provides by @Path. It is used at subclass of ResourceConfig.
@ApplicationPath("/spring-app")
public class JerseyConfig extends ResourceConfig {
}
Now the URI to access getArticleDetails() REST web service method will be as given below.
/spring-app/article/details 
3. Find the javax.ws.rs API to produce and consume media type.
@Produces : Dfines the media types that the method can produce. If no media type is defined then container can assume to produce any type of media type. The media type defined by @Produces at method level overrides the media type defined by @Produces at class level. If a HTTP request demands a media type that cannot be produced by REST web service method then the container must respond with HTTP status 406 Not Acceptable.
@GET
@Path("/details")
@Produces(MediaType.APPLICATION_JSON)
public Response getArticleDetails() {
} 
The above method will produce media type as application/json.

@Consumes : Defines the media types that the method can consume. If no media type is defined then container can assume to consume any type of media type. The media type defined by @Consumes at method level overrides the media type defined by @Consumes at class level. If a HTTP request has a media type that cannot be consumed by REST web service method then the container must respond with HTTP status 415 Unsupported Media Type.
@DELETE
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)		
public Response deleteArticle(@PathParam("id") Integer id) {
	articleService.deleteArticle(id);
	return Response.noContent().build();
} 
The above method will consume media type as application/json.

4. Find the javax.ws.rs API to produce and consume media type.
@PathParam: Binds the value of URI template parameter to a resource method parameter. Find the code snippet.
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getArticleById(@PathParam("id") Integer id) {
} 
@QueryParam: Binds the value of HTTP query parameter to a resource method parameter. Find the code snippet.
@GET
@Path("/data")
@Produces(MediaType.APPLICATION_JSON)
public Response getArticleById(@QueryParam("id") Integer id) {
} 
@CookieParam: Binds the value of HTTP cookie to a resource method parameter.
@FormParam: Binds the value of form parameter in a request entity to a resource method parameter.
@HeaderParam: Binds the HTTP header to a resource method parameter.
@MatrixParam: Binds the URI matrix parameter to a resource method parameter.
@DefaultValue: Binds the default value to a resource method parameter. It is used with @PathParam, @QueryParam etc.
@BeanParam: Injects custom JAX-RS parameter aggregator value object into a resource class field.

JAX-RS API for Jersey Client

Find the JAX-RS API for Jersey client.
1. Client
The Client is an interface which is contained in javax.ws.rs.client package introduced in JAX-RS 2.0. Jersey uses Client as the main entry point to execute client requests to consume responses returned from the RESTful web services. Client is a heavy-weight object. So we should avoid creating so many objects of Client implementations. It is necessary to close Client object by calling close() method to avoid leaking resources.
Client client = ClientBuilder.newClient();
---
client.close(); 
2. WebTarget
WebTarget is an interface contained in javax.ws.rs.client package. It has been introduced in JAX-RS 2.0 .WebTarget is a resource target identified by resource URI.
WebTarget base = client.target("http://localhost:8080/spring-app/article"); 
We can append the URI to base URI using WebTarget.path() method that returns the instance of WebTarget.
WebTarget details = base.path("details"); 
So the final URL will be as below.
http://localhost:8080/spring-app/article/details 
If we want to add query parameter, do as follows.
WebTarget details = base.path("details").queryParam("id", "101"); 
If we want to add path parameter, do as follows.
WebTarget articleById = base.path("{id}").resolveTemplate("id", "101"); 
3. SyncInvoker and Invocation.Builder
SyncInvoker is the uniform interface for synchronous invocation of HTTP methods. Invocation.Builder is the implementation class of SyncInvoker interface.
Now find the methods of SyncInvoker interface that are used to interact with REST web service resources. They serve current request synchronously.
get(): Invoke HTTP GET method.
post(): Invoke HTTP POST method.
put(): Invoke HTTP PUT method.
delete(): Invoke HTTP DELETE method.
head(): Invoke HTTP HEAD method.
options(): Invoke HTTP OPTIONS method.

To get instance of Invocation.Builder, we need to call following method.
WebTarget.request(MediaType... acceptedResponseTypes) 
For Example
Invocation.Builder builder = details.request(MediaType.APPLICATION_JSON); 
As we know that Invocation.Builder is the implementation of SyncInvoker, we can call get() method as below.
public void getArticleDetails() {
    Client client = ClientBuilder.newClient();
    WebTarget base = client.target("http://localhost:8080/spring-app/article");
    WebTarget details = base.path("details");
    List<Article> list = details.request(MediaType.APPLICATION_JSON)
		.get(new GenericType<List<Article>>() {});
		
    list.stream().forEach(article -> 
	 System.out.println(article.getArticleId()+", "+ article.getTitle()+", "+ article.getCategory()));
	    
    client.close();
} 
If we want to add request headers, do it as below.
MultivaluedMap<String, Object> myHeaders = new MultivaluedHashMap<>();
myHeaders.add("Content-Type", "application/json");
List<Article> list = details.request(MediaType.APPLICATION_JSON).headers(myHeaders)
	.get(new GenericType<List<Article>>() {}); 
MultivaluedMap and MultivaluedHashMap are the API of javax.ws.rs.core package.

Create REST Web Service

We will create our demo application now for Jersey RESTful web service. We will perform CRUD operation on articles. We will provide Jersey endpoint to create, read, update and delete article.

1. REST Web Service URLs for CRUD

In our example we will create following REST web service URLs for CRUD operation.
1. Create :
HTTP Method: POST, URL: /spring-app/article/add
HTTP Response Status Code: 201 CREATED and 409 CONFLICT

2. Read :
HTTP Method: GET, URL: /spring-app/article/{id}
HTTP Method: GET, URL: /spring-app/article/details
HTTP Response Status Code: 200 OK

3. Update :
HTTP Method: PUT, URL: /spring-app/article/update
HTTP Response Status Code: 200 OK

4. Delete :
HTTP Method: DELETE, URL: /spring-app/article/{id}
HTTP Response Status Code: 204 NO CONTENT

2. Project Structure using Eclipse

Find the structure of our demo project in eclipse.
Spring Boot + Jersey REST + JPA + Hibernate CRUD Example

3. Create Maven File

Find the Maven file used in our example.
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">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.concretepage</groupId>
	<artifactId>spring-boot-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>spring-demo</name>
	<description>Spring Boot Demo Project</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.3.RELEASE</version>
	</parent>
	<properties>
		<java.version>1.8</java.version>        
	</properties>
	<dependencies>
	    <dependency>
		 <groupId>org.springframework.boot</groupId>
		 <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
	    <dependency>
		 <groupId>org.springframework.boot</groupId>
		 <artifactId>spring-boot-starter-jersey</artifactId>
	    </dependency>
	    <dependency>
		 <groupId>org.springframework.boot</groupId>
		 <artifactId>spring-boot-starter-data-jpa</artifactId>
	    </dependency>				
	    <dependency>
		 <groupId>mysql</groupId>
		 <artifactId>mysql-connector-java</artifactId>
	    </dependency>		
    	    <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-devtools</artifactId>
                 <optional>true</optional>
            </dependency> 
	</dependencies> 
	<build>
	    <plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
	    </plugins>
	</build>
</project> 
Find the description of Spring boot starter.
spring-boot-starter-parent: Parent POM for dependency management.
spring-boot-starter-web: Starter for building web, REST applications. It uses tomcat server as default embedded server.
spring-boot-starter-data-jpa: Starter for Spring data JPA with hibernate.
spring-boot-starter-jersey: Starter for Jersey RESTful web service.
spring-boot-devtools: It provides developer tools. These tools are helpful in application development mode. One of the features of developer tool is automatic restart of the server for any change in code.
spring-boot-maven-plugin: It is used to create executable JAR of the application.

4. Create Jersey Endpoint

Find the Jersey endpoint that will define web service methods. We will create methods for create, read, update and delete operations.
ArticleEndpoint.java
@Component
@Path("/article")
public class ArticleEndpoint {
	private static final Logger logger = LoggerFactory.getLogger(ArticleEndpoint.class);	
	@Autowired
	private IArticleService articleService;
	@GET
	@Path("/details")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getArticleDetails() {
		List<Article> list = articleService.getAllArticles(); 
		return Response.ok(list).build();
	}
	@GET
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response getArticleById(@PathParam("id") Integer id) {
		Article article = articleService.getArticleById(id);
		return Response.ok(article).build();
	}
	@POST
	@Path("/add")
	@Consumes(MediaType.APPLICATION_JSON)
	public Response addArticle(Article article) {
                boolean isAdded = articleService.addArticle(article);
                if (!isAdded) {
        	   logger.info("Article already exits.");
	           return Response.status(Status.CONFLICT).build();
                }
                return Response.created(URI.create("/spring-app/article/"+ article.getArticleId())).build();
	}	
	@PUT
	@Path("/update")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)	
	public Response updateArticle(Article article) {
		articleService.updateArticle(article);
		return Response.ok(article).build();
	}
	@DELETE
	@Path("/{id}")
	@Consumes(MediaType.APPLICATION_JSON)		
	public Response deleteArticle(@PathParam("id") Integer id) {
		articleService.deleteArticle(id);
		return Response.noContent().build();
	}	
} 

5. Register Jersey Endpoints using ResourceConfig

To register Jersey endpoints, we need to create a class implementing ResourceConfig and call its register() method and pass our endpoint as an argument.
JerseyConfig.java
@Component
@ApplicationPath("/spring-app")
public class JerseyConfig extends ResourceConfig {
   public JerseyConfig() {
	register(ArticleEndpoint.class);
   }
} 
If we have more than one endpoints, we need to call register() method more than one time.
public JerseyConfig() {
   register(UserEndpoint.class);
   register(ArticleEndpoint.class);
} 
@ApplicationPath: It defines the application path that is used as base URI for all resource URIs provides by @Path . Default value for application path is "/"

6. CORS Configuration

In Jersey RESTful web service, to handle Cross-Origin-Resource-Sharing (CORS), we will create a Filter. We need to keep its order highest, so that it can be served for every request.
CORSFilter.java
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
        	HttpServletResponse httpResponse = (HttpServletResponse) response;
	        httpResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:8585");
	        httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        	httpResponse.setHeader("Access-Control-Allow-Headers", "X-Auth-Token, Content-Type");
	        httpResponse.setHeader("Access-Control-Expose-Headers", "custom-header1, custom-header2");
        	httpResponse.setHeader("Access-Control-Allow-Credentials", "false");
	        httpResponse.setHeader("Access-Control-Max-Age", "4800");
	        chain.doFilter(request, response);
	}
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}
	@Override
	public void destroy() {
	}
}  
In the above code we have enabled following URL
http://localhost:8585 
Application running on the above domain can access our Jersey RESTful web service using script.

7. Create Database using MySQL

Find the MySQL database schema for CRUD operation.
Database Schema
-- Dumping database structure for concretepage
CREATE DATABASE IF NOT EXISTS `concretepage` ;
USE `concretepage`;
-- Dumping structure for table concretepage.articles
CREATE TABLE IF NOT EXISTS `articles` (
  `article_id` int(5) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `category` varchar(100) NOT NULL,
  PRIMARY KEY (`article_id`)
) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=latin1;
-- Dumping data for table concretepage.articles: ~4 rows (approximately)
INSERT INTO `articles` (`article_id`, `title`, `category`) VALUES
	(101, 'Angular 2 Tutorial using CLI', 'Angular'),
	(102, 'Spring Boot Getting Started', 'Spring Boot'),
	(103, 'Lambda Expressions Java 8 Example', 'Java 8'),
	(104, 'Android AsyncTask Example', 'Android'); 
Find the JPA entity for the database table.
Article.java
@Entity
@Table(name="articles")
public class Article implements Serializable { 
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="article_id")
        private int articleId;  
	@Column(name="title")
        private String title;
	@Column(name="category")	
	private String category;
	public int getArticleId() {
		return articleId;
	}
	public void setArticleId(int articleId) {
		this.articleId = articleId;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
}

8. Create application.properties

In Spring boot, to configure database related properties, Hibernate and logging, we need to use application.properties. We are using Hibernate implementation of JPA specification.
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/concretepage
spring.datasource.username=root
spring.datasource.password=
spring.datasource.tomcat.max-wait=20000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.max-idle=20
spring.datasource.tomcat.min-idle=15

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.id.new_generator_mappings = false
spring.jpa.properties.hibernate.format_sql = true

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=INFO
logging.level.com.concretepage= INFO  
Use prefix spring.datasource.* to configure datasource related properties. Use prefix spring.jpa.properties.* to configure JPA related properties.
We can configure following Jersey properties in application.properties to change default Spring boot configurations for Jersey.
spring.jersey.application-path: Application path that acts as base URI. It overrides the value of @ApplicationPath.
spring.jersey.type: The value can be servlet or filter. Default value is servlet.
spring.jersey.filter.order: It defines the Jersey filter chain order. Default value is 0.
spring.jersey.init.*: Init parameters that will be passed to Jersey servlet or filter.
spring.jersey.servlet.load-on-startup: Load on startup priority of Jersey servlet. Default is -1.

9. Create DAO

Find the DAO interface.
IArticleDAO.java
public interface IArticleDAO {
    List<Article> getAllArticles();
    Article getArticleById(int articleId);
    void addArticle(Article article);
    void updateArticle(Article article);
    void deleteArticle(int articleId);
    boolean articleExists(String title, String category);
} 
Find the implementation of DAO interface. We are using here JPA EntityManager to interact with database.
ArticleDAO.java
@Transactional
@Repository
public class ArticleDAO implements IArticleDAO {
	@PersistenceContext	
	private EntityManager entityManager;	
	@Override
	public Article getArticleById(int articleId) {
		return entityManager.find(Article.class, articleId);
	}
	@SuppressWarnings("unchecked")
	@Override
	public List<Article> getAllArticles() {
		String hql = "FROM Article as atcl ORDER BY atcl.articleId";
		return (List<Article>) entityManager.createQuery(hql).getResultList();
	}	
	@Override
	public void addArticle(Article article) {
		entityManager.persist(article);
	}
	@Override
	public void updateArticle(Article article) {
		Article artcl = getArticleById(article.getArticleId());
		artcl.setTitle(article.getTitle());
		artcl.setCategory(article.getCategory());
		entityManager.flush();
	}
	@Override
	public void deleteArticle(int articleId) {
		entityManager.remove(getArticleById(articleId));
	}
	@Override
	public boolean articleExists(String title, String category) {
		String hql = "FROM Article as atcl WHERE atcl.title = ? and atcl.category = ?";
		int count = entityManager.createQuery(hql).setParameter(1, title)
		              .setParameter(2, category).getResultList().size();
		return count > 0 ? true : false;
	}
} 

10. Create Service

Find the service interface and its implementation.
IArticleService.java
public interface IArticleService {
     List<Article> getAllArticles();
     Article getArticleById(int articleId);
     boolean addArticle(Article article);
     void updateArticle(Article article);
     void deleteArticle(int articleId);
} 
ArticleService.java
@Service
public class ArticleService implements IArticleService {
	@Autowired
	private IArticleDAO articleDAO;
	@Override
	public Article getArticleById(int articleId) {
		Article obj = articleDAO.getArticleById(articleId);
		return obj;
	}	
	@Override
	public List<Article> getAllArticles(){
		return articleDAO.getAllArticles();
	}
	@Override
	public synchronized boolean addArticle(Article article){
                if (articleDAO.articleExists(article.getTitle(), article.getCategory())) {
    	           return false;
                } else {
    	           articleDAO.addArticle(article);
    	           return true;
                }
	}
	@Override
	public void updateArticle(Article article) {
		articleDAO.updateArticle(article);
	}
	@Override
	public void deleteArticle(int articleId) {
		articleDAO.deleteArticle(articleId);
	}
} 

11. Create Main using SpringApplication

Create a class with main() method that will call SpringApplication.run() to run the application. We need to annotate it with @SpringBootApplication.
ApplicationStarter.java
@SpringBootApplication
public class ApplicationStarter {  
	public static void main(String[] args) {
		SpringApplication.run(ApplicationStarter.class, args);
        }       
} 

Create Jersey Client

Here we will create Jersey client. We will perform CRUD operation. We will create methods for create, read, update and delete operation.
JerseyClient.java
public class JerseyClient {
	public void getArticleDetails() {
		Client client = ClientBuilder.newClient();
		WebTarget base = client.target("http://localhost:8080/spring-app/article");
		WebTarget details = base.path("details");
		List<Article> list = details.request(MediaType.APPLICATION_JSON)
				.get(new GenericType<List<Article>>() {});
		
	        list.stream().forEach(article -> 
	        System.out.println(article.getArticleId()+", "+ article.getTitle()+", "+ article.getCategory()));
	    
	        client.close();
	}
	public void getArticleById(int articleId) {
		Client client = ClientBuilder.newClient();
		WebTarget base = client.target("http://localhost:8080/spring-app/article");
		WebTarget articleById = base.path("{id}").resolveTemplate("id", articleId);
		Article article = articleById.request(MediaType.APPLICATION_JSON)
				.get(Article.class);
		
                System.out.println(article.getArticleId()+", "+ article.getTitle()+", "+ article.getCategory());
        
	        client.close();
	}
	public void addArticle(Article article) {
		Client client = ClientBuilder.newClient();
		WebTarget base = client.target("http://localhost:8080/spring-app/article");
		WebTarget add = base.path("add");
		Response response = add.request(MediaType.APPLICATION_JSON)
				.post(Entity.json(article));
		
		System.out.println("Response Http Status: "+ response.getStatus());
                System.out.println(response.getLocation());
        
	        client.close();
	}
	public void updateArticle(Article article) {
		Client client = ClientBuilder.newClient();
		WebTarget base = client.target("http://localhost:8080/spring-app/article");
		WebTarget update = base.path("update");
		Response response = update.request(MediaType.APPLICATION_JSON)
				.put(Entity.json(article));
		
		System.out.println("Response Http Status: "+ response.getStatus());
		Article resArticle = response.readEntity(Article.class);
		System.out.println(resArticle.getArticleId()+", "+ resArticle.getTitle()+", "+ resArticle.getCategory());
        
	        client.close();
	}
	public void deleteArticle(int articleId) {
		Client client = ClientBuilder.newClient();
		WebTarget base = client.target("http://localhost:8080/spring-app/article");
		WebTarget deleteById = base.path("{id}").resolveTemplate("id", articleId);
		Response response = deleteById.request(MediaType.APPLICATION_JSON)
				.delete();
		
		System.out.println("Response Http Status: "+ response.getStatus());
		if(response.getStatus() == 204) {
			System.out.println("Data deleted successfully.");
		}
        
	        client.close();
	}	
	public static void main(String[] args) {
		JerseyClient jerseyClient = new JerseyClient();
	        jerseyClient.getArticleDetails();
		//jerseyClient.getArticleById(102);
		
		Article article = new Article();
		article.setTitle("Spring REST Security using Hibernate2");
		article.setCategory("Spring"); 
		//jerseyClient.addArticle(article);
		
		article.setArticleId(105);
		//jerseyClient.updateArticle(article);
		
		//jerseyClient.deleteArticle(105);
	}
} 

Run Application

To run the application, first create table in MySQL as given in the example. Now we can run REST web service in following ways.
1. Using Eclipse: Download the project source code using the download link given at the end of page. Import the project into eclipse. Using command prompt, go to the root folder of the project and run.
mvn clean eclipse:eclipse 
and then refresh the project in eclipse. Run Main class ApplicationStarter by clicking Run as -> Java Application. Tomcat server will be started.

2. Using Maven Command: Download the project source code. Go to the root folder of the project using command prompt and run the command.
mvn spring-boot:run 
Tomcat server will be started.

3. Using Executable JAR: Using command prompt, go to the root folder of the project and run the command.
mvn clean package 
We will get executable JAR spring-boot-demo-0.0.1-SNAPSHOT.jar in target folder. Run this JAR as
java -jar target/spring-boot-demo-0.0.1-SNAPSHOT.jar 
Tomcat server will be started.

Now we are ready to test the application. To run client, go to the JerseyClient class in eclipse and click on Run as -> Java Application.
We can also test application using Postman. Find the print screen.
Spring Boot + Jersey REST + JPA + Hibernate CRUD Example


I am done now. Happy Spring Boot and Jersey learning!

References

JAX-RS and Jersey
Package javax.ws.rs
Advanced Features of the Client API

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us