Creating a REST Web Service in Java with Eclipse Neon, Tomcat 9, JAX-RS Jersey 2.24 and Jackson

In this example, the Eclipse Neon Java EE IDE and the Apache Tomcat 9 web server are used to create, deploy and test a REST web service using the Jersey JAX-RS API and Jackson Parser API.

Related Posts

Summary

  1. Setup of Jersey JAX-RS API User Library in Eclipse
  2. Configuration of Dynamic Web Project for REST Web Services Development
  3. Creating a REST Web Service in Eclipse with JAX-RS Annotations
  4. Deploying and Running the REST Web Service on Tomcat 9
  5. Testing the REST Web Service with Postman

Software Downloads

  • The open-source software used in this example can be downloaded from these sources:
Java JDK, Java IDE and Servlet Container
Eclipse Neon Java EE IDE from from the Eclipse Download Page
Java JDK version 1.8.0_111 from Oracle
Jersey JAX-RS API
Jersey JAX-RS reference implementation jaxrs-ri-2.24.zip from the Jersey homepage
Jersey Media JSON Jackson jersey-media-json-jackson-2.24.jar from the Maven Repository
Jackson JAX-RS Base 2.8.4: jackson-jaxrs-base-2.8.4.jar
Jackson JAX-RS JSON Provider 2.8.4: jackson-jaxrs-json-provider-2.8.4.jar
Jackson Annotations 2.8.4: jackson-annotations-2.8.4.jar
Jackson Module: JAX-B Annotations 2.8.4: jackson-module-jaxb-annotations-2.8.4.jar
Jackson Core 2.8.4: jackson-core-2.8.4.jar
Jackson Databind 2.8.4: jackson-databind-2.8.4.jar
REST Service Testing
Postman Google Chrome App from the Chrome store

A Dynamic Web Project in Eclipse is a project facet for developing Java web applications as defined in the Servlet Specification. It provides means to export the project to a web archive (WAR) file for deployment to a Servlet container such as Tomcat.

Jersey RESTful Web Services in Java - Download

  • Save the ZIP file and unzip it to some folder on the hard disk. Going forward, this folder is called Jersey API root. It contains the files shown below:

Jersey RESTful Web Services in Java - Installation folder

  • In Eclipse, navigate to Window > Preferences in the main menu and open Java > Build Path > User Libraries in the Preferences tree menu as shown below.
  • Click on the New button to setup a new user library for the Jersey REST API.

Eclipse - Java Build Path - Preferences - User Libraries

  • Enter a descriptive name for the user library and click on OK.

Eclipse - New User Library - Enter library name

  • The Jersey REST API is now listed under User Libraries. Highlight the library and click on Add External JARs.

Eclipse - Java Build Path - User Library JAX-RS-Jersey-API

  • Navigate to the Jersey API root folder and add the JAR file from the api folder.

Jersey RESTful Web Services in Java - javax.ws.rs-api-2.0.1.jar API file

  • Repeat the step and add all JAR files from the lib folder.

Jersey RESTful Web Services in Java - lib JAR files

  • Repeat the step again and add all JAR files from the ext folder.

Jersey RESTful Web Services in Java - ext JAR files

  • The imported JAR files will now be listed underneath the JAX-RS-Jersey-API library.

Jersey RESTful Web Services in Java - Eclipse user library

  • Click on OK to complete the new user library setup for Jersey JAX-RS development.

  • An existing dynamic web project in Eclipse needs to be configured properly in order to add support for the development of REST web services.
  • If you don’t have a dynamic web project setup, please refer to the below post for step-by-step instructions:
  • Open the project in the Eclipse Project Explorer and right click on its name and select Properties.
  • In the Project Properties dialog, navigate to Project Facets in the tree menu and make sure that the Dynamic Web Module version is set to 3.0 or higher.
  • Check the JAX-RS (REST Web Services) project facet checkbox and set the version to 2.0.

Eclipse - Project Facets - JAX-RS (REST Web Services)

  • Click on the link Further configuration available…
  • In the JAX-RS Capabilities dialog, select User Library from the JAX-RS Implementation Library dropdown and check the checkbox of the user library that contains the Jersey JAR files.
  • Check the checkbox for Include this library with this application.
  • Do not check the checkbox for Update Deployment Descriptor. This would cause Eclipse to generate a default web.xml file. In this example, only annotations are used.
  • Click on OK to close the dialog and then click OK again to close the project properties dialog.

Eclipse - Project Facets - JAX-RS Capabilities - Select JAX-RS Implementation Library

  • The JAX-RS-Jersey-API library will now be listed under the Libraries node in the Project Explorer.

