REST for Java developers, Part 3: NetKernel

A next-generation environment where SOA meets multicore

1 2 3 Page 2
Page 2 of 3
<export>
    <uri>
      <match>active:fetch-customer.*</match>
      <match>ffcpl:/customer.*</match>
      ...
    </uri>
  </export>

These are exported by the module definition, so any module that imports this one can issue requests for the behavior I describe below. Other modules are configured to convert external requests such as http://someserver/customer into an internal request of: ffcpl:/customer. This is just an internal request that gets remapped to the active:fetch-customers scheme like this:

<rewrite>
    <rule>
      <match>ffcpl:/customer</match>
      <to>active:fetch-customers</to>
    </rule>
  </rewrite>

The question remains, What actually executes the request? What handler do you rewrite the active URI to? Eventually, you imagine you will use a database, Web service, or other RESTful system to pull the information in but, when you get started, you may not know this. For the time being, you will simply dump some XML representations of customers into a directory and serve them up by munging them together. The initial rewrite rule will point to a BeanShell script to take care of this:

<rewrite>
    <match>active:fetch-customers</match>
    <to>active:beanshell+operator@ffcpl:/scripts/defaultcustomers.bsh</to>
  </rewrite>

This tells NetKernel to convert any request for active:fetch-customers into a request to execute the specified BeanShell. Notice how the pattern continues. You are identifying the functionality (active:beanshell, the logical name for interpreting BeanShell scripts) and the state needed to satisfy the request (a BeanShell script found at ffcpl:/scripts/defaultcustomers.bsh). Another module is responsible for handling this request. You can use it from your customer module because the NetKernel script module is included in your module definition:

<import>
    <uri>urn:org:ten60:netkernel:ext:script</uri>
  </import>

You could go look at that module's definition to find out how the script is actually executed, but for now, you do not care. You simply want it to happen. The default customer-handling BeanShell looks like Listing 1:

Listing 1. Default customer-handling script

import org.ten60.netkernel.layer1.nkf.*;
import org.ten60.netkernel.layer1.nkf.impl.*;
import org.ten60.netkernel.xml.representation.*;
import org.ten60.netkernel.xml.xda.*;

import com.ten60.netkernel.urii.aspect.*;

main()
{
  // Fetch the list of XML files in the data directory
  req = context.createSubRequest("active:mls");
  req.addArgument("operator", "ffcpl:/etc/MLS-Config.xml");
  req.setAspectClass(IAspectXDA.class);

  mls = context.issueSubRequestForAspect(req);

  // Iterate over the results
  xdaROItor = mls.getXDA().readOnlyIterator("/mls/dir/res");

  sb = new StringBuffer("<customers>");

  while(xdaROItor.hasNext()) {
    xdaROItor.next();
    current = xdaROItor.getText(".", true);

    // Fetch the content of the appropriate file
    cust = context.sourceAspect("ffcpl:/data/" + current, IAspectString.class);

    // Append to the results
    sb.append(cust.getString());
  }

  sb.append("</customers>");

  response = context.createResponseFrom(new StringAspect(sb.toString()));
  response.setExpired();
  response.setMimeType("text/xml");

  context.setResponse(response);
}

Listing 1 is busy, but reasonably straightforward to follow. First, a call is issued to the active:mls handler, which will fetch all of the files in a module that follow a certain pattern. The etc/MLS-Config.xml file from the module you installed shows that any XML files in the data directory should be returned:

<mls>
    <module-uri>urn:net:bosatsu:jw-rest</module-uri>
    <filter>ffcpl:/data/.*xml</filter>
  </mls>

Note: This capability of fetching module-specific files can be reused and cached, just like anything else in NetKernel!

A listing of the sample XML files are returned and iterated over. Each document is fetched as a String:

cust = context.sourceAspect("ffcpl:/data/" + current, IAspectString.class);
  sb.append(cust.getString());

