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
-
Java 6
-
Maven >=2
-
A webserver of your choice to deliver some XML and test basic authentication e.g. Apache, nginx, lighthttpd …
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 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“.
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.