Enterprise Java

RESTful Web Services with RESTeasy JAX-RS on Tomcat 7 – Eclipse and Maven project

The RESTful approach of developing web services is constantly gaining more and more attention and seems to be pushing SOAP into deprecation. I am not going to discuss which approach is better, but I believe that we all agree that REST is much more lightweight. In this tutorial, I am going to show you how to develop RESTful services with RESTeasy and how to deploy them on a Tomcat server. An Eclipse based Maven project is also created along the way.

I recently wanted to test a REST client that I planned to use into an application I am building, so I needed a quick way to set up a RESTful infrastructure. Justin has written a cool guide on how to use Spring for providing RESTful services. However, I wanted something faster and not to mess with Spring for once. For that reason, I decided to go with JBoss RESTeasy. From the official site:

RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is a fully certified and portable implementation of the JAX-RS specification. JAX-RS is a new JCP specification that provides a Java API for RESTful Web Services over the HTTP protocol.
RESTEasy can run in any Servlet container, but tighter integration with the JBoss Application Server is also available to make the user experience nicer in that environment.

The latest RESTeasy version can be found here and the relevant documentation here. At the time being, the latest version is the 2.1.0.GA. You will also probably need the RESTeasy JavaDoc and the JAX-RS JavaDoc.

Since using JBoss AS would cancel the whole “lightweight” concept, I decided to go with our old friend Tomcat. I downloaded the latest version (7.0.5 beta) of the beloved servlet container from here. Note that at the time being this is in beta phase, but Tomcat has been proven very robust and I think no problems should occur.

Let’s start by creating an Eclipse based Maven project under the name “RESTeasyProject”. The archetype used is “webapp-jee5” as shown in the following image:

For the parameters, we use “com.javacodegeeks” as group Id and “resteasy” as artifact Id.

The next step is to add the RESTeasy dependencies in our pom.xml file. The repository URL is http://repository.jboss.org/maven2/ but unfortunately the latest version provided in that repository is 2.0-beta-2. Anyway, the lines that we need to add to the maven file are:

…
<repositories>
 <repository>
       <id>org.jboss.resteasy</id>
  <url>http://repository.jboss.org/maven2/</url>
 </repository>
</repositories>
…
<dependencies>
…
<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>2.0-beta-2</version>
</dependency>
<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxb-provider</artifactId>
 <version>2.0-beta-2</version>
</dependency>
<dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jettison-provider</artifactId>
      <version>2.0-beta-2</version>
</dependency>
…
</dependencies>
…

The resteasy-jaxrs artifact refers to the core RESTeasy class for the JAX-RS implementation. Additionally, we use both resteasy-jaxb-provider and resteasy-jettison-provider, since we want to support both XML and JSON response formats. The JAXB architecture is used for the XML serializations and the Jettison framework for writing JSON.

Here is the full 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.javacodegeeks</groupId>
  <artifactId>resteasy</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>

  <name>resteasy JEE5 Webapp</name>
  <url>http://maven.apache.org</url>
  
  <repositories>
    <repository>
      <id>org.jboss.resteasy</id>
      <url>http://repository.jboss.org/maven2/</url>
    </repository>
    </repositories>

  <dependencies>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jaxrs</artifactId>
        <version>2.0-beta-2</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jaxb-provider</artifactId>
        <version>2.0-beta-2</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jettison-provider</artifactId>
        <version>2.0-beta-2</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
    <finalName>resteasy</finalName>
  </build>
</project>

Let’s first see the model class that will be used in our service:

package com.javacodegeeks.resteasy.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "employee")
public class Employee {
    
    private String employeeId;
    private String employeeName;
    private String job;
    
    @XmlElement
    public String getEmployeeId() {
        return employeeId;
    }
    
    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }
    
    @XmlElement
    public String getEmployeeName() {
        return employeeName;
    }
    
    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
    
    @XmlElement
    public String getJob() {
        return job;
    }
    
    public void setJob(String job) {
        this.job = job;
    }
    
}

