Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use PATCH method in CXF

I am trying to use PATCH method in my client using CXF implementation of JAX-RS. At first I defined the PATCH annotation as

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

Referencing what was written here : How to have a @PATCH annotation for JAX-RS?

Then I found out @PATCH was added into CXF 3.1.2, so I changed version in my maven's pom.xml and indeed there is public @interface PATCH inside of package org.apache.cxf.jaxrs.ext; and the code actually looks exactly as what I posted above.

However, when I try to use this annotation on my service definition as

@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface AbcService {

    @PATCH
    @Path("/abc/efg")
    public SomeDTO patchSomething(RequestObject request);
}

I end up with the java.net.ProtocolException: Invalid HTTP method: PATCH as was said in the queston link I posted above. They discuss some solution for this with Jersey, however what I can I do in CXF, so that I can use :

AbcService abcService = JAXRSClientFactory.create(myURI, AbcService.class, myProviders, true);
abcService.patchSomething(new RequestObject('something'));

So I have couple of questions:

  1. How can I make this work ? No I need to write custom CXF interceptor ?
  2. Why did they add the PATCH annotation into CXF if it doesn't work ?
  3. Some guys in the other topic said that the mentioned PATCH annotation definition works for them. How come ? Does it only make trouble on the client side, and if so why is it ?
  4. Why I can't find this annotation in CXF documentation ? I looked into org.apache.cxf.jaxrs.ext package at http://cxf.apache.org/javadoc/latest/ and I don't see any PATCH. Yet in the latest cxf 3.1.2 I really can find it in this package.
like image 609
Patrik Stas Avatar asked Aug 18 '15 08:08

Patrik Stas


People also ask

Why do we need Apache CXF?

You also have a choice of different transports such as HTTP, JMS, JBI and the choice of front-end API's like JAX-RS and JAX-WS. Having so many options for web service development, there is a need for an open source services framework to glue all the above mentioned options together and that is what Apache CXF does.

How do I use CXF in Apache Camel?

In Apache Camel, the Camel CXF component is the key to integrating routes with Web services. You can use the Camel CXF component to create a CXF endpoint, which can be used in either of the following ways: Consumer — (at the start of a route) represents a Web service instance, which integrates with the route.

What is this CXF tutorial about?

This tutorial has been prepared to cater the needs of both the beginners and experts in Apache CXF. The tutorial has a flow that takes you from the simpler concepts to in depth ones and lets you gain confidence as you progress through it.

How to implement Helloworld interface in Apache CXF?

The HelloWorld interface is implemented in the HelloWorldImpl Apache CXF class as shown below − The greetings method receives a parameter of string type, appends it to a greeting message and returns the resultant string to the caller. Next, we write the server application to host the HelloWorld service.


1 Answers

It turns out it's cause because in JAVA7, HttpURLConnection doesn't support PATCH, the supported methods in that class are defined statically as

   private static final String[] methods = {
        "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
    };

However it is possible to send PATCH request in CXF, but the Conduit object must be of type AsyncHTTPConduit. To make CXF use AsyncHTTPConduit, you can programatically achieve it like this

AbcService service = JAXRSClientFactory.create(myURI, AbcService.class, myProviders, true);
WebClient.getConfig(service).getRequestContext().put("use.async.http.conduit", true);
service.patchEnvironmentParameters(patchRequest);

Or

WebClient client = WebClient.create("http://localhost:53261/v1-0/api/environment/parameters");
WebClient.getConfig(client).getRequestContext().put("use.async.http.conduit", true);
client.invoke("PATCH", "{}");

But beware !! In order to make this work, you have put this dependency into your project

<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-transports-http-hc</artifactId>
  <version>${cxf.version}</version>
</dependency>

Also make sure that you use the same version of cxf-rt-transports-http-hc and cxf.

But as you can see what I described doesn't solve the original issue, this way I just made 1 specific PATCH request. However in my project there are many PATCH services defined using interfaces like I showed originally

public interface AbcService {

    @PATCH
    @Path("/abc/efg")
    public SomeDTO patchSomething(RequestObject request);
}

So in order to use the AsyncHTTPConduit only on PATCH methods, I had to write custom CXF interceptor, about which you can learn more here http://cxf.apache.org/docs/interceptors.html The interceptor I wrote runs in PRE_LOGIC phase and it checks what kind of method is used and in case it PATCH, it defined the conduit property. Then in latter phases of service invocation, CXF uses this property to choose which Conduit implementation should be used, and so after

if ( message.get(Message.HTTP_REQUEST_METHOD).equals("PATCH") {
  message.put("use.async.http.conduit", true);
}

the AsyncHTTPConduit instance will be used with which the PATCH will work.

like image 65
Patrik Stas Avatar answered Oct 12 '22 10:10

Patrik Stas