Creating a REST Client Step-by-Step using JAX-RS, JAX-B and Jersey
November 25th, 2010 by Micha KopsOften 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.
Contents
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 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
- Jersey Project Website
- Jakub Podlesak. Consuming RESTful Web Services With the Jersey Client API. Sun Developer Blogs
- Jersey User Guide
- JAX-B Mapping Annotations List
- Blaise Doughan. XmlAdapter – JAXB’s Secret Weapon
- Apache Webserver Website
- nginx Webserver Project
- Lighthttpd Webserver Site
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.
- Integrating Swagger into a Spring Boot RESTful Webservice with Springfox
- Documenting RESTful Webservices in Swagger, AsciiDoc and Plain Text with Maven and the JAX-RS Analyzer
- JAX-RS Server API Snippets
- JAX-RS 2.0 REST Client Features by Example
- Testing RESTful Web Services made easy using the REST-assured Framework
- REST-assured vs Jersey-Test-Framework: Testing your RESTful Web-Services
- Creating REST Clients for JAX-RS based Webservices with Netflix Feign
Article Updates
- 2015-08-06: Links to other REST articles of mine added.
- 2015-10-22: Link list updated.
Tags: jax-b, jax-rs, jaxrs, jersey, jsr-311, marshalling, maven, rest, tutorial, webservice
February 17th, 2011 at 2:35 pm
Nice tutorial.
February 9th, 2012 at 4:58 pm
[...] Jersey REST Client Tutorial [...]
September 3rd, 2012 at 7:45 pm
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>() {};
November 6th, 2012 at 10:41 pm
What if I want to do the same thing using JSON in my service instead of xml?
November 7th, 2012 at 7:34 pm
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..
September 27th, 2013 at 1:47 pm
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();
}
}
November 20th, 2015 at 4:41 pm
Generates errors, Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.17:test
November 24th, 2015 at 9:05 pm
Any more detailed information? :)
May 11th, 2017 at 1:51 pm
Hi,
Can we run parallel test in Rest Assured?
Can we create a test suite?