Monday, February 27, 2012

RESTFul Servlets

I already wrote the blog on RESTFull Dynamic service proxy ( have a Coffee cup ). In this blog, address the use of Servlets, because Servlets are best candidates for RESTFul services. The main advantage is HTTPServlet which cover the HTTP communication completely.

RESTFul (HTTP method) HTTPServlet
GET doGet()
POST doPost()
PUT doPut()
DELETE doDelete()

In this blog, I just use doPost() to store Person object in the server side Map (employees) and get the complete list of people (employees) as a response. All of the implementation from the RESTFull Dynamic service proxy blog, except the PersonService, which is replaced by the PersonServlet.java as shown in the following listing.

package com.blogspot.ojitha.wsex7.web;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.blogspot.ojitha.wsex6.person.People;
import com.blogspot.ojitha.wsex6.person.Person;

/**
* Servlet implementation class PersonServlet
*/
@WebServlet("/PersonServlet")
public class PersonServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

People<Person> people = new People<Person>();

/**
* @see HttpServlet#HttpServlet()
*/
public PersonServlet() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {

try {
// unmarshall
JAXBContext jaxbCtx = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();
InputStream in = request.getInputStream();
Person person = (Person) unmarshaller.unmarshal(in);
in.close();
people.employees.put(person.getId(), person);

// marshall
marshallPeople(response);

} catch (ParserConfigurationException | JAXBException e) {
e.printStackTrace();
}

}

private void marshallPeople(HttpServletResponse response)
throws JAXBException, ParserConfigurationException, IOException {
JAXBContext jaxbCtx = JAXBContext.newInstance(People.class);
Marshaller marshaller = jaxbCtx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
OutputStream out = response.getOutputStream();
marshaller.marshal(people, out);
out.close();
}

/**
* @see HttpServlet#doPut(HttpServletRequest, HttpServletResponse)
*/
protected void doPut(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}

/**
* @see HttpServlet#doDelete(HttpServletRequest, HttpServletResponse)
*/
protected void doDelete(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}

}

You can use Curl tool to test the POST method as shown in the following figure.

image

There was a record with id=2, and I added the id=1 which is in the person.xml. The source code is available to download here .

Sunday, February 26, 2012

RESTFull Dynamic service proxy

A RESTFull provider implements the method public Source invoke(Source request) in the server side and the twin Dispatch object in the client side.  This way of handling the RESTFull is not easy. The best way is to use of JAX-RS as shown in my previous blog entries JAX-RS: 1 min RESTFul with JAXB  and  JAX-RS Tutorial: 1 min RESTFul service.

WSEx6

 

As shown in the above figure, The person is JavaBean with JAXB xml root as shown in the following listing. As well it compose an Address object for each and every Person.

package com.blogspot.ojitha.wsex6.person;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import com.blogspot.ojitha.wsex6.common.Address;

@XmlRootElement
@XmlType(propOrder={"id", "firstName", "lastName", "age","address"},
namespace="com.blogspot.ojitha.wsex6.person")
public class Person {
private String firstName;
private String lastName;
private int age;
private Integer id;
private Address address;

public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}



}

In the Address class, JAXB xml root is not defined because Address is composed by Person always and it is never exist its own.

package com.blogspot.ojitha.wsex6.common;

import javax.xml.bind.annotation.XmlType;

@XmlType(namespace="com.blogspot.ojitha.wsex6.common")
public class Address {
private int no;
private String street;
private String suburb;
private int postalCode;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getSuburb() {
return suburb;
}
public void setSuburb(String suburb) {
this.suburb = suburb;
}
public int getPostalCode() {
return postalCode;
}
public void setPostalCode(int postalCode) {
this.postalCode = postalCode;
}



}

To create java.util.Map is not very well supported by JAXB. Therefore, I’ve created another class that holds global variable( I know this violate the object-oriented encapsulation concept, but this is the best shortcut for the java maps, otherwise you have to create adapter ). This class is People it holds two variables for employees and managers.

package com.blogspot.ojitha.wsex6.person;

