Book Image

RESTful Java Web Services, Second Edition

Book Image

RESTful Java Web Services, Second Edition

Overview of this book

Table of Contents (17 chapters)
RESTful Java Web Services Second Edition
Credits
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Index

Implementing PATCH support in JAX-RS resources


When a client changes only one part of the resource, you can optimize the entire update process by allowing the client to send only the modified part to the server and thereby saving the bandwidth and server resources. RFC 5789 proposes a solution for this use case via a new HTTP method, PATCH. We have already covered the theoretical concepts behind the PATCH method in Chapter 8, RESTful API Design Guidelines, under the Implementing partial update section. In this section, we will see how to actually implement the PATCH method in your JAX-RS application.

Defining the @PATCH annotation

JAX-RS allows you to define annotations to represent any custom or non-standard HTTP methods by annotating them with javax.ws.rs.HttpMethod. We will use this feature to define the @PATCH annotation, which can be used later to designate resource class methods for responding to HTTP PATCH method calls. Following is an example:

import javax.ws.rs.HttpMethod;
import javax.ws.rs.NameBinding; 
//Other imports are removed for brevity
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
@Documented
@NameBinding
public @interface PATCH {
}

Defining a resource method to handle HTTP PATCH requests

As we discussed at the beginning of this section, a PATCH request sends only the modified fields to the server, along with the type of operations performed on each modified field.

The following example illustrates a PATCH request for a department resource:

PATCH /departments/10 HTTP/1.1
[
  { "op": "replace", "path": "/managerId", "value": 300 },
  { "op": "add", "path": "/locationId", "value": 1200 }
]

JAX-RS does not support a PATCH method by default. However, this does not prevent you from supporting PATCH in your application. Following are the steps that you need to take:

  1. The resource method that you define should have logic to read the partial changes sent by the client.

  2. The next step is to construct the resource entity representation out of the partial representation.

  3. The last step is to save the changes.

The following code snippet demonstrates how you can implement a partial update for the Department resource. This example uses the json-patch library available in GitHub (https://github.com/fge/json-patch) to read the partial updates present in the request payload and build an entity out of it:

//Other imports are removed for brevity
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.JsonPatch;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

@Path("departments")
public class DepartmentResource{

@PATCH
@Path("{id}")
@Consumes({"application/json-patch+json"})
public Response patch(@PathParam("id") Short id, 
   String deptUpdatesInJSON) {
  // get the dept from DB
  Department dept = findDepartmentEntityFromDB(id);

  try {
    if (dept != null) {
      //The mapper provides 
      //functionality for converting between Java objects 
      //and matching JSON constructs.
      ObjectMapper mapper = new ObjectMapper();

      // convert JSON string to a Java class
      //JsonPatch is here https://github.com/fge/json-patch
      JsonPatch patch = mapper.readValue(deptUpdatesInJSON, 
                    JsonPatch.class);
      // convert Dept from DB to a JSON object
      JsonNode deptJson = mapper.valueToTree(dept);
      // apply patch on latest Dept read from DB
      JsonNode patched = patch.apply(deptJson);
      // convert the patched object to Dept to store
      dept = mapper.readValue(patched.toString(), 
                    Department.class);
      // save the patched department object
      saveEntity(dept);
    }else{
                //Dept obj for the patch sent by client is 
                //not found in DB
                return Response.serverError().
                    status(Status.NOT_FOUND).build();
          }
    } catch (Exception ex) {
      throw new WebApplicationException(ex);
    }
    return Response.ok().build();
   }
//Other methods are removed for brevity

}

Tip

The complete source code for this example is available in the Packt website. You can download the example from the Packt website link that we mentioned at the beginning of this book, in the Preface section. In the downloaded source code, see the rest-appendix-patch-demo project to get a feel of the sample PATCH implementation that we discussed in this section.

You can use REST API testing tools, such as the Postman REST client or the SoapUI, to quickly test the PATCH implementation that we built. The sample PATCH API call may look like the following:

PATCH /rest-appendix-patch-demo/webresources/departments/10 HTTP/1.1
Host: localhost:8080
Content-Type: application/json-patch+json
Cache-Control: no-cache

[ { "op": "replace", "path": "/departmentName", "value": "IT"}, 
  { "op": "add", "path": "/locationId", "value": 1200 } ]