Friday, August 27, 2010

Unit Testing Jersey REST service

In my last post i show how to create Rest service with Jersey api. I also try to give some idea about the basic concept of REST in this link. Now we should start testing before start to do anything else. I write one test case using Jersey Test Framework also create one client using Jersey client tool.

The Jersey Test Framework currently allows you to run your tests on any of the following three light weight containers:
• Embedded GlassFish
• Grizzly Web Server
• Lightweight HTTP Server
The framework is built over JUnit 4.x using Maven.
For details information on Jersey Test Framework check this post


Test case for ProductResource
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
import java.io.UnsupportedEncodingException;
import org.junit.Test;
import static org.junit.Assert.*;
import com.sun.jersey.test.framework.WebAppDescriptor;
import com.sun.jersey.test.framework.spi.container.http.HTTPContainerFactory;
import java.math.BigDecimal;
import org.model.Product;
import javax.ws.rs.core.MediaType;

/** *
* @author Kamal Hossain
*/
public class ProductServiceTest extends JerseyTest {

public static final String PACKAGE_NAME = "org.resources";
private WebResource ws;

public ProductServiceTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}

@Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();

}

@Test
public void testProductResponse() throws UnsupportedEncodingException {
ws = resource().path("product/kamal");
ClientResponse response = ws.accept(MediaType.APPLICATION_XML).get(ClientResponse.class);
assertEquals(200, response.getStatus());
System.out.println(response.getStatus());


}

@Test
public void testProductPrice() throws UnsupportedEncodingException {
ws = resource().path("product/kamal");
Product prod = ws.accept(MediaType.APPLICATION_XML).get(Product.class);
assertEquals(BigDecimal.valueOf(120), prod.getPrice());


}
}

Test case for ProductsResource
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.GenericType;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
import java.io.UnsupportedEncodingException;
import org.junit.Test;
import static org.junit.Assert.*;
import com.sun.jersey.test.framework.WebAppDescriptor;
import com.sun.jersey.test.framework.spi.container.http.HTTPContainerFactory;
import org.model.Product;
import java.util.Collection;
import java.util.List;
import javax.ws.rs.core.MediaType;

/** *
* @author kamal hossain
*/
public class ProductsServiceTest extends JerseyTest {
public static final String PACKAGE_NAME = 'org.resources';
private WebResource ws;

public ProductsServiceTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}

@Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();

}

@Test
public void testProductResponse() throws UnsupportedEncodingException {
ws = resource().path("product/kamal");
ClientResponse response = ws.accept(MediaType.APPLICATION_XML).get(ClientResponse.class);
assertEquals(200, response.getStatus());
System.out.println(response.getStatus());


}

@Test
public void testProductList() throws UnsupportedEncodingException {
ws = resource().path("products");
GenericType<Collection<Product>> genericType = new GenericType<Collection<Product>>(){};
List<Product> wsResult = (List<Product>) ws.accept(MediaType.APPLICATION_XML).get(genericType);
assertEquals(2, wsResult.size());

}    

}




You can also install firefox addon Poster to test services.

Monday, August 23, 2010

Jersey JAX-RS implementation


JAX-RS annotations

@Path Every Jersey resource has a URI pointing to it. A resource is identified with the @Path(“relative URI path”)annotation. This annotation is also used to define variables in URIs @Path(“relative URI path/{variable}”). Regular expression also allowed @Path("relative URI path/{variable:[a-zA-Z][a-zA-Z_0-9]}")

@GET, @PUT, @POST, @DELETE, and @HEAD are resource method designator annotations defined by JAX-RS and which correspond to the similarly named HTTP methods.

@Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client. can be applied at both the class and method levels. This annotation works with @GET, @POST, and @PUT. if the value of the
HTTP header Accept is application/xml, the method handling the request returns
a stream of MIME type application/xml

@Consumes annotation is used to specify the MIME media types of representations a resource can consume. This annotation works together with @POST and @PUT. the client sets
the Content-Type HTTP header and the framework delegates the request to the
corresponding handling method.

@PathParam to extract a path parameter from the path component of the request URL. This annotation is used together with @Path and in conjunction with @GET, @POST,
@PUT, and @DELETE. we don't use the @PathParam annotation with a POST request.

@QueryParam is used to extract query parameters from the Query component of the request.

@FormParam is slightly special because it extracts information from a request representation that is of the MIME media type "application/x-www-form-urlencoded" and conforms to the encoding specified by HTML forms

@DefaultValue annotation is used to set default value to the query parameter if not exist.

@MatrixParam extracts information from URL path segments.

@HeaderParam extracts information from the HTTP headers.

@CookieParam extracts information from the cookies declared in cookie related HTTP headers

@Provider annotation is used for anything that is of interest to the JAX-RS runtime

@Context  ServletConfig, ServletContext, HttpServletRequest HttpServletResponse and SecurityContextare available using this annotation