Then each document is appended to a StringBuffer to return the results. This one call to sourceAspect underscores NetKernel 's RESTful nature quite nicely. You have a logical request (ffcpl:/data/somefile.xml), a verb (SOURCE, equivalent to REST's GET) and content negotiation. Here, NetKernel is asked to convert the file on disk into a String representation known as a StringAspect. It would be trivial to convert the request to return a byte array, a JDOM Document, a DOM instance, or any other form you might want in a different context. An Aspect in NetKernel is an immutable view of a particular representation of a resource. Because it is immutable, the whole request can be cached until one of the input resources changes. If you do not care what form comes back, you can call the source method instead, which takes only the URI for the information resource you want.

Once the StringBuffer has all of the documents, the customer element is terminated, converted to a String and wrapped in a StringAspect. Based on existing mappings, if NetKernel is running and you installed the example module, you should get a list of customers back in plain XML when you hit http://localhost:8080/customer. Remember the HTTP request is rewritten to an active:fetch-customers request, which gets rewritten to an execution of ffcpl:/scripts/defaultcustomers.bsh.

Layering applications

You usually want a nicely formatted list of data, so now you will convert the XML that comes back from active:fetch-customers into some nice HTML. NetKernel has plenty of tools to help you do this. XRL is a recursive template mechanism not unlike Cocoon and similar tools. To keep you from getting confused, the module is configured to map external requests for http://localhost:8080/xrlcustomers to a BeanShell that will do the XSLT conversion of the data that is returned from the logically named customer persistence layer. Normally, you would use the same URL and content negotiation to decide whether to return XML or HTML. The module exports ffcpl:/xrlcustomers.* and then configures this behavior with a rewrite rule:

<rewrite>
    <rule>
      <match>(ffcpl:/xrlcustomers.*)</match>
      <to>active:mapper+operator@ffcpl:/links.xml+operand@$1</to>
    </rule>
  </rewrite>

This works because the sample module imports the XRL handling behavior:

<import>
    <uri>urn:org:ten60:netkernel:ext:xrl</uri>
  </import>

The XRL handler expects a file of links that customizes one or more rewrite rules. (It would get really old having to specify them all by hand!)

<links basepath="ffcpl:/xrlcustomers/">
    <link>
      <name>index</name>
      <ext>/index.html</ext>
      <int>active:xrl-html+template@ffcpl:/content/customers.html+content@xrl:customers</int>
      <args>links</args>
    </link>
    <link>
      <name>customers</name>
      <int>active:beanshell+operator@ffcpl:/scripts/customers.bsh</int>
    </link>
  </links>

A complete XRL tutorial is beyond this article's scope, but what is happening is that a request for ffcpl:/xrlcustomers/index.html is going to be rewritten to invoke the templating of the ffcpl:/content/customers.html file with whatever comes back from issuing the xrl:customers link. This currently points to the execution of ffcpl:/scripts/customers.bsh. The HTML template looks like Listing 2:

Listing 2. HTML template

<html xmlns:xrl="http://1060.org/xrl">
  <!-- Style cribbed from http://www.somacon.com/p338.php -->
  <head>
    <style type="text/css">
    table.customer {
      border: 6px inset #8B8378;
      -moz-border-radius: 6px;
    }
    table.customer td {
    border: 1px solid black;
      padding: 0.2em 2ex 0.2em 2ex;
      color: black;
    }
    table.customer tr.d0 td {
      background-color: #FCF6CF;
    }
    table.customer tr.d1 td {
      background-color: #FEFEF2;
    }
    </style>
  </head>

  <body>
    <h1>Customers</h1>
    <xrl:include href="xrl:content"/>
  </body>
</html>

The <xrl:include> tag will be replaced by the body of whatever is passed in through the content parameter. In this way, the HTML does not have to change if the implementation to generate what goes in it does.

