Welcome to RESTEasy Tutorial. RESTEasy is the JAX-RS implementation provided by JBoss project. We can use RESTEasy to create restful web services. RESTEasy provides tighter integration with the JBoss Application Server but we can deploy it on any servlet container. So today we will learn how to create restful web service using RestEasy framework and deploy in Tomcat servlet container to test it.
Table of Contents
RESTEasy Tutorial
Some of the features of RESTEasy framework are:
- JAX-RS API compliant – so mostly you need to plug it with JAX-RS API coding to create rest web services.
- Runs on almost any servlet container supporting Java 6 or higher
- Provides support for writing client programs using JAX-RS 2.0 client API. We will look into test program too.
- Rich set of providers – XML, JSON, YAML, Multipart, XOP, Atom, etc.
- OAuth2 and Distributed SSO with JBoss AS7
- EJB, Seam, Guice, Spring, and Spring MVC integration
RESTEasy Example
Below are the URIs we will be exposing in our RestEasy web service implementation.
URI | HTTP Method | Description |
---|---|---|
/employee/add | POST | Add an employee |
/employee/getDummy | GET | returns a dummy employee object |
/employee/{id}/get | GET | Get the employee with ‘id’ in the URI |
/employee/getAll | GET | Get all employees |
/employee/{id}/delete | DELETE | Delete employee with ‘id’ in the URI |
RESTEasy Example Eclipse Project
Below image shows our final project structure. First of all create a “Dynamic Web Application” in Eclipse and then convert it to Maven project to get the web project skeleton.
Let’s look into different components of our RestEasy web services project.
RESTEasy Example Model Classes
We have two model classes – Employee
for employee object and GenericResponse
for sending client response object for status, message and error code.
package com.journaldev.jaxrs.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "employee")
public class Employee {
private String name;
private double salary;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return id + "::" + name + "::" + salary;
}
}
package com.journaldev.jaxrs.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "response")
public class GenericResponse {
private boolean status;
private String message;
private String errorCode;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
@Override
public String toString() {
return status + "|" + message + "|" + errorCode;
}
}
Notice that both the java beans are annotated with @XmlRootElement
annotation. This is required for JAXB API for converting objects to xml and vice versa.
RESTEasy Example Service Classes
Let’s first create an interface for all the operations we are trying to expose in our restful web service.
package com.journaldev.jaxrs.service;
import javax.ws.rs.core.Response;
import com.journaldev.jaxrs.model.Employee;
public interface EmployeeService {
public Response addEmployee(Employee e);
public Response deleteEmployee(int id);
public Employee getEmployee(int id);
public Employee[] getAllEmployees();
}
Below is the implementation class for above employee service interface.
package com.journaldev.jaxrs.service;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.journaldev.jaxrs.model.Employee;
import com.journaldev.jaxrs.model.GenericResponse;
@Path("/employee")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public class EmployeeServiceImpl implements EmployeeService {
private static Map<Integer,Employee> emps = new HashMap<Integer,Employee>();
@Override
@POST
@Path("/add")
public Response addEmployee(Employee e) {
GenericResponse response = new GenericResponse();
if(emps.get(e.getId()) != null){
response.setStatus(false);
response.setMessage("Employee Already Exists");
response.setErrorCode("EC-01");
return Response.status(422).entity(response).build();
}
emps.put(e.getId(), e);
response.setStatus(true);
response.setMessage("Employee created successfully");
return Response.ok(response).build();
}
@Override
@DELETE
@Path("/{id}/delete")
public Response deleteEmployee(@PathParam("id") int id) {
GenericResponse response = new GenericResponse();
if(emps.get(id) == null){
response.setStatus(false);
response.setMessage("Employee Doesn't Exists");
response.setErrorCode("EC-02");
return Response.status(404).entity(response).build();
}
emps.remove(id);
response.setStatus(true);
response.setMessage("Employee deleted successfully");
return Response.ok(response).build();
}
@Override
@GET
@Path("/{id}/get")
public Employee getEmployee(@PathParam("id") int id) {
return emps.get(id);
}
@GET
@Path("/{id}/getDummy")
public Employee getDummyEmployee(@PathParam("id") int id) {
Employee e = new Employee();
e.setSalary(8976.55);
e.setName("Dummy");
e.setId(id);
return e;
}
@Override
@GET
@Path("/getAll")
public Employee[] getAllEmployees() {
Set<Integer> ids = emps.keySet();
Employee[] e = new Employee[ids.size()];
int i=0;
for(Integer id : ids){
e[i] = emps.get(id);
i++;
}
return e;
}
}
We are using only JAX-RS API annotations such as @Path
, @PathParam
, Response
etc. Notice that I am using javax.ws.rs.core.Response
as response object in some of the methods where I want to send the HTTP status code other than 200.
RESTEasy Configuration
We haven’t done anything related to RestEasy till now, it’s time now to configure RestEasy as our JAX-RS API implementation for our restful web service.
First step is to extend javax.ws.rs.core.Application
class and override few of the methods to inject our service class implementation.
package com.journaldev.jaxrs.resteasy.app;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
import com.journaldev.jaxrs.service.EmployeeServiceImpl;
public class EmployeeApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
public EmployeeApplication() {
singletons.add(new EmployeeServiceImpl());
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
Next step is to add RESTEasy maven dependencies in our pom.xml file as shown below.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.13.Final</version>
</dependency>
<!-- Below dependency is for JAXB integration -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.13.Final</version>
</dependency>
Final step is to configure RESTEasy servlet class in deployment descriptor as front controller.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="https://xmlns.jcp.org/xml/ns/javaee https://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>RestEasy-Example</display-name>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.journaldev.jaxrs.resteasy.app.EmployeeApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
That’s it. Our web service is ready. You can see how easy it was to plugin RestEasy as our JAX-RS implementation for restful web service.
RESTEasy Restful Web Service Test
I am using Chrome Postman extension to perform testing for our web service. Below are some of the test cases with response.
One common part is the headers in all the requests as shown in below image.
As you can see that all the tests passed with flying colors, our rest web service is working fine.
RestEasy Client – ResteasyClient example
As I mentioned earlier, RestEasy also provides API for testing rest web services through java program. For this we need to add another dependency in our pom.xml file.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.0.13.Final</version>
</dependency>
Below is the simple test program where I am testing our web service programmatically. I am providing example for testing GET, POST and DELETE HTTP methods.
package com.journaldev.jaxrs.resteasy.client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import com.journaldev.jaxrs.model.Employee;
import com.journaldev.jaxrs.model.GenericResponse;
public class RestEasyTestClient {
public static void main(String[] args) {
ResteasyClient client = new ResteasyClientBuilder().build();
//GET example
ResteasyWebTarget getDummy = client.target("https://localhost:8080/RestEasy-Example/employee/99/getDummy");
Response getDummyResponse = getDummy.request().get();
String value = getDummyResponse.readEntity(String.class);
System.out.println(value);
getDummyResponse.close();
//POST example
ResteasyWebTarget add = client.target("https://localhost:8080/RestEasy-Example/employee/add");
Employee emp = new Employee();
emp.setId(50);emp.setName("Rick");emp.setSalary(1000);
Response addResponse = add.request().post(Entity.entity(emp, MediaType.APPLICATION_XML));
System.out.println(addResponse.readEntity(GenericResponse.class));
System.out.println("HTTP Response Code:"+addResponse.getStatus());
addResponse.close();
addResponse = add.request().post(Entity.entity(emp, MediaType.APPLICATION_XML));
System.out.println(addResponse.readEntity(GenericResponse.class));
System.out.println("HTTP Response Code:"+addResponse.getStatus());
addResponse.close();
//DELETE example
ResteasyWebTarget delete = client.target("https://localhost:8080/RestEasy-Example/employee/50/delete");
Response deleteResponse = delete.request().delete();
System.out.println(deleteResponse.readEntity(GenericResponse.class));
System.out.println("HTTP Response Code:"+deleteResponse.getStatus());
deleteResponse.close();
deleteResponse = delete.request().delete();
System.out.println(deleteResponse.readEntity(GenericResponse.class));
System.out.println("HTTP Response Code:"+deleteResponse.getStatus());
deleteResponse.close();
}
}
Below is the output produced by above RESTEasy client program.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee><id>99</id><name>Dummy</name><salary>8976.55</salary></employee>
true|Employee created successfully|null
HTTP Response Code:200
false|Employee Already Exists|EC-01
HTTP Response Code:422
true|Employee deleted successfully|null
HTTP Response Code:200
false|Employee Doesn't Exists|EC-02
HTTP Response Code:404
That’s all for RESTEasy tutorial. RESTEasy is a very easy framework for creating Restful web services in java easily. You can download the project from below link and play around with it to learn more.
getting this exception javax.ws.rs.NotFoundException: RESTEASY003210: Could not find resource for full path:
http://localhost:8080/****
I am also getting same error, kindly help
Hi Pankaj, what I have to change in Employee class, or in the WS Clients that use it to ‘send’ the JSon Object without the root element ‘Employee’?
I mean directly:
{
“id”: 8877,
“name”: “kkkk”,
“salary”: 999.87
}
… instead of:
{
“employee” :
{
“id”: 8877,
“name”: “kkkk”,
“salary”: 999.87
}
}
Thanks and congrats!!
Hi thanks for the tutorial. I am trying to run this tutorial on IBM Websphere 8.5.5. I can see the servlet successfully initiated however i still encounter error code 404.
Hello Pankaj,
can you please guide me for writing spring integration tests for the above application
we have the same application like above i’m trying to write the spring integration test cases using SpringJUnit4ClassRunner. I’m confused can you please guide me here for the approach
Thanks
Vijay Bhaskar
Hello,
I have one doubt, In client side code, how to get Employee class reference in client side.
In above client example.
—————————————————————————————————–
public class RestEasyTestClient {
public static void main(String[] args) {
//some code
//POST example
ResteasyWebTarget add = client.target(“https://localhost:8080/RestEasy
Example/employee/add”);
Line 20: Employee emp = new Employee(); // how to find Employeee class
emp.setId(50);emp.setName(“Rick”);emp.setSalary(1000);
Response addResponse = add.request().post(Entity.entity(emp,
//some code
}
}
———————————————————————————————————–
I am not getting that, how to get employee class in client section. which i am mention in line 20.
Usually, the API provides the model classes to the client programs as a separate JAR.
Thanks for the tutorial, it has been so helpful and concise. I am getting 404 when running the tomcat container
Type: Status report
message: /RestEasy-Example/
description The requested resource is not available
The logs are not including any error:
INFO: Deployment of web application directory C:\Program Files\Apache Software Foundation\Tomcat 8.0.36\webapps\ROOT has finished in 30 ms
May 08, 2019 2:55:41 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [“http-nio-8080”]
May 08, 2019 2:55:41 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [“ajp-nio-8009”]
May 08, 2019 2:55:41 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 2274 ms
Web.xml is as follows:
RestEasy Example
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
resteasy-servlet
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
javax.ws.rs.Application
com.bc.ca.resteasy.app.EmployeeApplication
resteasy-servlet
/service/*
resteasy.servlet.mapping.prefix
/service
Do you know what can be wrong in the configuration?
Thanks a lot
Hi Pankaj,
Sorry for the earlier comment. import javax.ws.rs.core.Response is resolving. I left out the core part.
Great tutorial.
Thanks
Hi Pankaj,
I try this and I get an error. import javax.ws.rs.Response; is not able to be resolved. I downloaded a jar file, added it to the class build path, but I still get an error. What am I missing?
nice RESTEasy introduction example, however your application is not RESTfull. The goal of the REST Architecture, is to expose resources and not fonctionalities. Having a look on your URIs, /employee/add, /employee/getDummy, /employee/{id}/get , etc., you are exposing functionalities. According to the purpose of the REST architecture, you should rather expose resources. For exemple to ADD an employee you could do this by defining an URI like this: /employee/{id}. Getting the Employee should occur by mapping the addEmployee()-Method to the HTTP GET-fonction like you do over the annotation @GET. Addressable resources, a uniform constrained interface, e.g. Use a small set of well-defined methods to manipulate your resources, this is what the REST Architecture specifies.
You are right, I kept the URIs like this to avoid any confusion around its purpose.
Hello.
For people who, like me, are a bit young to Maven/Java/Eclipse/Tomcat etc…
I was missing one step in my configuration: telling Tomcat about the Maven libraries.
This is done as follow:
– right-click on project and choose “Properties”,
– click on “Deployment Assembly”, then click on “Add” button
– select “Java Build Path Entries” and click on “Next” button
– selecting “Maven Dependencies” and clicking on “Finish” button
This, way, all Maven libraries become visible to Tomcat!
Many things for that well-designed tutorial,
Daniel
Hi,
Can we have more than one application classes for two different URL paths? I have added two application classes, it does not give any error as such, but when we make a request, it gives a 404 for any API calls for both the Applications.
@Path(“/employee”)
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public class EmployeeServiceImpl implements EmployeeService {
can we produce multiple media type in @Produces like JASON, XML,HTML etc?
yes sure you can provide multiple value for Produces annotation. Refer this
https://docs.oracle.com/cd/E19798-01/821-1841/gipzh/index.html
Is there a way to validate with annotations the parameters in the post request ?
Something like
@NotNull
@NonEmpty
@Size (min=2, max=5)
IF so, which jars should be added
Good article Pankaj!
I have a couple of questions –
1) Can we have multiple @Post methods in the same service implementation class?
2) Can we have multiple application classes (like the EmployeeApplication in this example) in the same war, each serving a different URL pattern? So in your case the end point is something like
https://.localhost:8080/RestEasy-Example/employee/add
. Can we have another application class catering to HR in same war with URLhttps://localhost:8080/RestEasy-Example/HR/add
?1. Yes, you can have as many GET, POST methods in a same service. @Path should be defined properly so that there is no ambiguity.
2. Yes, you can have a class
HRServiceImpl
like below.Nice blog Pankaj. I have successfully run “https://localhost:8080/RestEasy-Example/employee/99/getDummy” using your project. However when I POST “https://localhost:8080/RestEasy-Example/employee/add”, I get 415 Unsupported Media Type” error. I am using Postman. I tried adding “content-type” = “application/xml” to the header. Bu then the error changed to “400 Bad Request”.
Thanks in advance for your help
Put XML in capital letters like “application/XML”
Getting this error when running on tomcat in eclipse:
java.lang.RuntimeException: java.lang.ClassNotFoundException: com.journaldev.jaxrs.resteasy.app.EmployeeApplication
at org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:292)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:95)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1269)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1182)
at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:853)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1083)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:640)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.journaldev.jaxrs.resteasy.app.EmployeeApplication
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1892)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1735)
at org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:288)
… 20 more
Oct 25, 2016 7:14:12 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Allocate exception for servlet resteasy-servlet
java.lang.ClassNotFoundException: com.journaldev.jaxrs.resteasy.app.EmployeeApplication
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1892)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1735)
at org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:288)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:95)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1269)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1182)
at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:853)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1083)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:640)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
you must configure the employeeapplication code as
package ntg.resteasy.App;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import ntg.resteasy.Service.ResourceService;
@ApplicationPath(value = “/service”)
public class EmployeeApplication extends Application {
private Set singletons = new HashSet();
public EmployeeApplication() {
singletons.add(new ResourceService());
}
@Override
public Set getSingletons() {
return singletons;
}
}
and the web.xml like
RESTEasyImplement
resteasy.servlet.mapping.prefix
/service
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
resteasy-servlet
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
javax.ws.rs.Application
ntg.resteasy.App.EmployeeApplication
resteasy-servlet
/service/*
Thanks. This is very helpful.
I added the below dependency to use json instead of xml, and changed consume and produce annotations to MediaType.APPLICATION_JSON.
org.jboss.resteasy
resteasy-jackson-provider
3.0.9.Final
Very good article Pankaj ! Congratualtions !
I just noticed from the RestEasy 3.0.13 documentation, that, if you include the following dependency :
org.jboss.resteasy
resteasy-servlet-initializer
3.0.13.Final
instead of the regular reseasy-jaxrs – Then, the web.xml can be left empty completely !
No, manual configuration of the RestEasy dispatcher servlet is necessary.
Only, the EmployeeApplication.java needs to be annotated with an @ApplicationPath(” “) annotation and the war assembly is automatically scanned by RestEasy.