Eclipse - Project Explorer - JAX-RS User Library in Java Resources

  • When expanding the JAX-RS-Jersey-API node, all JAR files in the library can be viewed.

Any new or existing plain old Java object (POJO) can be converted into a REST resource by using annotations form the Jersey JAX-RS API. By default, Jersey does not include a JSON parser and support for JSON marshaling/unmarshaling annotations (…conversion between Java beans and JSON and vice versa). A JSON parser API needs to be added as another user library to the dynamic web project.

  • Create another user library (e.g.: Jackson-Parser-API) as shown in section 2 and add the 7 JAR files.
  • Add the Jackson-Parser-API user library to the Java Build Path of the project as shown below.

Eclipse - Java Build Path - Jackson Parser API user library

For initializing Jersey and for providing package names that contain REST resources, a separate Java class needs to be created.

  • This class is annotated with the @ApplicationPath annotation which specifies the base URI (uniform resource identifier) for REST resources within a web application.
  • In this example, the base URI for REST resources within the SampleWebProject is:

https://localhost:8080/SampleWebProject/restservices/

The below Java code of the class MyRESTServices shows an example where the Jersey ResourceConfig class is used to create a new REST web service application as described in the Jersey user guide.

  • Jersey will use the JSON parser in com.fasterxml.jackson.jaxrs.json (Jackson-Parser-API library)
  • Jersey will look for JAX-RS annotated Java classes in the package com.pegaxchange.java.web.rest.
  • For more information on the annotations etc., please refer to the Jersey documentation.
package com.pegaxchange.java.web.rest;

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("restservices")
public class MyRESTServices extends ResourceConfig {

    public MyRESTServices() {
        packages("com.fasterxml.jackson.jaxrs.json");
        packages("com.pegaxchange.java.web.rest");
    }
}
  • A Java class can be annotated with the @Path annotation to expose it as a REST resource.
  • Public methods can be annotated with @GET and @POST to handle HTTP GET and POST requests.
  • The @Consumes and @Produces annotations can be used to specify request and response formats such as XML and JSON.

The below ProductCatalogResource Java class represents a product catalog that can be accessed through a REST interface. It exposes 3 methods:

  • The method searchByCategory handles HTTP GET calls to search the catalog by product category.
  • The category name is passed as a URI path parameter using the annotations:

@Path(search/category/{category}) and @PathParam("category")

  • The method searchByName searches the catalog by product name, which is passed as a query parameter using the  @QueryParam("name") annotation.
  • The method insert adds a product to the catalog. It receives a JSON encoded product from the HTTP POST request, inserts the product and returns a JSON string with a status message.
package com.pegaxchange.java.web.rest;

import java.util.*;

import javax.ws.rs.Consumes;
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.QueryParam;
import javax.ws.rs.core.MediaType;

import com.pegaxchange.java.bean.Product;
import com.pegaxchange.java.bean.Status;

@Path("productcatalog")
public class ProductCatalogResource {

    private static List productCatalog;

    public ProductCatalogResource() {
        initializeProductCatalog();
    }

    @GET
    @Path("search/category/{category}")
    @Produces(MediaType.APPLICATION_JSON)
    public Product[] searchByCategory(@PathParam("category") String category) {

        List products = new ArrayList();

        for (Product p : productCatalog) {
            if (category != null && category.equalsIgnoreCase(p.getCategory())) {
                products.add(p);
            }
        }

        return products.toArray(new Product[products.size()]);
    }
    
    @GET
    @Path("search")
    @Produces(MediaType.APPLICATION_JSON)
    public Product[] searchByName(@QueryParam("name") String name) {

        List products = new ArrayList();

        for (Product p : productCatalog) {
            if (name != null && name.toLowerCase().startsWith(p.getName().toLowerCase())) {
                products.add(p);
            }
        }

        return products.toArray(new Product[products.size()]);
    }
    
    @POST
    @Path("insert")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Status insert(Product product) {
        productCatalog.add(product);
        return new Status("SUCCESS", "Inserted " + product.getName());
    }

    private void initializeProductCatalog() {
        if (productCatalog == null) {
            productCatalog = new ArrayList();
            productCatalog.add(new Product(1, "Keyboard", "Electronics", 29.99D));
            productCatalog.add(new Product(2, "Mouse", "Electronics", 9.95D));
            productCatalog.add(new Product(3, "17\" Monitor", "Electronics", 159.49D));
            productCatalog.add(new Product(4, "Hammer", "Hardware", 9.95D));
            productCatalog.add(new Product(5, "Screwdriver", "Hardware", 7.95D));
            productCatalog.add(new Product(6, "English Dictionary", "Books", 11.39D));
            productCatalog.add(new Product(7, "A House in Bali", "Books", 15.99D));
            productCatalog.add(new Product(8, "An Alaskan Odyssey", "Books", 799.99D));
            productCatalog.add(new Product(9, "LCD Projector", "Electronics", 1199.19D));
            productCatalog.add(new Product(10, "Smart Thermostat", "Electronics", 1199.19D));
        }
    }
}
  • The Product Java bean class shown below is annotated with @XmlRootElement to support XML binding and to allow Jackson to generate JSON and XML representations of Java objects.
