RDF Gateway and RESTful Web Services

| | Comments (5) | TrackBacks (0)

I was looking into RDF Gateway the other week, and found it to be a very interesting product. It provides an RDF store which can be accessed using SPARQL or T-SQL and persists its data in a SQL Server database. It also allows you to expose your SPARQL queries by creating RDF Server Pages (RSPs). This technology is similar in nature to ASP or JSP in that it mixes markup with SPARQL quires and a JavaScript-like language called RDF Query Language (RQL) similarly to the way ASP embeds JScript/VBScript in markup and JSP intermixes it with Java. RSP documents are interpreted by RDF Gateway, which runs as a Windows NT service. These scripts are requested over HTTP, making them accessible to a wide array of applications and programming environments. After just a cursory investigation, it isn't hard to see how one could use this product to expose an RDF store as a collection of RESTful Web services. In case you're having trouble visualizing this, have a look at the following figure that shows the overall architecture of how this might be done:

In my view, each RESTful service would have one RSP script. The client would indicate which action or operation they wanted to invoke by setting a parameter on the query string. The RSP page would grab this argument, and invoke the corresponding function to fulfill that specific type of request. I realize that this design would make the service an REST-RPC hybrid (using Richardson and Ruby's term which they coined in their book RESTful Web services), but it would avoid the need to use something like Squid simply to do URL rewriting (since RDF Gateway doesn't have such abilities natively).

To show how this would work, I'll describe the idiom used by the different RSP pages above. Because each service would have one RSP page, it would perform the role of "dispatcher." It would grab the action off the query string, switch on it, and call the appropriate function. To demonstrate how this might look, consider the following snippet of RQL code:

<%
import "operations/a1.rql";
import "operations/a2.rql";
import "/common.rql";
%>
<Response>
<%
Response.contentType = "text/xml";

var action = Request["action"]; // Dispatcher

switch (action)
{
case "a1":
a1();
break;
case "a2":
a2();
break;
default:
_Error(); // Imported from common.rql
break;
}
%>
</Response>

This code fragment is the service's entry point, its "main" if you will. To keep the code organized, each of the service's operations is fulfilled by different functions which are factored out into separate RQL script and imported in the main RSP page. Each of the RSP pages for the different services would be almost identical. (I can imagine a little code generator to produce this code and a plug-in for Visual Studio or other editors to call it.)

When the action is fired, it would process the body of the HTTP request, use SPARQL to update or query the RDF store, and return the results in XML or JSON format. (The snippet above precludes the use of JSON, but that could be easily be changed and which format should be returned could be based on the value of a query string argument, e.g., json=1.)

Like the format of the results, the contents of the request would be XML or JSON. In the latter case, I think that RQL's eval statement could be used to turn it into an object that could be processed directly. In the case of XML, a little munging would have to be done, and, I'm not sure what support RDF Gateway has for this. Once converted to objects that could be used in the RQL script, they would be passed to SPARQL to perform the operation-specific processing as shown in the following snippet:

function a1()
{
    var input;    

// RQL doesn't have a contentType property on their
// Request object, so we have to see if the input
// is JSON by trying to eval it. I realize that
// this makes the system susceptible to script injection,
// but this is just a POC.
//
// The object returned from eval and MungeXml contains
// two properties, lbound (lower bound) and ubound (upper
// bound).

try {
input = eval(Request.binaryRead());
input.lbound; input.ubound;
}

catch(ex)
{
input = MungeXml();
}

// Use input parameters in a SPARQL expression to update
// or query the datastore.

var rs = (SELECT TOP 5 ?p ?s ?o USING mydata
WHERE {?p ?s ?o} and between(?o, #(input.lbound),
#(input.ubound)));

    Response.Write("<data>");    

    for (; rs.EOF; rs.MoveNext())
    {
        Response.Write("<datum><p>");
        Response.Write(rs["p"]);
        Response.Write("</p><s>");
        Response.Write(rs["s"]);
        Response.Write("</s><o>");
        Response.Write(rs["o"]);        
        Response.Write("</o></datum>");
    }    

    Response.Write("</data>");
}

As I mentioned at the beginning of this post, I've only had time to perform a short investigation into RDF Gateway. So, if there are glaring problems or emissions with the ideas presented here, please let know as a learn more about this exciting product.

If you would like to get started with RDF Gateway, surf over to Intellidimension's Web site and request a 60 day trial license by emailing [email protected].