Akashic Records

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

오래된글/Articles

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

Andrew's Akashic Records 2018. 4. 19. 14:07
728x90

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

원본 문서: http://www.javacodegeeks.com/2011/01/restful-web-services-with-resteasy-jax.html


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

  • http://localhost:8080/resteasy/restful-services/sampleservice/employee/1

  • http://localhost:8080/resteasy/restful-services/sampleservice/json/employees

  • http://localhost:8080/resteasy/restful-services/sampleservice/json/employee/1


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.


728x90

'오래된글 > Articles' 카테고리의 다른 글

Introduction to servlet 3 new features  (0) 2018.04.19
SIP Servlet  (0) 2018.04.19
Introduction of JDK 7  (0) 2018.04.19
Inspecting HotSpot JVM Options  (0) 2018.04.19
Three Common Application Performance Challenges for Developers  (0) 2018.04.19
Comments