<dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-hk2</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>2.29.1</version> </dependency>The first dependency is for Jersey container servlet that processes requests for RESTful webservices. The second one is for an injection library required by Jersey. And the third one is for processing data in JSON format (convert Java objects to JSON and vice-versa). The version 2.29.1 (or newer) works well on JDK 8 or newer (tested with JDK 11).Then open the web.xml file and insert the following code:
<servlet> <servlet-name>Jersey REST Service</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>net.codejava.ws</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey REST Service</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>This is to specify Jersey Container servlet is responsible to handle all requests coming to the web application with URL starts with /rest/ (after the webapp’s context path). We also specify the package net.codejava.ws contains RESTful webservices classes to be exposed to the clients.
package net.codejava.ws; public class Product { private int id; private String name; private float price; public Product(int id) { this.id = id; } public Product() { } public Product(int id, String name, float price) { this.id = id; this.name = name; this.price = price; } // getters and setters are not shown for brevity @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Product other = (Product) obj; if (id != other.id) return false; return true; } }As you can see, in this model class we implement equals()and hashCode() based on product ID so a Product object can be found in an ArrayList as shown below in the DAO class.
package net.codejava.ws; import java.util.ArrayList; import java.util.List; public class ProductDAO { private static ProductDAO instance; private static List<Product> data = new ArrayList<>(); static { data.add(new Product(1, "iPhone X", 999.99f)); data.add(new Product(2, "XBOX 360", 329.50f)); } private ProductDAO() { } public static ProductDAO getInstance() { if (instance == null) { instance = new ProductDAO(); } return instance; } public List<Product> listAll() { return new ArrayList<Product>(data); } public int add(Product product) { int newId = data.size() + 1; product.setId(newId); data.add(product); return newId; } public Product get(int id) { Product productToFind = new Product(id); int index = data.indexOf(productToFind); if (index >= 0) { return data.get(index); } return null; } public boolean delete(int id) { Product productToFind = new Product(id); int index = data.indexOf(productToFind); if (index >= 0) { data.remove(index); return true; } return false; } public boolean update(Product product) { int index = data.indexOf(product); if (index >= 0) { data.set(index, product); return true; } return false; } }As you can see, this DAO class implements CRUD operations to manage products. It will be used by RESTful web services class in the next section below.
Spring Boot REST APIs Ultimate Course
Hands-on REST API Development with Spring Boot: Design, Implement, Document, Secure, Test, Consume RESTful APIs
package net.codejava.ws; import java.net.*; import java.util.*; import javax.ws.rs.*; import javax.ws.rs.core.*; @Path("/products") public class ProductResource { private ProductDAO dao = ProductDAO.getInstance(); // RESTful API methods go here... }Then write code to implement API method for each operation in the CRUD.\
@GET @Produces(MediaType.APPLICATION_JSON) public List<Product> list() { return dao.listAll(); }This method serves HTTP GET request and returns a list of products in JSON format. Type the following command to test with curl:
curl http://localhost:8080/MyWebsite/rest/productsResponse from the server:
[{"id":1,"name":"iPhone X","price":999.99},{"id":2,"name":"XBOX 360","price":329.5}]NOTE: The best practice for this retrieval operation is returning HTTP status code 204 No Content if no data in the response. Hence update the code as below:
@GET @Produces(MediaType.APPLICATION_JSON) public Response list() { List<Product> listProducts = dao.listAll(); if (listProducts.isEmpty()) { return Response.noContent().build(); } return Response.ok(listProducts).build(); }
@GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public Response get(@PathParam("id") int id) { Product product = dao.get(id); if (product != null) { return Response.ok(product, MediaType.APPLICATION_JSON).build(); } else { return Response.status(Response.Status.NOT_FOUND).build(); } }This method returns information of a particular product based on a given product ID. If a product is found, it returns the response with HTTP 200 status and includes JSON data of the product. Command to test:
curl http://localhost:8080/MyWebsite/rest/products/2Response from the server:
{"id":2,"name":"XBOX 360","price":329.5}
@POST @Consumes(MediaType.APPLICATION_JSON) public Response add(Product product) throws URISyntaxException { int newProductId = dao.add(product); URI uri = new URI("/products/" + newProductId); return Response.created(uri).build(); }As you can see, this method serves HTTP POST request and requires content type of JSON posted from the client. It returns a Response object with HTTP 201 (Created) status and the URI of the newly added item. Run the following command to test (type all in one line):
curl -v -X POST -H "Content-Type: application/json" -d "{\"name\":\"MacBook\",\"price\":1000}" http://localhost:8080/MyWebsite/rest/products/The server’s response includes something like this:
HTTP/1.1 201 Location: http://localhost:8080/products/3 Content-Length: 0
@PUT @Consumes(MediaType.APPLICATION_JSON) @Path("{id}") public Response update(@PathParam("id") int id, Product product) { product.setId(id); if (dao.update(product)) { return Response.ok().build(); } else { return Response.notModified().build(); } }As you can see, this method requires HTTP PUT method and application/json content type. ID of the product is passed as a path parameter in the request URI. If the product is update successfully, return HTTP 200 (OK) status. Else if the product is not found, return HTTP 304 (Not Modified) status.Run this command to update the product with ID 1:
curl -v -X PUT -H "Content-Type: application/json" -d "{\"name\":\"iPad\",\"price\":888}" http://localhost:8080/MyWebsite/rest/products/1You can see the following information in the server’s response:
HTTP/1.1 200 Content-Length: 0If you try to update a product with non-exist ID, the server will return this response:
HTTP/1.1 304
@DELETE @Path("{id}") public Response delete(@PathParam("id") int id) { if (dao.delete(id)) { return Response.ok().build(); } else { return Response.notModified().build(); } }You see, this method requires HTTP DELETE method with ID of the product as a path parameter in the request URI. If the product is removed successfully, the server returns HTTP 200 (OK) status; else returns HTTP 304 (Not Modified) status.Type the following command to test:
curl -v -X DELETE http://localhost:8080/MyWebsite/rest/products/2This will remove the product with ID 2.NOTE: the best practice for delete operation if successful is returning HTTP status code 204 No Content. So update the code as below:
if (dao.delete(id)) { return Response.noContent().build(); }
<dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-client</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jersey.inject</groupId> <artifactId>jersey-hk2</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>2.29.1</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.2</version> </dependency>Write initial code for the client program as follows:
package net.codejava.ws; import javax.ws.rs.client.*; import javax.ws.rs.core.*; import org.glassfish.jersey.client.ClientConfig; public class ProductClientTest { private static String baseURI = "http://localhost:8080/MyWebsite/rest/products"; static WebTarget getWebTarget() { ClientConfig config = new ClientConfig(); Client client = ClientBuilder.newClient(config); return client.target(baseURI); } public static void main(String[] args) { // call a test method: testList(), testAdd(), testGet()... } }To test listing all items, code the following method:
static void testList() { WebTarget target = getWebTarget(); String response = target.request().accept(MediaType.APPLICATION_JSON).get(String.class); System.out.println(response); }Test to get a specific item by ID:
static void testGet() { WebTarget target = getWebTarget(); String productId = "3"; Product product = target.path(productId) .request().accept(MediaType.APPLICATION_JSON) .get(Product.class); System.out.println(product); }Test to add a new item:
static void testAdd() { WebTarget target = getWebTarget(); Product product = new Product("ZenFoneX", 888.88f); Response response = target.request() .post(Entity.entity(product, MediaType.APPLICATION_JSON), Response.class); System.out.println(response.getLocation().toString()); }The following method tests updating an item:
private static void testUpdate() { WebTarget target = getWebTarget(); Product product = new Product("ZenFoneX", 100f); String productId = "4"; Response response = target.path(productId).request() .put(Entity.entity(product, MediaType.APPLICATION_JSON), Response.class); System.out.println(response); }And code the method that tests deleting an item as follows:
private static void testDelete() { WebTarget target = getWebTarget(); String productId = "3"; Response response = target.path(productId).request() .delete(Response.class); System.out.println(response); }That’s how to code a RESTful webservices client program to test CRUD operations.So far you have learned how to code a RESTful webservices application with CRUD (Create, Retrieve, Update and Delete) operations. I hope you found this tutorial helpful. And for your reference, you can download the sample project in the attachments section below. You can also get code from GitHub here.You can also watch the video version below: What's Next? I recommend you follow this one: Spring Boot Hello World RESTful Web Services Tutorial