Using PATCH with Jersey Client API for unit testing

Assuming your implementation consists of a custom annotation like this

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;

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

Trying to do something like this with the Client

 String response = target.request().method("PATCH", Entity.text("Hello"), String.class);

by default is not supported, and will an exception like

java.net.ProtocolException: Invalid HTTP method: PATCH

This is not a problem with the Client API directly, but with the lower level Java APIs. Seems to be some security restriction.

With the Client API we can override this by setting a property

  • HttpUrlConnectionProvider.SET_METHOD_WORKAROUND to true

In the JerseyTest, one way to configure the Client is to override configureClient, and set the property with the ClientConfig. You could just as easily set the property on the Client itself, but staying in the spirit of the JerseyTest framework (where we don't need to explicitly access the Client, the example below will just just override the method

public class PatchTest extends JerseyTest {

    @Path("patch")
    public static class PatchResource {
        @PATCH
        @Produces(MediaType.TEXT_PLAIN)
        public String getPatch(String request) {
            return "Patched " + request;
        }
    }

    @Override
    protected void configureClient(final ClientConfig config) {
        config.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
    }

    @Override
    public Application configure() {
        return new ResourceConfig(PatchResource.class);
    }

    @Test
    public void doPatchTest() {
        WebTarget target = target("patch");
        String response = target.request().method("PATCH", Entity.text("Hello"), String.class);
        Assert.assertEquals("Patched Hello", response);
        System.out.println(response);
    }
}

To send the HTTP PATCH via JAX RS Client API without any extra configuration:

 client.target("$baseUrl$restUsersUrl/$userId")
                .request("application/json")
                .build("PATCH", Entity.entity(json2Update, MediaType.APPLICATION_JSON))
                .invoke()

Annotation @PATCH is now available in JAX-RS 2.1. You can implement this HTTP method on the server side like:

@PATCH
public Response updateResource() { ... } 

As for the client side, you can do something like:

Response r = ClientBuilder.newClient()
    .target("http://localhost:8080/patch")
    .request()
    .build("PATCH", Entity.text("patch"))
    .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
    .invoke();

Where SET_METHOD_WORKAROUND is used to avoid the protocol exception, as indicated by @peeskillet:

java.net.ProtocolException: Invalid HTTP method: PATCH