Typical model class with some fields and the respective getters/setters. The JAXB annotations are used in order to indicate which fields will be serialized and to what elements they will be mapped to. Mores specifically, the XmlRootElement annotation is used in order to indicate a top-level class element. Similarly, the getters are annotated with XmlElement in order to map the respective JavaBean property to a XML element. By default the property name is used, but we can override that by providing a custom name.

Let’s create now our first RESTeasy enabled class, named “SampleService”.

package com.javacodegeeks.resteasy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import com.javacodegeeks.resteasy.model.Employee;

@Path("/sampleservice")
public class SampleService {
    
    private static Map<String, Employee> employees = new HashMap<String, Employee>();
    
    static {
        
        Employee employee1 = new Employee();
        employee1.setEmployeeId("1");
        employee1.setEmployeeName("Fabrizio");
        employee1.setJob("Software Engineer");
        employees.put(employee1.getEmployeeId(), employee1);
        
        Employee employee2 = new Employee();
        employee2.setEmployeeId("2");
        employee2.setEmployeeName("Justin");
        employee2.setJob("Business Analyst");
        employees.put(employee2.getEmployeeId(), employee2);
        
    }

    @GET
    @Path("/hello")
    @Produces("text/plain")
    public String hello(){
        return "Hello World";    
    }
    
    @GET
    @Path("/echo/{message}")
    @Produces("text/plain")
    public String echo(@PathParam("message")String message){
        return message;    
    }
    
    @GET
    @Path("/employees")
    @Produces("application/xml")
    public List<Employee> listEmployees(){
        return new ArrayList<Employee>(employees.values());
    }
    
    @GET
    @Path("/employee/{employeeid}")
    @Produces("application/xml")
    public Employee getEmployee(@PathParam("employeeid")String employeeId){
        return employees.get(employeeId);        
    }
    
    @GET
    @Path("/json/employees/")
    @Produces("application/json")
    public List<Employee> listEmployeesJSON(){
        return new ArrayList<Employee>(employees.values());
    }

    @GET
    @Path("/json/employee/{employeeid}")
    @Produces("application/json")
    public Employee getEmployeeJSON(@PathParam("employeeid")String employeeId){
        return employees.get(employeeId);        
    }
    
}

As you can see, our service is heavily annotated. We can define declaratively the HTTP method that each method responds to, for example GET or POST. For the URL under which resources are served, we use PATH, both in service and method level. If a method accepts a parameter or falls under a specific path segment, that is denoted by PathParam. Finally, the Consumes annotation defines the media types that the methods of a resource can accept and Produces defines the types that can be produced.

Let’s examine some more details. We store some mock data on a static map, which on a real application would be replaced by a DAO. Note that the model classes will be serialized by the library both for XML and JSON representations. Let’s see now the exposed methods:

  • hello: A method that simply prints a predefined string with text/plain content type.
  • echo: This methods returns whatever argument is provided. Note that the field name inside the brackets must match the parameter name. Similar content type with above.
  • listEmployees: This methods provides an XML representation of a list of model objects. The format is indicated by the Produces annotation.
  • getEmployee: Same with previous but returns only one model object based on the ID argument.
  • listEmployeesJSON: Similar to the XML counterpart, but the produced format is JSON.
  • getEmployeeJSON: Same with previous but returns only one model object based on the ID argument.

Next we have to setup our web apps’ web.xml file accordingly so that RESTeasy takes care of the incoming REST requests. In our Maven project, this can be found under the “src/main/webapp/WEB-INF” directory. Here what our declaration file looks like:

<?xml version="1.0" encoding="UTF-8"?>

