Consuming RESTful Web Services With the Jersey Client API

by Jakub Podlesak

Jersey 1.0 is an open-source, production-ready reference implementation of JAX-RS, the Java API for RESTful Web Services (JSR-311). Jersey makes it easy to create RESTful web services using Java technology.

An earlier Tech Tip, Implementing RESTful Web Services in Java, introduced RESTful Web Services, JAX-RS, and Jersey. It also showed how using Java technology you can write RESTful web services that conform to the JAX-RS specification. Another tip, Configuring JSON for RESTful Web Services in Jersey 1.0, showed how to configure data in JSON (JavaScript Object Notation) using Jersey 1.0.

In this tip, you will learn how to use the Jersey 1.0.2 Client API to consume HTTP-based RESTful Web Services. The Jersey 1.0.2 client API is an easy-to-use, high level, Java technology API that can help you write clients for any HTTP-based RESTful web service. The API is built on the uniform interface concept, one of the key principles of REST. The uniform interface concept means that whatever URIs a REST-based application accesses, the interface to those URIs should be the same

Jersey Client API Basics

To start working with the Jersey client API, you need to create an instance of the com.sun.jersey.api.client.Client class. The simplest way to do that is as follows:

   import com.sun.jersey.api.client.Client;

   Client client = Client.create();

The Client class is the main configuration point for building a RESTful web service client. You use it to configure various client properties and features and indicate which resource providers to use. Creating an instance of a Client is an expensive operation, so try to avoid creating an unnecessary number of client instances. A good approach is to reuse an existing instance, when possible.

After you create a Client instance, you can start using it. However, to issue requests, you need to create a WebResource object, which encapsulates a web resource for the client. For example, the following code creates a WebResource object for a web resource whose URI is http://example.com/base:

   import com.sun.jersey.api.client.WebResource;

   WebResource webResource = client.resource("http://example.com/base");

You use the WebResource object to build requests to send to the web resource and to process responses returned from the web resource. For example, you can use the WebResource object for HTTP GET, PUT, POST, and DELETE requests.

GET Request: Use the get() method in the WebResource class to submit an HTTP GET request to the web resource:

   String s = webResource.get(String.class);

This means that if the URI for the WebResource object is http://example.com/base, an HTTP GET request is submitted to the resource whose URI is http://example.com/base. If you're familiar with curl, the command line HTTP tool, you'll see that:

   String s = webResource.get(String.class);

corresponds to the following curl command:

   curl http://example.com/base

You can specify query parameters in the get() request. For example, the following code specifies two query parameters in the get() request:

   MultivaluedMap queryParams = new MultivaluedMapImpl();
   queryParams.add("param1", "val1");
   queryParams.add("param2", "val2");
   String s = webResource.queryParams(queryParams).get(String.class);

This corresponds to the following curl command:

   curl http://example.com/base?param1=val1&param2=val2

You can also specify the acceptable MIME type for the response. For example, the following code specifies an acceptable MIME type of text:

   String s = webResource.accept("text/plain").get(String.class);

This corresponds to the following curl command:

   curl -HAccept:text/plain http://example.com/base

In addition, you can get the HTTP status code for the request. Here, for example, is a request that returns a text entity and a status code:

   ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class);
   int status = response.getStatus();
   String textEntity = response.getEntity(String.class);

The ClientResponse object represents an HTTP response in the client.

PUT Request: Use the put() method in the WebResource class to submit an HTTP PUT request to the web resource. For example, the following request puts a text entity foo:bar into a web resource:

   ClientResponse response = webResource.type("text/plain").put(ClientResponse.class, "foo:bar");

This corresponds to the following curl command:

   curl -XPUT -HContent-type:text/plain --data "foo:bar" http://example.com/base

You can also specify query parameters in the put()request. You do this in a way that is similar to specifying query parameters for a get() request. In the following example, the same query parameters that were used in the previous get() method example are specified in a put() request:

  MultivaluedMap queryParams = new MultivaluedMapImpl();
  queryParams.add("param1", "val1");
  queryParams.add("param2", "val2");
  ClientResponse response = webResource.queryParams(queryParams).put(ClientResponse.class, "foo:bar");

This corresponds to the following curl command:

   curl -XPUT -HContent-type:text/plain --data "foo:bar" http://example.com/base?param1=val1&param2=val2