import java.util.HashMap;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(namespace="com.blogspot.ojitha.wsex6.person")
public class People<T extends Person> {

public HashMap<Integer, T> managers = new HashMap<Integer, T>();
public HashMap<Integer, T> employees = new HashMap<Integer, T>();

}

Next is PersonService which implements the RESTFull service to add a new employee. For example,

package com.blogspot.ojitha.wsex6.service;

import javax.annotation.Resource;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.BindingType;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;

import org.w3c.dom.Document;

import com.blogspot.ojitha.wsex6.person.People;
import com.blogspot.ojitha.wsex6.person.Person;

@WebServiceProvider
@BindingType(value=HTTPBinding.HTTP_BINDING)
public class PersonService implements Provider<Source> {


@Resource
WebServiceContext ctx;

People<Person> people = new People<Person>();

@Override
public Source invoke(Source request) {
//web service context should exist
if (ctx == null) throw new RuntimeException("Server error!...");
MessageContext msgCtx = ctx.getMessageContext();
String mth = (String)msgCtx.get(MessageContext.HTTP_REQUEST_METHOD);

try {
if ("GET".equalsIgnoreCase(mth)) return doGet();
if ("POST".equalsIgnoreCase(mth)) return doPost(request);

} catch (JAXBException | ParserConfigurationException e) {
e.printStackTrace();
}

return null;
}

private Source doGet() throws JAXBException, ParserConfigurationException{
return marshallPeople();

}

private Source marshallPeople() throws JAXBException, ParserConfigurationException{
JAXBContext jaxbCtx = JAXBContext.newInstance(People.class);
Marshaller marshaller = jaxbCtx.createMarshaller();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(people, doc);
DOMSource source = new DOMSource(doc);
return source;
}

public Source doPost(Source request) throws JAXBException, ParserConfigurationException{
JAXBContext jaxbCtx = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();
Person person = (Person)unmarshaller.unmarshal(request);
people.employees.put(person.getId(), person);
return marshallPeople();

}

public static void main(String[] args){
Endpoint.publish("http://127.0.0.1:9875/WSEx6", new PersonService());
}

}

As shown in line 73 to 77, the request xml (which is in the body of the POST) un-marshalled and read in the Person object and added to the People’s employee map. As a response entire People object is sending back to the client (lines 59-69).

POST /WSEx6 HTTP/1.1
Content-Type: text/xml
User-Agent: JAX-WS RI 2.2.4-b01
Host: 127.0.0.1:9876
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 212

<person>
<id>2</id>
<firstName>AlanTT</firstName>
<lastName>BoarderTT</lastName>
<age>20</age>
<address>
<no>11</no>
<postalCode>2900</postalCode>
<street>Ankatel St</street>
<suburb>GreenwayTT</suburb>
</address>
</person>

In the request and the following response, the highlighted line shows the root of the xml. This is the important of @XMLRootElement 

HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: text/xml
Date: Sun, 26 Feb 2012 06:39:40 GMT

1fb
<people>
<managers></managers>
<employees>
<entry>
<value>
<firstName>Alan</firstName>
<lastName>Boarder</lastName>
<age>20</age>
<address>
<no>11</no>
<postalCode>2900</postalCode>
<street>Ankatel St</street>
<suburb>Greenway</suburb>
</address>
</value>
</entry>
<entry>
<key>2</key>
<value>
<id>2</id>
<firstName>AlanTT</firstName>
<lastName>BoarderTT</lastName>
<age>20</age>
<address>
<no>11</no>
<postalCode>2900</postalCode>
<street>Ankatel St</street>
<suburb>GreenwayTT</suburb>
</address>
</value>
</entry>
</employees>
</people>
0

Above xml is generated using TCPMON tool. Lets see the Client which use dispatch method. Here the client application

package com.blogspot.ojitha.wsex6.client;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;

import org.w3c.dom.Document;

import com.blogspot.ojitha.wsex6.common.Address;
import com.blogspot.ojitha.wsex6.person.People;
import com.blogspot.ojitha.wsex6.person.Person;

