Back

Friday, July 20, 2012

RESTFul Web Service Access

It was quite clear to me that jCoreDB should provide Web Services on each layer to have the most of the flexiblity available if you want to use it in an service oriented environment. The first implementation which I did for this purpose was a SOAP one. And I still think that SOAP has some advantages. SOAP is by default well defined. This allows to create a Web Service Client for about every platform with less effort. In fact the Client code is generated automatically from the Web Service's descriptor. There are also disadvantages. One big one is that SOAP is very 'chatty'. In order to be such well defined, it's required to transport a lot of data about the data (so meta data). This can become an issue if you transfer a lot of data or if performance is a requirement. So why seems REST to be cooler? REST is stateless and it is light weighted (only the HTTP methods are used - GET, DELETE, PUT and POST). Not that amazing so far? It means especially, that a RESTFul Web Service provides you HTTP based access to resources. So the client/server communication happens not message based, but resource based. In SOAP the client would send a message like 'Please call the following method on the server side and send me it's result'. In the REST world you just directly access resource which is then dynamically produced on server side. Our RESTFul Web Services will use JSON as the resource format. This has 2 reasons. The first one is that JSON is really as powefull as XML, but less 'chatty'. The other second one is that JSON can be interpreted out of the box by ever Java Script application. So a Java Script based Web Application (and there are more and more ...) can use the data just out of the box.
 
So what would a RESTFul Service require:
  • It needs to talk HTTP, which means it should at least be able to handle GET requests. 
  • Dynamic resources are required. They are generated dependent on the request parameters those the client passes to the server. The resources must be mapped to URL-s.
  • A basic authentication should work. So it's only possible to access a specifc resource if a previous authentication was successful.
Maybe the following implementation is a bit strange, because I know that there are RESTFul frameworks based on JAX-RS in the Java world. Seems to be no reason to do it by my own ;-).
So a very simple HTTP server was developed. One advantage of it is that you do not need a Servlet Container in order to host the service (Yes, we could have used the Endpoint classes of Java ...). It's now possible to provide several services on several ports, even if you deploy the jCoreDB core to a servlet container like Tomcat. So the services are Servlet Container independent. Back to the HTTP Server: My HTTPServer class implements the IServer interface. This means that it has a 'start', 'stop' and 'status' method. The primarily method is named 'doGet'. It has 3 parameters 'getHeader', 'authHeader' and 'out'. The first one is used to pass the GET request information. The second one contains the basic HTTP authentication information. 'out' is a printstream which is used to return the result to the client. The next part of this lightweighted REST framework is an abstract class (implements IResource) with the name AResource. Such a resource gets constrcuted by passing a Hashmap of parameters and the target output stream. AResource has one abstract method which is named 'create'. This method is used to create the dynamic resource on demand. Such a resource needs to be annotated in order to map it to an URL. Therefore the annotation interface WebResourceURL was created. Then Java's reflection mechanisms are used to find the resource which is mapped to a specific URL. Here the source code to create the resource as part of the HTTP server's doGET method:

Class c = HTTPResourceFinder.getClassByURL(urlprefix);
 

if (c != null) { 

    Constructor ctx = c.getConstructor(new Class[] {
       PrintStream.class,
       new HashMap<string, string="">().getClass() });

    
   Object[] args = new Object[] { out, getParams(url) };

   IHTTPResource resource = (IHTTPResource) ctx.newInstance(args);

   resource.create(); 
}

The annotation interface looks quite simple:

@Retention(RetentionPolicy.RUNTIME)
public @interface WebResourceURL  {

    String value();
}


To get the list of available containers a ContainerListResouce was created. Here its code:

@WebResourceURL(value = "/listcontainers")
public class ContainerListResource extends AHTTPResource { 


  public ContainerListResource(PrintStream out, HashMap<String, String> params) {
        super(out, params);
    }

    @Override
    public void create() {
   
        IFileSystem fs = FileSystemRegistry.get(Configs.getFSConfig().getRootDir());
        IContainer[] cs = fs.getContainers();
               
        out.println("{");
        out.println("\"containers\":" + "[");
       
       
        for (int i = 0; i < cs.length; i++)
        {
             IContainer c = cs[i];

             if (i < cs.length-1) out.println("{\"id\":" + c.getId() + "}" + ",");
             else out.println("{\"id\":" + c.getId() + "}");               
        }
       
       
        out.println("]");
        out.println("}");
    }

 }

Another resource is the one to get a specific container details:

@WebResourceURL(value="/container")
public class ContainerResource extends AHTTPResource{

    /**
     * The container's id
     */
    int id;
   
    /**
     * The resource constructor
     * @param out
     */
    public ContainerResource(PrintStream out, HashMap<String,String> params) {
   
        super(out, params);
        this.id = Integer.parseInt(params.get("id"));
    }
   
    @Override
    public void create() {
       
        IFileSystem fs = FileSystemRegistry.get(Configs.getFSConfig().getRootDir());
        IContainer c = fs.getContainer(id);
       
        if (c!=null)
        {
       
        out.println("{");
       
        out.println("\"id\":" + c.getId() + ",");
        out.println("\"parentpath\":" + "\"" + c.getParentPath() + "\"" + ",");
        out.println("\"numofsegs\":" + c.getNumOfSegs() + ",");
        out.println("\"segments\":" + "[");
       
       
        for (int i = 0; i < c.getSegments().length; i++)
        {
             ISegment s = c.getSegments()[i];

             if (i < c.getSegments().length-1) out.println("{\"id\":" + s.getId() + "}" + ",");
             else out.println("{\"id\":" + s.getId() + "}");               
        }
       
       
        out.println("]");
       
       
        out.println("}");
       
        }
        else
        {
            out.println("{");
            out.println("}");
        }
    }
   
...

}

 I will soon publish the source code of the above mentioned components. So maybe you like this lightweighted RESTFul Web Service framework ( only a few classes: HTTPServer, HTTPConstants, HTTPResourceFinder, IResource, AResource, WebResourceURL) which exists now in the context of jCoreDB.

No comments:

Post a Comment