POST Request: A POST request is a syntactic combination of a GET request and a PUT request, that is, you can use a POST request to send an entity to a web resource and receive another entity. Use the post() method in the WebResource class to submit an HTTP POST request to the web resource. For example, the following code submits a POST request with query parameters and URL-encoded form data:

  MultivaluedMap formData = new MultivaluedMapImpl();
  formData.add("name1", "val1");
  formData.add("name2", "val2");
  ClientResponse response = webResource.type("application/x-www-form-urlencoded").post(ClientResponse.class, formData);

This corresponds to the following curl command:

   curl -d name1=val1 -d name2=val2 http://example.com/base

DELETE Request: Use the delete() method in the WebResource class to submit an HTTP DELETE request to the web resource. For example, the following request deletes the resource whose URI is http://example.com/base/user/123:

  ClientResponse response = webResource.path("user/123").delete(ClientResponse.class);

This corresponds to the following curl command:

   curl -XDELETE http://example.com/base/user/123

Note that the WebResource.path() method, which can also be used in all the HTTP request methods, allows you to specify an additional path for the requested web resource. Another WebResource method, header(), allows you to add an HTTP header to your request.

Configuring the Jersey Client

Before submitting requests, you might need to configure the client. This can involve registering providers. You also have the option of adding filters. See the Jersey 1.0.2 Client API for an overview of all possible options.

Registering Providers: In JAX-RS, a provider is an implementation of a JAX-RS extension. A provider class is marked with a @Provider annotation. The Jersey server presents an infrastructure for providers. In implementing JAX-RS, Jersey includes standard provider classes. The Jersey client API reuses the same provider infrastructure as the Jersey server. However, you need to explicitly register all non-standard providers because no automatic classpath scan takes place on the client side.

To register a provider, you need to add its provider class to the ClientConfig object for the Client instance. The ClientConfig class declares the common property names, features, properties, provider classes, and singleton provider instances that can be used by a Client object. For example, the following code registers a JSON provider class for use by a Client object:

   ClientConfig config = new DefaultClientConfig();
   config.getClasses().add(JSONRootElementProvider.class);
   Client client = Client.create(config);

Notice the use of the DefaultClientConfig class. It declares the default client configuration.

Adding Filters: Another option you have in configuring a client is to add filters to the client instance. A filter dynamically intercepts requests and responses targeted to a resource class and can be used to modify the request or response. The Jersey client API provides a number of classes that implement filters. One of them is LoggingFilter, which implements a logging filter. You can use a logging filter to track communication between the client and a server application, something that can be valuable in debugging. Here is how to add a logging filter to the client:

   import com.sun.jersey.api.client.filter.LoggingFilter

   client.addFilter(new LoggingFilter());

An Example of a Jersey-Based Client

Accompanying this tip is an example application that uses the Jersey client API to access the popular Twitter web service. The example demonstrates the ability of the Jersey Client API to consume real-world, HTTP-based web services. You can download the example application as a Twitter client ZIP archive. If you expand the archive, you can examine the source code for the client. You can also download a runnable Twitter client JAR file to test the application and see how it works. Note that you need Java SE Runtime Environment (JRE) 6 to run the application.

Twitter: Twitter is a service that allows you to exchange short text messages with friends, coworkers, family members, and others. The messages are designed to answer the question, "What are you doing?" Here is an example of some messages displayed through Twitter.

Twitter messages
Twitter Messages
 

Twitter also provides a public Twitter API that you can use to programmatically produce or consume Twitter messages and access other aspects of the service. If you use the Twitter API, one thing you need to take into account is Twitter's authentication and security mechanism and requirements.

Twitter Authentication and Security: Twitter uses a Basic HTTP authentication schema. This means that if you access Twitter through its API, you need to add a special Authorization header to your request. Then you will probably also want to secure the communication using the Secure Sockets Layer (SSL). You can do this simply with the following code:

   ClientConfig config = new DefaultClientConfig();
   SSLContext ctx = SSLContext.getInstance("SSL");
   ctx.init(null, myTrustManager, null);
   config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hostnameVerifier, ctx));
   Client client = Client.create(config);

