Creating a REST Client Step-by-Step using JAX-RS, JAX-B and Jersey

November 25th, 2010 by

Often in a developer’s life there is a REST service to deal with and nowadays one wants a fast and clean solution to create a client for such a service.

The following tutorial shows a quick approach using JAX-RS with its reference implementation, Jersey in combination with JAX-B for annotation driven marshalling between XML or JSON structures and our Java-Beans.

 

Prerequisites

The following stuff is needed to run the following examples and code samples

Project Setup

The same procedure as every year  tutorial Miss Sophie

  • Create a new Maven project using your beloved IDE and the Maven plugin or by typing
    mvn archetype:generate
  • Add the following dependencies for Jersey the reference implementation of JAX-RS, JSON support and for JUnit for our tests to your pom.xml
    <properties>
     <jersey.version>1.15</jersey.version>
    </properties>
     
    <dependencies>
     <dependency>
     <groupId>com.sun.jersey</groupId>
     <artifactId>jersey-client</artifactId>
     <version>${jersey.version}</version>
     </dependency>
     <dependency>
     <groupId>com.sun.jersey</groupId>
     <artifactId>jersey-json</artifactId>
     <version>${jersey.version}</version>
     </dependency>
     <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.10</version>
     <scope>test</scope>
     </dependency>
    </dependencies>
  • Specify Java 6 for the Maven compiler plugin
    <build>
     <plugins>
      <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <configuration>
        <source>1.6</source>
        <target>1.6</target>
       </configuration>
      </plugin>
     </plugins>
    </build>
  • My final pom.xml is this
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.hascode.tutorial</groupId>
    <artifactId>rest-client-sample</artifactId>
    <version>0.0.1</version>
    <url>https://www.hascode.com</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jersey.version>1.15</jersey.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    </project>

Analyzing the REST Service

You can use your favourite browser or – if you prefer the command line – curl to test the response from the REST server .. but we need to set up our server first..

  • You need to configure your webserver to deliver the following XML when the following URL of your webserver is called: “/customer.xml”
    <?xml version="1.0" ?>
    <customer id="123">
      <name>I R testuser</name>
      <customer_email>test@hascode.com</customer_email>
      <website>https://www.hascode.com</website>
    </customer>
  • Test the response using curl or your web browser
    user@host:~$ curl http://localhost/customer.xml
    <?xml version="1.0" ?>
    <customer id="123">
      <name>I R testuser</name>
      <customer_email>test@hascode.com</customer_email>
      <website>https://www.hascode.com</website>
    </customer>

So what do get? A customer with an id, a name, an email and a website .. let’s create a bean from this information..

  • Create a new Java class named CustomerBean like this one
    package com.hascode.tutorial.rest.bean;
     
    public class CustomerBean {
    	private Long	id;
    	private String	name;
    	private String	email;
    	private String	website;
     
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getEmail() {
    		return email;
    	}
    	public void setEmail(String email) {
    		this.email = email;
    	}
    	public String getWebsite() {
    		return website;
    	}
    	public void setWebsite(String website) {
    		this.website = website;
    	}
    }

Nothing magic so fare here .. just a plain old java bean with getters and setters.

Creating the REST Client with JAX-RS / Jersey

In the next step we need a client to query our REST service and for handling request parameters and authentication.

Jersey the open source reference implementation for JAX-RS (JSR-311) make life very easy here ..

  • Create the following class for our rest client named  RestClient
    package com.hascode.tutorial.rest.service;
     
    import com.hascode.tutorial.rest.bean.CustomerBean;
    import com.sun.jersey.api.client.Client;
    import com.sun.jersey.api.client.WebResource;
     
    public class RestClient {
    	public CustomerBean getCustomer() {
    		Client client = new Client();
     
    		WebResource webResource = client.resource("http://localhost/customer.xml");
    		return webResource.get(CustomerBean.class);
    	}
    }

Handling Basic Authentication

Dealing with basic http authentication is quite easy using the client’s filter method

client.addFilter(new HTTPBasicAuthFilter("USERNAME", "PASSWORD"));

Request Parameters

You’re able to pass request parameters to the client by using a generic MultivaluedMap

MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.add("queryTerm", "someterm");
 
WebResource webResource = client.resource("http://localhost/customer.xml");
return webResource.queryParams(queryParams).get(CustomerBean.class);

Finally our REST client with query parameters and basic http authentication would look like this

package com.hascode.tutorial.rest.service;
 
import javax.ws.rs.core.MultivaluedMap;
 
import com.hascode.tutorial.rest.bean.CustomerBean;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.core.util.MultivaluedMapImpl;
 