public class Client {

Dispatch<Source> dispatch;
public Client() {
URI uri=null;
try {
uri = new URI("");
} catch (URISyntaxException e) {
e.printStackTrace();
}

QName serviceName = new QName("PersonService", uri.toString());
QName portName = new QName("port",uri.toString());
String endpointAddress = "http://127.0.0.1:9876/WSEx6";

//now create service dispatcher
Service service = Service.create(serviceName);
service.addPort(portName, HTTPBinding.HTTP_BINDING, endpointAddress);
dispatch = service.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
}


public People<Person> request(String verb, Person person) throws JAXBException, ParserConfigurationException{
//create request
Map<String, Object> requestCtx = dispatch.getRequestContext();
requestCtx.put(MessageContext.HTTP_REQUEST_METHOD, verb);

JAXBContext jaxbCtx = JAXBContext.newInstance(People.class);
Marshaller marshaller = jaxbCtx.createMarshaller();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(person, doc);
//marshaller.marshal(people, doc);
DOMSource sourceRequest = new DOMSource(doc);
Source sourceResponse = dispatch.invoke(sourceRequest);

Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();
return (People<Person>)unmarshaller.unmarshal(sourceResponse);
}

public static void main(String[] args){

Client client = new Client();
Person p = new Person();
p.setFirstName("AlanTT");
p.setLastName("BoarderTT");
p.setAge(20);
p.setId(3);
Address a = new Address();
a.setNo(11);
a.setPostalCode(2900);
a.setStreet("Ankatel St");
a.setSuburb("GreenwayTT");
p.setAddress(a);
try {
People people = client.request("POST", p);
System.out.println(people.employees.size());
} catch (JAXBException | ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

As shown in the above listing, in the line 67, dispatch is executed. This invoke the server side invoke() method as shown in the line 37 in the PersonService.java listing. Download the source from here.

Sunday, February 19, 2012

JAX-RS: 1 min RESTFul with JAXB

This is second post of the JAX-RS Tutorial: 1 min RESTFul service post extended to use JAXB. Follow the first tutorial to understand the Grizzly framework usage.

restexamplefirst

I’ve created two classes Person and the Address as shown in the above class diagram.

package com.blogspot.ojitha.rest.example.first;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Person {
	private String firstName;
	private String lastName;
	private Long id;
	private Address address;
	
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
}

 

package com.blogspot.ojitha.rest.example.first;

public class Address {
	private String street;
	private String suburb;
	private String state;
	private int postCode;
	public String getStreet() {
		return street;
	}
	public void setStreet(String street) {
		this.street = street;
	}
	public String getSuburb() {
		return suburb;
	}
	public void setSuburb(String suburb) {
		this.suburb = suburb;
	}
	public String getState() {
		return state;
	}
	public void setState(String state) {
		this.state = state;
	}
	public int getPostCode() {
		return postCode;
	}
	public void setPostCode(int postCode) {
		this.postCode = postCode;
	}

}

The most important annotation is @XmlRootElement which is from JAXB. Following service is capable of passing Person and Address classes as XML to the consumer.

package com.blogspot.ojitha.rest.example.first;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.xml.ws.http.HTTPException;

@Path("personservice")
public class PersonService {

	Map<Long, Person> people = new HashMap<Long, Person>();
	public PersonService(){
		Person p = new Person();
		p.setId(new Long(1));
		p.setFirstName("Ojitha");
		p.setLastName("Kumanayaka");
		Address a = new Address();
		a.setState("Tenison Woods");
		a.setSuburb("Bonython");
		a.setState("ACT");
		a.setPostCode(2905);
		p.setAddress(a);
		people.put(p.getId(), p);
	}
	
	@GET
	@Path("{id}")
	@Produces({"application/xml"})
	public Person findById(@PathParam("id")Long id){
		Person p = null;
		p = people.get(new Long(id));
		if (p!=null) return p;
		else throw new HTTPException(405);
	}
}

The most important element to consider is @Produces in the above service. You will get the following result when you test the application.

<person>
	<address>
		<postCode>2905</postCode>
		<state>ACT</state>
		<suburb>Bonython</suburb>
	</address>
	<firstName>Ojitha</firstName>
	<id>1</id>
	<lastName>Kumanayaka</lastName>
</person>