<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>resteasy</display-name>

    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>Resteasy</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Resteasy</servlet-name>
        <url-pattern>/restful-services/*</url-pattern>
    </servlet-mapping>
    
    <context-param>
         <param-name>resteasy.scan</param-name>
        <param-value>true</param-value>    
    </context-param>

    
    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/restful-services</param-value>
    </context-param>
    
</web-app>

The ResteasyBootstrap context listener has to be deployed in order to create the registry for resteasy ,while the HttpServletDispatcher servlet is used so that incoming requests are correctly routed to the appropriate services. We have configured the specific servlet, named “Resteasy”, to intercept requests under the “/restful-services/*” path. We need to define that to the RESTeasy framework by using the resteasy.servlet.mapping.prefix configuration option. Note that the value does not containing the trailing slash nor the wildcard. Finally, we use the resteasy.scan switch to automatically scan WEB-INF/lib jars and WEB-INF/classes directory for the various annotated classes. There are also a number of other RESTeasy configuration switches you can use in order to fine tune your application’s behavior.

The final step is to build the project and deploy to the servler container. Run the Eclipse configuration and choose “Maven install”. Assuming everything is fine, this will generate a web archive named “resteasy.war” under the “target” folder of your project. The exploded folder can also be found in the same directory. Copy the WAR file to Tomcat’s application repository, i.e. the “apache-tomcat-7.0.5\webapps” folder. Start the server if you do not have already done so and you should see the following line at the console:

Adding scanned resource: com.javacodegeeks.resteasy.SampleService

Now that the application is deployed, let’s test it. Note that, since all methods handle GET requests, testing can be performed by using your favorite browser and just writing the URL. For the lazy developer, here they are (in the XML and JSON cases I also add a public link with the expected result):

http://localhost:8080/resteasy/restful-services/sampleservice/hello

http://localhost:8080/resteasy/restful-services/sampleservice/echo/message

http://localhost:8080/resteasy/restful-services/sampleservice/employees
(link)

http://localhost:8080/resteasy/restful-services/sampleservice/employee/1
(link)

http://localhost:8080/resteasy/restful-services/sampleservice/json/employees
(link)

http://localhost:8080/resteasy/restful-services/sampleservice/json/employee/1
(link)

Note that the full path consists of the web application context (“resteasy”), the context we defined RESTeasy to handle (“restful-services”), the service path (“sampleservice”) and finally the corresponding method path.

That would be all. As always, you can find the Eclipse project here.

Related Articles :

Ilias Tsagklis

Ilias is a software developer turned online entrepreneur. He is co-founder and Executive Editor at Java Code Geeks.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

17 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
andrew d
12 years ago

can you make an oauth 2 legged tutorial

lilyju
lilyju
12 years ago

This sample is the best

Rick O'Shay
11 years ago

Tomcat 7 runs the Java EE web-profile so JAX-RS should be build right in. 

Marco Antonio
Marco Antonio
10 years ago

Nice work. Thanks for the RESTEasy sample, I have successfully deployed in Tomcat 7 with eclipse. As I am working in a project that uses RESIN, I tried to deploy it under Resin, I got the following error when going to the urls:

404 Could not find resource for relative : /RESTfulExample/restful-services/sampleservice/hello of full path: http://localhost:8080/RESTfulExample/restful-services/sampleservice/hello/

If anyone has some clue on this or can helpo me to fix the issue, it would be greatly apreciated.

Manav
Manav
9 years ago
Reply to  Marco Antonio

Add the below in web.xml

resteasy.resources
com.javacodegeeks.resteasy.EmployeeService

Mike
Mike
9 years ago
Reply to  Manav

Could you enplane more clear what exactly need to add to web.xml to solve the problem

para-bola
para-bola
7 years ago
Reply to  Marco Antonio

The issue is with the URL. See: http://stackoverflow.com/questions/9419270/404-response-on-simple-resteasy-example-with-tomcat
The first part is the project name as it is installed by Maven. Should be the name of the .war file.
In my case it was:
http://localhost:8080/resteasy-0.0.1-SNAPSHOT/restful-services/sampleservice/hello

Marco Antonio
Marco Antonio
10 years ago

Yes you can, I just deployed the example in Tomcat7 and eclipse.

Fred
Fred
10 years ago

Hi My maven install is failing with followinf error:
Downloading: http://repository.jboss.org/maven2/org/scannotation/scannotation/1.0.2/scannotation-1.0.2.pom
[INFO] ————————————————————————
[INFO] BUILD FAILURE
[INFO] ————————————————————————
[INFO] Total time: 3.036s
[INFO] Finished at: Fri Aug 09 15:19:08 CST 2013
[INFO] Final Memory: 4M/15M
[INFO] ————————————————————————
[ERROR] Failed to execute goal on project resteasy: Could not resolve dependencies for project com.javacodegeeks:resteasy:war:0.0.1-SNAPSHOT: Failed to collect dependencies for [javax.servlet:servlet-api:jar:2.5 (provided), javax.servlet.jsp:jsp-api:jar:2.1 (provided), junit:junit:jar:3.8.1 (test), org.jboss.resteasy:resteasy-jaxrs:jar:2.0-beta-2 (compile), org.jboss.resteasy:resteasy-jaxb-provider:jar:2.0-beta-2 (compile), org.jboss.resteasy:resteasy-jettison-provider:jar:2.0-beta-2 (compile)]: Failed to read artifact descriptor for org.scannotation:scannotation:jar:1.0.2: Could not transfer artifact org.scannotation:scannotation:pom:1.0.2 from/to org.jboss.resteasy (http://repository.jboss.org/maven2/): Access denied to http://repository.jboss.org/maven2/org/scannotation/scannotation/1.0.2/scannotation-1.0.2.pom. Error code 403, Forbidden -> [Help 1]

Majid
Majid
10 years ago

Hi,
Thank you for this tutorial,
I imported it in eclipse, but I got this error :

Description Resource Path Location Type
The container ‘Maven Dependencies’ references non existing library ‘/Users/majid/.m2/repository/org/jboss/resteasy/resteasy-jaxrs/2.0-beta-2/resteasy-jaxrs-2.0-beta-2.jar’ resteasy Build path Build Path Problem

Thanks, your help is appreciated.

amir
amir
10 years ago

Hello

My war file (resteasy.war) contains class files, lib and web.xml. I am trying to deploy the war on Apache Tomcat 7. But I get this error during deploying the war file:
Error listenerStart.

Thank you for any help.

Thai Students from SIIT Thammasat University
Thai Students from SIIT Thammasat University
9 years ago

Thank you so much

trxtr
trxtr
9 years ago

So, for me this resteasy.scan option is not working. So I’ll do my own reading into how that works.

If you cannot locate services with test url’s. Then possibly the scan option is not working for you and using the context property
resteasy.resources
will fix the issue by loading the class for that specified value.

Neha
Neha
8 years ago

Could you please provide a link to download source code? I am not able to download it successfully.

shalu
shalu
7 years ago

can we do it without maven.Simply creating one java file ,web.xml and including jars like resteasy,jaxrs and javaassist

Jorge Reyes-Spindola
7 years ago

This tutorial needs an update. The archetype “webapp-jee5” doesn’t seem to exist anymore. I tried what I thought was a similar archetype ‘maven-archetype-webapp’ but it didn’t exactly work out. I used the author’s pom.xml with some modifications, the classes, and the web.xml. After building the maven tree structure by hand and putting everything in its place, the project built and ran. However, when trying the links, I got the message “Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/xml” Maybe I was expecting too much but it would’ve been nice to address the output issues.… Read more »

Jorge Reyes-Spindola
7 years ago

My apologies about my comment regarding the nonexistence of the ‘webapp-jee5’ archetype. It turns out that if you configure your Eclipse Maven Archetypes catalog with http://repo.maven.apache.org/maven2/archetype-catalog.xml you will get among the myriad other archetypes, the webapp-jee5 one.

Nonetheless, I still think we should update this site.

Thanks

Back to top button