Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.


 

 

 

 

...

Span
stylefont-size:2em;font-weight:bold
JAX-RS : Client API

...

 

 

 

...


Table of Contents

Maven Dependency

Code Block
xml
xml
<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-client</artifactId>
  <version>3.0.0-milestone1<15</version>
</dependency>

In CXF 2.7.x no JAX-RS 2.0 Client API is supported and CXF specific Client API is located in the cxf-rt-frontend-jaxrs module.

...

Code Block
xml
xml
<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-transports-http-hc</artifactId>
  <!-- 2.7.8 or 3.0.0-milestone115 --> 
  <version>${cxf.version}</version>
</dependency>

...

Code Block
java
java
@Path("/bookstore")
public interface BookStore {
   @GET
   Books getAllBooks();
   
   @Path("{id}")
   BookResource getBookSubresource(@PathParam("id") long id) throws NoBookFoundException;
}

public class BookStoreImpl implements BookStore {
   public Books getAllBooks() {}
   
   public BookBookResource getBookSubresource(long id) throws NoBookFoundException {}
}

public interface BookResource {
   @GET
   Book getBook();
}

public class BookResourceImpl implements BookResource {
   Book getBook() {}
}

...

For injecting proxies via a spring context, use the jaxrs:client element like:

Code Block
xml
xml
  <jaxrs:client id="restClient"
         address="http://localhost:${testutil.ports.BookServerRestSoap}/test/services/rest"
         serviceClass="org.apache.cxf.systest.jaxrs.BookStoreJaxrsJaxws"
         inheritHeaders="true">
         <jaxrs:headers>
             <entry key="Accept" value="text/xml"/>
         </jaxrs:headers>
  </jaxrs:client>  

See this bean for a full example of how jaxrs:client can be used to inject a proxy

Buffering Responses

One way to buffer proxy responses is to have a proxy method return JAX-RS Response, use its bufferEntity()  method (available in JAX-RS 2.0) and use Response.readEntity which can return typed responses if preferred.

The other option is to have a "buffer.proxy.response" property enabled on a given proxy instance.

Limitations

Proxy sub-resource methods returning Objects can not be invoked. Prefer to have sub-resource methods returning typed classes: interfaces, abstract classes or concrete implementations.

The following applies to CXF 2.6.x-2.4.x only:

When a proxy method returning a JAX-RS Response is invoked, the returned Response.getEntity() will return a response InputStream by default. Starting with CXF 2.3.2 one can register an org.apache.cxf.jaxrs.client.ResponseReader provider and cast the Response.getEntity() to more specific application classes:

. Note that WebClient can also be injected as a jaxrs:client.


Asynchronous proxy invocations

Starting from CXF 3.1.7 it is possible to do the asynchronous proxy invocations. One needs to register JAX-RS 2.0 InvocationCallback as a proxy request context property:

Code Block
java
java
BookStore proxy = JAXRSClientFactory.create("http://books", BookStore.class);

Book book = null;
final InvocationCallback<Book> callback = new InvocationCallback<Book>() {
  public void completed(Book response) {
     book = response;
  }
  public void failed(Throwable error) {
  }
};


WebClient.getConfig(proxy).getRequestContext().put(InvocationCallback.class.getName(), callback);
assertNull(proxy.getBook());
Thread.sleep(3);
assertNotNull(book);

If you have a proxy with different methods returning different response types then either register an Object bound InvocationCallback or register a collection of type-specific callbacks:

Code Block
java
java
BookStore proxy = JAXRSClientFactory.create("http://books", BookStore.class);

// Book
Book book = null;
final InvocationCallback<Book> bookCallback = new InvocationCallback<Book>() {
  public void completed(Book response) {
     book = response;
  }
  public void failed(Throwable error) {
  }
};
// Chapter
Chapter chapter = null;
final InvocationCallback<Chapter> chapterCallback = new InvocationCallback<Chapter>() {
  public void completed(Chapter response) {
     chapter = response;
  }
  public void failed(Throwable error) {
  }
};
 