public class RestClient {
	public CustomerBean getCustomer() {
		Client client = new Client();
		client.addFilter(new HTTPBasicAuthFilter("USERNAME", "PASSWORD"));
 
		MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
		queryParams.add("queryTerm", "someterm");
 
		WebResource webResource = client.resource("http://localhost/customer.xml");
		return webResource.queryParams(queryParams).get(CustomerBean.class);
	}
}

JAX-B Marshalling

If we tried to run the rest client without further modification it would fail for sure because we need some information for marshalling the XML data structure to the bean.

This is where JAX-B come into its own – these annotations save us a lot of time:

  • XmlRootElement – used to map the a root element – in our case the XML element “<customer>” to CustomerBean. Because we named our class CustomerBean and not Customer we need to specify the mapped name in the annotation (XmlRootElement(name=”customer”))
  • XmlElement – maps a single element. We need it for our e-mail field (yes, that is the reason for the ugly name “customer_email” in the sample xml ..)
  • XmlAttribute – maps an attribute
  • For complete list of available JAX-B annotations take a look at its API documentation

Mapping a single Element

Applying the annotations above to our CustomerBean it should look like this now ..

package com.hascode.tutorial.rest.bean;
 
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement(name = "customer")
public class CustomerBean {
	private Long	id;
	private String	name;
	private String	email;
	private String	website;
 
	@XmlAttribute
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
 
	@XmlElement(name = "customer_email")
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getWebsite() {
		return website;
	}
	public void setWebsite(String website) {
		this.website = website;
	}
}

Mapping a generic List of Elements

It is predictable that we sometimes need to fetch a list of objects from a REST service.. don’t panic – JAX-RS allows us to fetch generic, typesafe collections from a rest service .. I really love this

  • To make this work you first need to create a generic type like this one
    GenericType<Collection<CustomerBean>> customerType = new GenericType<Collection<CustomerBean>>() {};
  • Using this type we’re now able to pull a typesafe collection of CustomerBeans from the REST service
    webResource.get(customerType)
  • Let’s add this in a method called getAllCustomers to our RestClient class .. this is the complete class
    package com.hascode.tutorial.rest.service;
     
    import java.util.Collection;
     
    import com.hascode.tutorial.rest.bean.CustomerBean;
    import com.sun.jersey.api.client.Client;
    import com.sun.jersey.api.client.GenericType;
    import com.sun.jersey.api.client.WebResource;
     
    public class RestClient {
    	public CustomerBean getCustomer() {
    		Client client = new Client();
     
    		WebResource webResource = client.resource("http://localhost/customer.xml");
    		return webResource.get(CustomerBean.class);
    	}
     
    	public Collection<CustomerBean> getAllCustomers() {
    		GenericType<Collection<CustomerBean>> customerType = new GenericType<Collection<CustomerBean>>() {
    		};
     
    		Client client = new Client();
     
    		WebResource webResource = client.resource("http://localhost/customers.xml");
    		return webResource.get(customerType);
    	}
    }

Testing

Having written so much code for now we want to test if the stuff really works.

  • First we need some more customers to test our new collection-returning-method – that’s why we need to make our webserver deliver the following XML when the URL “/customers.xml” is called as shown in the method above..
    <?xml version="1.0" ?>
    <customers>
     <customer id="123">
      <name>I R testuser</name>
      <customer_email>test@hascode.com</customer_email>
      <website>https://www.hascode.com</website>
     </customer>
     <customer id="456">
      <name>Mee testuser too</name>
      <customer_email>test2@hascode.com</customer_email>
      <website>https://www.hascode.com/tag/jax-rs</website>
     </customer>
     <customer id="789">
      <name>some developer</name>
      <customer_email>test3@hascode.com</customer_email>
      <website>https://www.hascode.com/about</website>
     </customer>
    </customers>
  • We want to be able to marshal json, too so be sure to have added the dependency to jersey-json to your pom.xml and add the following file named customer.json to your src/test/resources directory
    {
      "@id":123,
      "name":"I R testuser",
      "customer_email":"test@hascode.com",
      "website":"https://www.hascode.com"
    }
  • Create a class named RestClientTest in src/test/java containing tests for both methods in our rest client
    package com.hascode.tutorial.rest.service;
     
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    import static org.junit.Assert.assertTrue;
     
    import java.util.Collection;
     
    import org.junit.Test;
     
    import com.hascode.tutorial.rest.bean.CustomerBean;
     
    public class RestClientTest {
     @Test
     public void testGetCustomer() {
       RestClient client = new RestClient();
       CustomerBean customer = client.getCustomer();
       assertNotNull(customer);
       assertTrue(123l == customer.getId());
       assertEquals("I R testuser", customer.getName());
       assertEquals("test@hascode.com", customer.getEmail());
       assertEquals("https://www.hascode.com", customer.getWebsite());
     }
     
     @Test
     public void testGetAllCustomers() {
       RestClient client = new RestClient();
       Collection<CustomerBean> customers = client.getAllCustomers();
       assertNotNull(customers);
       assertEquals(3, customers.size());
     }
     
     @Test
     public void testGetAllCustomersAsJson() {
       RestClient client = new RestClient();
       CustomerBean customer = client.getCustomerByJson();
       assertNotNull(customer);
       assertTrue(123l == customer.getId());
       assertEquals("I R testuser", customer.getName());
       assertEquals("test@hascode.com", customer.getEmail());
       assertEquals("https://www.hascode.com", customer.getWebsite());
     }
    }
  • Run the unit test and enjoy ;)

  • If you want to take a deeper look on how to write tests for RESTful web services there are two articles of mine that might be of interest for you: “REST-assured vs Jersey-Test-Framework: Testing your RESTful Web-Services” and  “Testing RESTful Web Services made easy using the REST-assured framework”