package com.pegaxchange.java.bean;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Product implements Serializable {

    private static final long serialVersionUID = 6826191735682596960L;

    private int id;
    private String name;
    private String category;
    private double unitPrice;
    
    public Product() {} // needed for JAXB

    public Product(int id, String name, String category, double unitPrice) {
        this.id = id;
        this.name = name;
        this.category = category;
        this.unitPrice = unitPrice;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public double getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(double unitPrice) {
        this.unitPrice = unitPrice;
    }
}
  • The Status Java bean class is used for returning a message when a new product was inserted using the insert method.
package com.pegaxchange.java.bean;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Status implements Serializable {

    private static final long serialVersionUID = -9130603850117689481L;

    private String status;
    private String message;
    
    public Status() {} // needed for JAXB
    
    public Status(String status, String message) {
        this.status = status;
        this.message = message;
    }
    
    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
    
    public String getMessage() {
        return message;
    }
    
    public void setMessage(String message) {
        this.message = message;
    }
}

  • Before deploying the project as a WAR file to an external Tomcat 9 server from within Eclipse, it is necessary to configure the project’s Deployment Assembly.
  • The JAX-RS-Jersey-API user library should already be listed due to the earlier JAX-RS facet setup.
  • The Jackson-Parser-API user library needs to be added manually. Click on the Add… button.

Eclipse - Web Deployment Assembly

  • Select Java Build Path Entries as the directive type and click on Next > to continue.

Eclipse - Web Deployment Assembly - Add new assembly directive from Java build path entries

  • Select the Jackson-Parser-API user library that was configured earlier and click on Finish.

Eclipse - Web Deployment Assembly - Add new assembly directive from user library

  • The Jackson-Parser-API will now be listed as a source for deployment assembly and its JAR files will be added to the web application’s WAR file in the WEB-INF/lib folder.
  • Click on OK to complete the deployment assembly setup.

Eclipse - Web Deployment Assembly - Define packaging structure for this Java EE Web Application project

  • To deploy the project, right click on the project name in the Eclipse Project Explorer and select Export > WAR file from the context menu.

Eclipse - Project Explorer - Export dynamic web project as WAR file

  • In the below example, the WAR file is directly saved into the Tomcat server’s webapps folder. Tomcat will automatically deploy the web application and start it.

Eclipse - WAR Export - Tomcat webapps destination folder

  • Alternatively, the Tomcat manager app can be used to deploy the WAR file and to stop and restart the REST service application.

Apache Tomcat 9 - Manager application

  • A browser can be used to test the exposed methods that use HTTP GET and a REST service testing tool such as Postman can be used to test the methods that require HTTP POST requests.

Postman is a free Google Chrome based application for testing REST web services and APIs. It can be downloaded from here.

  • Open Postman and enter the URL for the REST service, in this case, the URL for searching the product catalog based on a URI path parameter for the category name is:

https://localhost:8080/SampleWebProject/restservices/productcatalog/search/category/electronics

  • The REST web service returns a JSON array of products in the given category as shown in the below screen shot where the category name is electronics.

Postman - HTTP GET request for searching product catalog by category

  • The search method allows to search for a product by name. In this case, the name is included in the URL as a query string parameter:

https://localhost:8080/SampleWebProject/restservices/productcatalog/search?name=Hammer

  • And the service returns a JSON array of products where the name is hammer.

Postman - HTTP GET request for searching product catalog by name

  • The insert method of the REST service allows to add a product to the catalog. This method is invoked via HTTP POST and the product to be added is passed as a JSON encoded string in the request body. The URL in this case is:

https://localhost:8080/SampleWebProject/restservices/productcatalog/insert

Postman - HTTP POST request for inserting a new product into the catalog

  • This method returns a JSON encoded status message -using the @Produces annotation and the Status Java bean as the return type- indicating that the insert was successful and reflecting the name of the inserted product.

Links to Relevant Documentations

  1. Jersey 2.24 User Guide
  2. Java API For JSON Processing
  3. Java Doc of JAX-RS 2.0 API Specification
  4. Oracle Example on Building RESTful Web Services with JAX-RS