Examining the Client: Download the Twitter client ZIP archive and expand it. Navigate to the TwitterClient class in the src\\main\\java\\com\\sun\\jersey\\techtips\\twitter directory. This is the Jersey-based client for the application.

As you examine the source code in the TwitterClient class, notice especially the following:

  • Code that creates the Client and WebResource objects:
       import com.sun.jersey.api.client.Client;
       import com.sun.jersey.api.client.WebResource;
    
       private static final String BaseURI = "https://twitter.com";
       private final WebResource wr;
    
       Client client = Client.create(config);
       wr = client.resource(BaseURI);
    
  • Code that configures the client:
       import com.sun.jersey.api.client.config.ClientConfig;
       import com.sun.jersey.api.client.config.DefaultClientConfig;
    
       ClientConfig config = new DefaultClientConfig();
    
       config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hv, ctx));
       config.getClasses().add(JAXBContextResolver.class);
    
  • Code that issues a get request:
       public List<StatusBean> getFriendsTimelineJson() {
               return wr.path("statuses/friends_timeline.json")
                       .header(AUTHENTICATION_HEADER, authentication)
                       .accept(MediaType.APPLICATION_JSON_TYPE)
                       .get(new GenericType<List<StatusBean>>() {
               });
        }
    
    Notice the addition of an Authentication header to the request.

Running the Example Application

To run the example application, do the following:

  1. If you do not have JRE 6 installed, download and install it .
  2. Download the Twitter client JAR file
  3. Issue the following command from the command line:
       java -jar jersey-twitter-client-1.0-SNAPSHOT-jar-with-dependencies.jar
    

    In response, you will be prompted for your Twitter user name and password. Respond to the prompts as appropriate. You should see then Twitter messages with timestamps. Here is an example:

    Simple Jersey (http://jersey.dev.java.net) based twitter client
    Twitter username:mytwitter
    Password:
    Hello mytwitter!
    Status will get updated every 60 secs.
    Enter !exit to finish the session.
    Arun Gupta (1238705837), at: Mon Feb 23 00:04:40 +0000 2009:
     #slumdog is a favorite ... have not seen the movie yet but would be great if it
    's the first Indian movie to win ... fingers crossed!
    LarryLary (1238103969), at: Mon Feb 23 00:07:33 +0000 2009:
     Gonna go for a walk. Need some fresh air.
    kmollo (1239025876), at: Mon Feb 23 01:43:34 +0000 2009:
     enjoying everyone's '15 Albums That Changed Your Life' lists. I have been so
     bored, it's nice to have some 'new' music to check out!
    iFab @fauntleroy (1239103887), at: Mon Feb 23 02:02:20 +0000 2009:
     It's a beautiful day
    JessicaxG (1239248068), at: Mon Feb 23 02:36:34 +0000 2009:
     Shop. Shop. Shop. Another way to avoid things I need to do.
    DoughJoe (1239571723), at: Mon Feb 23 03:55:39 +0000 2009:
     Slumdog grabs it all
    > Congratulations to Kate Winslet on her best actress Oscar.
    Posted at: Tue Feb 24 16:54:17 +0000 2009
    >!exit
    bye
     

    Notice that you can also enter your own messages in the client.

Further Reading

For more information on Jersey, see the following resources:

About the Author

Jakub Podlesak is a member of the Jersey project team. Previously, he participated in the development of Metro, the GlassFish web services stack, as a member of the WS-Policy team. Read his blog.


2009 JavaOne Conference, June 2-5, San Francisco \*\* Register Now\*\*

Stay on top of everything new and different, both inside and around Java technology. Register by April 22, 2009, and save $200 off a Conference Pass or Conference Plus Pass. Register now at http://java.sun.com/javaone.

Comments:

good

Posted by govardhan reddy on March 06, 2009 at 11:19 PM PST #

I am confused: how does the curl command tool have anything to do with jersey?

Posted by reader on March 17, 2009 at 11:50 PM PDT #

Impressive.

Posted by yadoo86 on March 18, 2009 at 12:50 AM PDT #

@reader: curl has nothing to do with Jersey. I just have used curl as an analogy, because i believed many people interested in how web works were familiar with it.

Posted by Jakub on March 18, 2009 at 02:30 AM PDT #

Thanks for this wonderful piece of art:) Just a small question. While reading the sourcecode of jersey-client I wondered why the feature rich httpclient project wasn't used as the foundation layer.
Any particular reason?

