Suresh Payankannur

Thursday, October 23, 2014

JAX-RS 2.0 and HTTP PATCH

When designing RESTful services, one of the scenario comes across is partial updates. Typical REST operations use the whole resource. Normally updates use PUT to modify the resource. More often, you need to do a GET before you modify the resource. This approach has a number of side effects. Increased chatter with the server, bandwidth issues, performance etc. If the resource is really big and you are only updating a few attributes, PUT is not a good idea.

Enter HTTP Patch. This is meant to address this, partial updates of resources.

A sample patch request

RFC 6902 defines a JSON document format for patching
{"op" : "test",    "path" : "/firstName", "value" : "John"},
{"op" : "replace", "path" : "/zipcode",   "value" : "94555"},
{"op" : "add",     "path" : "/phone",     "value" : "888-999-1234"},
{"op" : "remove",  "path" : "/hobby"},
{"op" : "copy", "from"    : "/primayAddress", "to" : "/secondaryAddress"},
{"op" : "move", "from"    : "/mobilePhone",   "to" : "/primaryPhone"}
In this post, I discuss a generic framework to patch a single resource.

Environment

  1. JAX-RS 2.0
  2. Jackson JSON Processor
  3. json-patch (https://github.com/fge/json-patch) implementing RFC6902

Model

public class User {
    private long id;
    private String username;
    private String firstName;
    private String phone;
    private String primaryAddress;
    private String secondaryAddress;
    private String mobilePhone;
    private String primaryPhone;
    private String hobby;

   // .. boiler plate code ..
}

Patch Framework

Steps involved in applying patch for a single resource:
  1. Define PATCH annotation
  2. Implement JAX-RS Resource
  3. Implement PATCH API
    • Read existing resource from back-end data store
    • Convert the resource to JSON
    • Use json-patch to apply the patch
    • Convert the patched JSON to Java class
    • Save the patched object to back-end data store

Implementation

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
@Documented
@NameBinding
public @interface PATCH {
}

@Produces('application/json")
public class UserResource {
    @Inject private UserManager userManager;

    @PATCH
    @Path("/users/{username}")
    @Transactional
    public Response patch(@PathParam("username") String username, String json) {
        // get the user from DB
        User user = userManger.findByUsername(username);

         try {
             if (user != null) {
                 ObjectMapper mapper = new ObjectMapper();

                 // convert JSON string to a Java class
                 JsonPatch patch = mapper.readValue(json, JsonPatch.class);

                 // convert User to a JSON object
                 JsonNode userJson = mapper.valueToTree(user);

                 // apply patch
                 JsonNode patched = patch.apply(userJson);

                 // convert the patched object to User
                 user = mapper.readValue(patched.toString(), User.class);

                 // save the patched object
                 user = userManager.save(user);
             }
         }  catch (Exception ex) {
             throw new RuntimeException(ex);
         }
         return Response.ok().build();
    }
}


2 comments:

  1. Excellent.Short and to the point. Many thanks-TN

    ReplyDelete
  2. Thanks for the post, I am techno savvy. I believe you hit the nail right on the head. I am highly impressed with your blog. It is very nicely explained. Your article adds best knowledge to our Java Online Training from India. or learn thru Java Online Training from India Students.

    ReplyDelete

Blog Archive

Scroll To Top