WebClient.getConfig(proxy).getRequestContext().put(InvocationCallback.class.getName(), 
             
Code Block
javajava
ResponseReader reader = new ResponseReader();
reader.setEntityClass(Book.class);
        
BookStore bs = JAXRSClientFactory.create("http://localhost:8080/books", BookStore.class,
                                      Arrays.asList(bookCallback, chapterCallback));
//  Collections.singletonList(reader));
Response r1 = bs.getBook("123");
Book book = (Book)r1.getEntity();

reader.setEntityClass(Author.class);
Response r2 = bs.getBookAuthor("123");
Author book = (Author)r2.getEntity();
Get Book
assertNull(proxy.getBook(123L));
Thread.sleep(3);
assertNotNull(book);
 
// Get Book Chapter
assertNull(proxy.getBookChapter(123L));
Thread.sleep(3);
assertNotNull(chapter);

Make sure a proxy is created in a thread safe mode if it is being accessed by multiple threads for every new request thread to have its own callback.

Buffering Responses

One way to buffer proxy responses is to have a proxy method return JAX-RS Response, use its bufferEntity()  method (available in JAX-RS 2.0) and use Response.readEntity which can return typed responses if preferred.

The other option is to have a "buffer.proxy.response" property enabled on a given proxy instance.

Limitations

Proxy sub-resource methods returning Objects can not be invoked. Prefer to have sub-resource methods returning typed classes: interfaces, abstract classes or concrete implementations.


Working with user models

Proxies can be created with the external user model being applied to a proxy class, for example:

...

The above code will send requests like "GET http://books/1", "GET http://books/2", etc.

If the request URI can be parameterized then you may want to use the following code:

...

Code Block
xml
xml
<bean id="myJsonProvider" 
class="org.apache.cxf.jaxrs.provider.JSONProvider" > 
        <property name="supportUnwrapped" value="true" /> 
        <property name="wrapperName" value="nodeName" /> 
    </bean> 

<util:list id="webClientProviders"> 
    <ref bean="myJsonProvider"/> 
</util:list> 

<bean id="myWebClient" class="org.apache.cxf.jaxrs.client.WebClient" 
factory-method="create"> 
        <constructor-arg type="java.lang.String" 
value="http://some.base.url.that.responds/" /> 
        <constructor-arg ref="webClientProviders" /> 
</bean> 

...

Code Block
xml
xml
<jaxrs:client id="webClient"
         address="https://localhost:${port}/services/rest"
         serviceClass="org.apache.cxf.jaxrs.client.WebClient">
         <jaxrs:headers>
             <entry key="Accept" value="text/xml"/>
         </jaxrs:headers>
  </jaxrs:client>

The only limitation of using this option is that some of jaxrs:client attributes ("inheritHeaders", "modelRef") and elements ("model") are not really applicable to WebClient.

...

A single client doing multiple invocations without changing the current URI or headers is thread-safe (while creating a Invocation.Builder instances concurrently is not thread-safe since the shared instance of non-thread-safe class ClientProviderFactory is used under the hood). The only limitation in this case applies to proxies, in that they can not get "out of band" headers without synchronizing, ex :

Code Block
java
java
// get some response headers passed to us 'out of band', which is not thread-safe for a plain proxy: 
String bookHeader = WebClient.toClientclient(injectedBookStoreProxy).getHeaders().getFirst("BookHeader"); 

...

Code Block
java
java
Book proxy = JAXRSClientFactory.create("http://books", Book.class);
ClientConfiguration config = WebClient.getConfig(proxy);
HTTPConduit conduit1 = (HTTPConduit)config = (HTTPConduit)config.getConduit();

WebClient webclient = WebClient.create("http://books");
HTTPConduit conduit2 = (HTTPConduit)WebClient.getConfig(webclient).getConduit();

WebClient webclient = WebClient.create("http://books");
HTTPConduit conduit2 = (HTTPConduit)WebClient.getConfig(webclient).getConduit();

When working with JAX-RS 2.0 Client API one can set some low-level HTTP properties via Configurable interface:

Code Block
java
java
//http.connection.timeout
//http.receive.timeout
//http.proxy.server.uri
//http.proxy.server.port
Client client = ClientBuilder.newClient();
client.property("http.receive.timeout", 1000000); 

Creating clients programmatically with no Spring dependencies

Example When working with JAX-RS 2.0 Client API one can set some low-level HTTP properties via Configurable interface :

Code Block
java
java
//http.connection.timeout
//http.receive.timeout
//http.proxy.server.uri
//http.proxy.server.port
Client client = ClientBuilder.newClient();
client.property("http.receive.timeout", 1000000); 

Creating clients programmatically with no Spring dependencies

Example :

...

JAXRSClientFactoryBean sf = new JAXRSClientFactoryBean();
sf.setResourceClass(CustomerService.class);
sf.setAddress("http://localhost:9000/");
BindingFactoryManager manager = sf.getBus().getExtension(BindingFactoryManager.class);
JAXRSBindingFactory factory = new JAXRSBindingFactory();
factory.setBus(sf.getBus());
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);
CustomerService service = sf.create(CustomerService.class);
WebClient wc = sf.createWebClient();
JAXRSClientFactoryBean sf = new JAXRSClientFactoryBean();
sf.setResourceClass(CustomerService.class);
sf.setAddress("http://localhost:9000/");
BindingFactoryManager manager = sf.getBus().getExtension(BindingFactoryManager.class);
JAXRSBindingFactory factory = new JAXRSBindingFactory();
factory.setBus(sf.getBus());
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);
CustomerService service = sf.create(CustomerService.class);
WebClient wc = sf.createWebClient();