Posted by Arnold Reuser on March 29, 2009 at 05:49 AM PDT #

In the twitter client code, for the postUpdate method, would it be simpler to have a way of populating the StatusBean with setters and just posting the resulting pojo ?

Posted by Patrick on March 30, 2009 at 12:47 AM PDT #

While Im at it :) Would it also be easier and possible to have an annotated pojo build the proper path value instead of having a path and post method when working with URI's (a bit like on the server side of things) ?

Posted by Patrick on March 30, 2009 at 01:53 AM PDT #

@Arnold Reuser: if you are referring to the apache httpclient project, there is another Jersey module (http://download.java.net/maven/2/com/sun/jersey/contribs/jersey-apache-client/ ) in Jersey contribs area, which uses just this as a basis.

@Patrick: Do you mean, instead of sending the form data, or using the StatusBean to populate the form data? If the former, i am not sure twitter API allows this. If the latter, you would need another code to take care of the transformation. Another option would be to use Form class.

Posted by Jakub on March 30, 2009 at 02:20 AM PDT #

@Jakub
Basically, add getters/setters to the status bean and post the populated bean instead of the map and the bean class.
For my other post, it was a general point (not for Twitter specifically). I would really like to have the ability to create a simple annotated pojo that, once populated, I could post, which would build the proper uri (like the path/pathParam/queryParam annotation for the server).

Posted by Patrick on March 30, 2009 at 03:06 AM PDT #

@Patrik:
when posting, only the map parameter matters for the request, the bean class parameter is used just to tell Jersey, what instance you want to get out of the response.

Regarding your POJO suggestion, could you, please, elaborate a bit and post a sample to users@jersey.dev.java.net of how the client code would look like?

Posted by Jakub on March 30, 2009 at 07:48 PM PDT #

I'm totally stuck with this...

MultivaluedMap queryParams = new MultivaluedMapImpl();
queryParams.add("param1", "val1");
queryParams.add("param2", "val2");
String s = webResource.queryParams(queryParams).get(String.class);

I'm using netbeans 6.5.1 and when I make a webresource from the client the webresource object has no .queryParams method???? It's driving me crazy as I can't pass parameters on get requests!!!

Posted by Confused on May 07, 2009 at 09:55 PM PDT #

@Confused: it depends on what version of Jersey is actually used, and i am not sure about what you get by default with NetBeans 6.5.1. The methods have changed in the recent versions of Jersey so that your code could look like: webResource.queryParam("param1","val1").get(String.class)
Please see https://jersey.dev.java.net/nonav/apidocs/1.1.0-ea/jersey/com/sun/jersey/api/client/WebResource.html for more details

Posted by Jakub on May 11, 2009 at 12:50 AM PDT #

is there not a way by which we can have a MVC based application consume the web services?

e.g. I want to develop a small application which has a login page and the auth is done by a restful webservice, and then the flow is directed to another JSP page using the 1.1 impl of JAX-RS in jersey.

Any example would be really helpful

Posted by Adhir Aima on June 02, 2009 at 08:37 PM PDT #

@Adhir: you might want to download http://download.java.net/maven/2/com/sun/jersey/samples/jersey-samples/1.1.1-ea-SNAPSHOT/jersey-samples-1.1.1-ea-SNAPSHOT-project.zip which contains a number of Jersey examples, and look at the Bookstore application there. It shows, how to use MVC with Jersey. It does not cover authentication part, but for this you could look at the atompub-contacts example (consists of 3 separate modules). Also please feel free to e-mail to users@jersey.dev.java.net if you have further questions.

Posted by Jakub on June 04, 2009 at 07:56 AM PDT #

Realy Impressive. Jersey examples, and look at the Bookstore application there. It shows, how queryParams.add("param1", "val1");
queryParams.add("param2", "val2");

Posted by Virtual girl on August 22, 2009 at 09:11 PM PDT #

How can I consume a restful web service that has a basic authentication mechanism. I mean how to pass the user credential through Client and WebResource?

Posted by Java Techie on September 01, 2009 at 04:56 PM PDT #

In the article, i have been directly adding the corresponding HTTP authentication header to each request. The simpler approach would be to employ com.sun.jersey.api.client.filter.HTTPBasicAuthFilter, and set it on the client like follows: client.addFilter(new HTTPBasicAuthFilter(username, password)); This should then automatically add the authentication header to all requests issued via web resources created from the client.

Posted by Jakub on September 02, 2009 at 03:49 AM PDT #

Great!
Just what I was searching for and what I was needing.
Thanks,
Paul Michiels
http://en.sondaletra.info/

Posted by Paul Michiels on November 05, 2009 at 06:35 PM PST #

I have used client.addFilter(new HTTPBasicAuthFilter(username, password)); to set the header from my client but in jax-rs webservice method when I am receiving it like "@HeaderParam("Authorization") String auth" then I am not getting the encoded value but some thing else.

How to get the same encoded value which was set by client (internally) at recieving end.

Posted by vishal on November 19, 2009 at 07:28 PM PST #

I'm trying to develop a simple client resful. The example I made work fine but I can't add an Authorization header with a user and password to my request !!! I need this data in the server side

Any idea why this doesn't work ?

Thanks

Here is the code
ClientConfig clientConfig = new DefaultClientConfig();
Client client = Client.create(clientConfig);

BASE64Encoder encoder = new BASE64Encoder();
String encodedCredential = encoder.encode( (username + ":" + password).getBytes() );
String authentication = "Basic " + encodedCredential;

WebResource webResource = client.resource("http://localhost:8087/extranet/resteasy/algoService/algos");
webResource.accept(MediaType.TEXT_PLAIN).
header(AUTHENTICATION_HEADER, authentication);

// body is a hard-coded string, with replacements for the variable bits
String response = webResource.get(String.class);

Posted by moundir jamal on November 20, 2009 at 12:00 AM PST #

Hi vishal
I resolve my problem and yours by the way

There's a problem on HTTPBasicAuthFilter in the method encodeCredentialsBasic(..).

You need juste to change this method.

I change it like this :

authentication = "Basic " + encode(username + ":" + password);

private String encode(final String string)
{
byte[] bytes = getBytes(string);
final int padding = (3 - (bytes.length % 3)) % 3;

bytes = zeroPad(bytes.length + padding, bytes);

final StringBuilder encoded = new StringBuilder();
for (int i = 0; i < bytes.length; i += 3)
{
final int threeBytes = (bytes[i] << 16) + (bytes[i + 1] << 8) + bytes[i + 2];
encoded.append(c_base64code.charAt((threeBytes >> 18) & 0x3f)).
append(c_base64code.charAt((threeBytes >> 12) & 0x3f)).
append(c_base64code.charAt((threeBytes >> 6) & 0x3f)).
append(c_base64code.charAt(threeBytes & 0x3f));
}
return encoded.substring(0, encoded.length() - padding) + "==".substring(0, padding);
}

private byte[] getBytes(final String string)
{
byte[] bytes;
try
{
bytes = string.getBytes("UTF-8");
}
catch (final UnsupportedEncodingException e)
{
bytes = string.getBytes();
}
return bytes;
}

private byte[] zeroPad(final int length, final byte[] bytes)
{
final byte[] padded = new byte[length];
System.arraycopy(bytes, 0, padded, 0, bytes.length);
return padded;
}
private static final String c_base64code = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz0123456789+/";

It works fine for me

Hope it will help

Posted by moundir jamal on November 20, 2009 at 01:27 AM PST #

@Moundir Jamal, Visal: there was a bug - https://jersey.dev.java.net/issues/show_bug.cgi?id=278 - in previous versions of Jersey, which indeed caused Jersey client to sent a wrong Authentication header in the request. The issue was fixed. And at least from the version 1.1.3-ea, (i am using this in another project) this feature should behave correctly. If you use something older, you might want to try to upgrade the Jersey version used in your application (the newest stable version is now 1.1.4) to see if it fixes your issue in a more ellegant way than encoding the header by yourself. If it still breaks, please feel free to re-open the bug. Thanks!

Posted by Jakub on November 23, 2009 at 03:01 AM PST #

what does curl has to do with jerserys.

Posted by Computer Service Toronto on October 03, 2010 at 05:33 PM PDT #

curl and jerseys how they are united?

Posted by Computer Service Toronto on October 03, 2010 at 05:34 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

edort

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today