Let’s start with creating project.
  1. create web project
  2. To add Jersey JARs (download link https://jersey.dev.java.net/)
asm-3.1.jar ,jsr311-api-3.1.jar ,jersey-bundle-1.3.jar,jersey-json-1.3.jar

3. Open the file "web.xml" and modify the file to the following.


<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><br />
<servlet><br />
<servlet-name>WebService</servlet-name><br />
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class><br />
<init-param><br />
<param-name>com.sun.jersey.config.property.packages</param-name> <param-value>org.resources</param-value> </init-param><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<servlet><br />
<servlet-name>ServletAdaptor</servlet-name><br />
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<servlet-mapping><br />
<servlet-name>ServletAdaptor</servlet-name><br />
<url-pattern>/resources/*</url-pattern><br />
</servlet-mapping><br />
<servlet-mapping><br />
<servlet-name>WebService</servlet-name><br />
<url-pattern>/*</url-pattern><br />
</servlet-mapping><br />
<welcome-file-list><br />
<welcome-file>index.jsp</welcome-file><br />
</welcome-file-list><br />
</web-app><br />
<br />
<br />




The parameter "com.sun.jersey.config.property.package" defines in which package jersey will look for the web service classes. This property must point to your resources classes.

4. Create the following class.



package org.model;

import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement
public class Product {

String name;
String description;
BigDecimal price;
int limit;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public BigDecimal getPrice() {
return price;
}

public void setProce(BigDecimal price) {
this.price = price;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public int getLimit() {
return limit;
}

public void setLimit(int limit) {
this.limit = limit;
}
}




package org.resources;

import com.sun.jersey.spi.resource.Singleton;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.model.Product;



@Path("/product/{name}")
@Singleton
public class ProductResource {

@GET
@Produces(MediaType.APPLICATION_XML)
public Product getXml(@PathParam("name") String name) {
Product p = new Product();
p.setName(name);
p.setDescription("descript");
p.setPrice(new BigDecimal(120));
return p;
}

@GET
@Produces(MediaType.APPLICATION_JSON)
public Product getJson(@PathParam("name") String name) {
Product p = new Product();
p.setName(name);
p.setDescription("descript");
p.setPrice(new BigDecimal(120));
return p;
}

@GET
@Produces(MediaType.TEXT_HTML)
public String getHtml(@PathParam("name") String name) {
Product p = new Product();
p.setName(name);
p.setDescription("descript");
p.setPrice(new BigDecimal(120));
return " " + "" + "Name: " + p.getName() + "
Description: " + p.getDescription() + "
Price: " + p.getProce() + "" + " ";
}

@Path("/allproducts")
@GET
@Produces(MediaType.APPLICATION_XML)
public List getProducts() {
List pl = new ArrayList();
Product p = new Product();
p.setName("Product x");
p.setDescription("descript");
p.setPrice(new BigDecimal(120));
pl.add(p);

p = new Product();
p.setName("Product x-2");
p.setDescription("descript-2");
p.setPrice(new BigDecimal(140));
pl.add(p);
return pl;
}
}


package org.resources;

import org.model.*;
import com.sun.jersey.spi.resource.Singleton;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;


@Path("/products")
@Singleton
public class ProductsResource {

@GET
@Produces(MediaType.APPLICATION_XML)
public List getXml(@DefaultValue("10") @QueryParam("limit") int limit) {
List pl = new ArrayList();
Product p = new Product();
p.setName("Product x");
p.setDescription("descript");
p.setPrice(new BigDecimal(120));
p.setLimit(limit);
pl.add(p);

p = new Product();
p.setName("Product x-2");
p.setDescription("descript-2");
p.setLimit(limit);
p.setPrice(new BigDecimal(140));
pl.add(p);

return pl;
}

@GET
@Produces(MediaType.TEXT_HTML)
public String getHtml() {
Product p = new Product();
p.setName("Product x");
p.setDescription("descript");
p.setProce(new BigDecimal(120));
return " " + "" + "Name: " + p.getName() + "
Description: " + p.getDescription() + "
Price: " + p.getProce() + "" + " ";
}
}








5. Now deploy in web server. I use Apache Tomcat v6.0


In the next post i will show you different way to test this service. 

RESTfull webservice

REST stands for Representational State Transfer. REST defines a set of architectural principles by which you can design Web services that focus on a system's resources where as SOAP focus on messaging.

To become REST full following constrains must be satisfied.
  • Client Server system
  • Must be stateless.
  • Support caching system
  • Uniformly accessible


Abstractions that make a Restful system, namely resources, representations, Uri’s, and the HTTP request types that make up the uniform interface used for client/server data transfers.

A Restful resource can be anything that is addressable over the Web.

A representation is a temporal state of the actual data located in some storage device at the time of a request.

A Uniform Resource Identifier, or URI, in a Restful web service is a hyperlink to a
resource


We have four specific actions that we can take upon resources—Create, Retrieve, Update, and Delete (CRUD)


We can now map our CRUD actions to the HTTP methods POST, GET, PUT, and DELETE as follows:

Data action HTTP protocol equivalent
CREATE          POST
RETRIEVE       GET
UPDATE          PUT
DELETE          DELETE


As of today there are a couple of JAX-RS libraries used by the Java community:
Jersey, Restlet, RestEasy etc

check other post for REST implementation in Jersey JAX-RS.

AWS Services

      1.         Identity Access Management (IAM): Used to control Identity (who) Access (what AWS resources).                   1....