Java developers who look at all these rewrites are surely thinking this looks obnoxious, inefficient, or insane. The hardest part of transitioning from an object-oriented to a resource-oriented perspective is to let go of your preconceived notions about how things should work. The steps I've shown you so far link a few things together logically. It is easy to change where the content comes from for the templating by changing a rewrite rule. Very large refactorings can be integrated into existing systems by making a change this small. To understand the power and beauty of REST on the inside, you must try to go with the flow!

The ffcpl:/scripts/customers.bsh script is quite straightforward: Go get the customer information and then style it:

main()
{
  req = context.createSubRequest("active:fetch-customers");
  customers = context.issueSubRequest(req);

  req = context.createSubRequest("active:xslt");
  req.addArgument("operator", "ffcpl:/scripts/style.xsl");
  req.addArgument("operand", customers);
  output = context.issueSubRequest(req);

  response = context.createResponseFrom(output);
  context.setResponse(response);
}


By now, you will not be surprised to see that applying XSLT to XML content looks like anything else in NetKernel. Under the hood, the module that handles this stuff uses Saxon, but again you do not really care. In Listing 3, we convert any <customers> elements into a table and populate it with rows filled from every matching <customer> element.

Listing 3. Transforming XML with XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="customers">
    <table class="customer">
      <tr class="header">
        <th>ID</th>
        <th>Name</th>
        <th>Street</th>
        <th>City</th>
        <th>State</th>
        <th>Zip</th>
      </tr>
      <xsl:apply-templates/>
    </table>
  </xsl:template>

  <xsl:template match="customer">
    <tr>
      <xsl:if test="position() mod 2 != 0">
        <xsl:attribute  name="class">d0</xsl:attribute>
      </xsl:if>
      <xsl:if test="position() mod 2 != 1">
        <xsl:attribute  name="class">d1</xsl:attribute>
      </xsl:if>
      <td><xsl:value-of select="@id"/></td>
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="address/street"/></td>
      <td><xsl:value-of select="address/city"/></td>
      <td><xsl:value-of select="address/state"/></td>
      <td><xsl:value-of select="address/zipcode"/></td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

Pointing your browser at http://localhost:8080/xrlcustomer/index.html should yield the content shown in Figure 1.

Styled customer data
Figure 1. Styled customer data (Click to enlarge.)

Ruby on Rails and Grails scaffolding can produce this much content by running a few scripts. Once you get some more NetKernel under your belt, however, you will be able write efficient, flexible, cached scaffolding too that should perform extremely well. NetKernel is not necessarily a replacement for these MVC frameworks (although it could be). Instead, you can easily imagine using a NetKernel backend with Rails via Active Resource. If you like Ruby, NetKernel supports it as one of its implementation languages. Groovy is supported nicely too.

Migration

The power of what you have seen so far is efficiency, productivity, maintainability, and the ability to use the right tool for the right job. The XSLT style sheet concisely expresses an efficient conversion of XML to XSLT in a language designed for that purpose. Trying to handle the conversion in Java would be ugly, error-prone, inefficient, and hard to maintain. NetKernel makes it extremely easy to move between the right tools. It also makes it possible to change technology choices without having a huge impact on dependent code. Again, interfaces kind of allow you to do this in languages like Java, but logical connections are much more flexible.

With working code to display customer information in place, it is now trivial to migrate the active:fetch-customers handler from the file-based prototype to an actual database-backed engine. This example uses HSQLDB, but it is easy to support other RDBMSs by modifying the driver configuration in etc/ConfigRDBMS.xml. First, seed the database by pointing your browser to: http://localhost:8080/jw-rest/db-init. This gets rewritten to a DPML script (another NetKernel language) that will populate the relational database. If you modify the example to use something other than HSQLDB, you will likely have to modify the SQL syntax as well. With the database initialized, it is now easy to come up with a new BeanShell that issues a SQL database query and then styles the results as XML:

1 2 3 Page 2
Page 2 of 3