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.

Comments

Popular posts from this blog

How To: GitHub projects in Spring Tool Suite

Spring 3 Part 7: Spring with Databases

Parse the namespace based XML using Python