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>
    single customer xml 300x181

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 ;)

    rest client unit test 222x300
  • 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 GitHub .. you may download it there or check it out using

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

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“.

Article Updates

  • 2015-08-06: Links to other REST articles of mine added.

  • 2015-10-22: Link list updated.