Running the REST Service

If you’ve got python installed, use the following command to start a simple HTTP server .. otherwise use Apache, netcat, nginx, node.js or any other tool of choice :)

cd src/test/resources
 
python -m SimpleHTTPServer 8080

Download Sources

I have put the sources for the examples here on Bitbucket .. you may download it there or check it out using

hg clone http://bitbucket.org/hascode/jaxrs-rest-client

Troubleshooting

  • com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions – Class has two properties of the same name “id”: This happens when you do not put the @XmlElement / @XmlAttribute on the getter but on the field

JAX-RS 2.0 / JSR-339

*update* Meanwhile the JAX-RS standard has evolved so if you’re interested in new features and working examples, please feel free to have a look at my article “JAX-RS 2.0 REST Client Features by Example“.

Resources

Additional REST articles of mine

Please feel free to have a look at these tutorials of mine covering different aspects of handling or creating RESTful webservices.

Article Updates

  • 2015-08-06: Links to other REST articles of mine added.
  • 2015-10-22: Link list updated.

Tags: , , , , , , , , ,

9 Responses to “Creating a REST Client Step-by-Step using JAX-RS, JAX-B and Jersey”

  1. Ankit Says:

    Nice tutorial.

  2. 204 Status Code in Jersey | Eduard Unruh Says:

    [...] Jersey REST Client Tutorial [...]

  3. Pulkit Singhal Says:

    Hey, thanks for the tutorial, just a comment on the formatting.
    The following shows the {}; when I copy paste but its not viewable when reading the blog:
    new GenericType<Collection>() {};

  4. Walter Says:

    What if I want to do the same thing using JSON in my service instead of xml?

  5. micha kops Says:

    You should add the following dependency to your project so that there is a message body reader for this MIME type (application/json).

    <dependencygt;
    <groupIdgt;com.sun.jersey</groupIdgt;
    <artifactIdgt;jersey-json</artifactIdgt;
    <versiongt;1.15</versiongt;
    </dependencygt;

    I have updated the tutorial to include a test handling JSON data..

  6. Kumar Says:

    Here are my problems -
    I have a JAX-RS Restful webservice. This resource/webservice is asset for us. I am exposing this service url to third party client that would call this service resource. I want to protect this service from another authorised client/vendors.
    Thus my question is -
    How to protect this.
    I thought to include an API key and the API key should be matched with a particular client API Key. And I also need to match the client url address means from whcih url the request is coming.
    How to do this. How to get the URL that is calling the our rest service. I am deploying my rest in Tomcat 7.0.
    @Path(“report”)
    @Produces(javax.ws.rs.core.MediaType.APPLICATION_XML)
    public class DnaReportResource {

    @GET
    @Produces(javax.ws.rs.core.MediaType.APPLICATION_XML)
    public void getDnaReport(@Context UriInfo uri) {
    System.out.println(uri.getRequestUri());
    System.out.println(uri.getPath());
    System.out.println(uri.getAbsolutePath());
    System.out.println(uri.getBaseUri());

    //MultivaluedMap queryParams = uri.getQueryParameters();
    }
    }

  7. javadevtech Says:

    Generates errors, Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.17:test

  8. micha kops Says:

    Any more detailed information? :)

  9. Ishi Says:

    Hi,
    Can we run parallel test in Rest Assured?
    Can we create a test suite?

Search
Categories