Configuring an HTTP Conduit from Spring

There's a number of ways to configure HTTPConduits for proxies and WebClients.

It is possible to have an HTTPConduit configuration which will apply to all clients using different request URIs or only to those with using a specific URI. For example:

Code Block
xml
xml
<http:conduit name="http://books:9095/bookstore.*"/> 

This configuration will affect all proxies and WebClients which have requestURIs starting from 'http://books:9095/bookstore'. Note the trailing '.*' suffix in the name of the http:conduit element.

Please see this configuration file for more examples.

Alternatively you can just do:

Code Block
xml
xml
<http:conduit name="*.http-conduit"/> 

This configuration will affect all the clients, irrespective of the URIs being dealt with.

If you work with proxies then you can have the proxy-specific configuration using the expanded QName notation

Configuring an HTTP Conduit from Spring

There's a number of ways to configure HTTPConduits for proxies and WebClients.

It is possible to have an HTTPConduit configuration which will apply to all clients using different request URIs or only to those with using a specific URI. For example:

Code Block
xml
xml
<http:conduit name="{http://books:9095/bookstore.*foo.bar}BookService.http-conduit"/> 

This configuration will affect all proxies and WebClients which have requestURIs starting from 'http://books:9095/bookstore'. Note the trailing '.*' suffix in the name of the http:conduit element.

Please see this configuration file for more examples.

In this example, 'foo.bar' is a reverse package name of the BookService proxy class.

Similarly, for WebClients you can Alternatively you can just do:

Code Block
xml
xml
<http:conduit name="*{http://localhost:8080}WebClient.http-conduit"/> 

This configuration will affect all the clients, irrespective of the URIs being dealt with.

If you work with proxies then you can have the proxy-specific configuration using the expanded QName notation:

...

<http:conduit name="{http://foo.bar}BookService.http-conduit"/> 

In this example, 'foo.bar' is a reverse package name of the BookService proxy class.

Similarly, for WebClients you can do:

...

<http:conduit name="{http://localhost:8080}WebClient.http-conduit"/> 

In this example, 'http://localhost:8080' is the base service URI.

Please see jaxrs-https-client1.xml and jaxrs-https-client2.xml configuration files for more examples.

Also see this wiki page on how to configure HTTPConduits.

Clients and Authentication

Proxies and HTTP-centric clients can have the HTTP Authorization header set up explicitly:

Code Block
java
java
// Replace 'user' and 'password' by the actual values
String authorizationHeader = "Basic " 
    + org.apache.cxf.common.util.Base64Utility.encode("user:password".getBytes());

// proxies
WebClient.client(proxy).header("Authorization", authorizationHeader);

// web clients
webClient.header("Authorization", authorizationHeader);

or by providing a username and password pair at client creation time, for example

In this example, 'http://localhost:8080' is the base service URI.

Please see jaxrs-https-client1.xml and jaxrs-https-client2.xml configuration files for more examples.

Also see this wiki page on how to configure HTTPConduits.

Clients and Authentication

Proxies and HTTP-centric clients can have the HTTP Authorization header set up explicitly:

Code Block
java
java
BookStore proxy = JAXRSClientFactory.create("http:// Replace 'user' and 'password' by the actual values
String authorizationHeader = "Basic " 
    + org.apache.cxf.common.util.Base64Utility.encode("user:password".getBytes());

// proxies
WebClient.client(proxy).header("Authorization", authorizationHeader);

// web clients
webClient.header("Authorization", authorizationHeader);

or by providing a username and password pair at client creation time, for example:

...

BookStore proxy = JAXRSClientFactory.create("http://books", BookStore.class, "username", "password", "classpath:/config/https.xml");

WebClient client = WebClient.create("http://books", "username", "password", "classpath:/config/https.xml");
books", BookStore.class, "username", "password", "classpath:/config/https.xml");

WebClient client = WebClient.create("http://books", "username", "password", "classpath:/config/https.xml");

When injecting clients from Spring, one can add 'username' and 'password' values as attributes to jaxrs:client elements or add them to WebClient factory create methods.

Clients in Spring Boot

Please see JAXRSClientSpringBoot documentation on how CXF JAX-RS Clients can be used in a SpringBoot Application.

Clients and HTTP(s)

The default HttpClientHTTPConduit conduit by default supports the following HTTPS protocols: TLSv1, TLSv1.1, TLSv1.2, TLSv1.3. Since Apache CXF 4.0.4 / 3.6.3 release, the default HttpClientHTTPConduit respects https.protocols system property (see please https://blogs.oracle.com/java/post/diagnosing-tls-ssl-and-https) and if set, would use the provided protocols. This behavior could be turned off by setting https.protocols.ignored system property to "true" (the default value is "false")When injecting clients from Spring, one can add 'username' and 'password' values as attributes to jaxrs:client elements or add them to WebClient factory create methods.