Introduction
NetKernel is agnostic with respect to web-service/XML-service technologies. Internally it uses REST-like
URI interfaces for services supplied from different modules. It is relatively simple to export
a module's public URI address space to an HTTP transport to create a classic REST service. This trailmap
shows how to build a module which exports an XML service interface over HTTP.
Service Specification
We will create a service which generates a PNG bitmap image of a supplied text string. We will also support an
optional fontsize argument. The service will be exported with a URL /trailmap/service/Text2PNG. So an example
request on the interface would look like...
http://localhost:8080/trailmap/service/Text2PNG?text=Hello%20World&fontsize=20
- text is the URL escaped text string to render
- fontsize is the optional font size to render the text with
We have assumed an HTTP GET request with parameters supplied as URL query items. In fact our service will support
either GET or POST encoded form data requests - the standard HTTP transport takes care of this for us.
If no query parameters are supplied we will return an HTML document giving the service interface description.
Example
There's an example service at /example/trailmap/service/Text2PNG. Here's an image generated by the service...
You can try it out for yourself here.
The service definition is available here.
At the bottom of this page is the DPML idoc that processes the service requests. It uses the
org.ten60.util.image.text2PNG accessor provided by the util_image module.
We will not cover the DPML in depth - the service to be exported as a REST service could do anything you like.
The example service shown above is tied into the documentation module so for the remainder of
this trailmap we'll discuss how to setup a standalone module configuration which exports this DPML service with the correct URL /trailmap/service/Text2PNG
as defined in the specification.
Developing the Service
-
To start, use the new module wizard to create a host module for the service. Call
the module Text2PNG. Set the external path to
/trailmap/service/ and keep the default setting for internal path. Import
the new module into the Frontend fulcrum - by default this will expose it on the HTTP transport on port 8080. Lastly include DPML support.
The module wizard will create your module in the <install>/modules/Text2PNG/ directory.
-
Copy the Text2PNG idoc listed below to a file Text2PNG.idoc in the resources/ directory of your module. You will now be able to
test your configuration by requesting the service definition at
http://localhost:8080/trailmap/service/Text2PNG.idoc.
-
The service will use the org.ten60.util.image.text2PNG accessor supplied from the util_image module. This module must be imported into your module by
adding the following fragment into the mapping section of your module.xml definition located in the root directory of your module.
<import> <uri>urn:org:ten60:util:image</uri>
</import>
-
Finally we want to hide the "idoc" file extension to conform with our specification - it is bad form and a long term
maintenance liability to expose the underlying processing technology on the REST-interface. We can do this by adding a rewrite
rule to the top of the list of rewrite rules in the module.xml definition. The rule creates an active URI to invoke the DPML runtime with the Text2PNG idoc and places any
addition parameters on the end of the new URI. By placing the rule at the top of the list of rules we make sure the default .*idoc matching
rule, created by the wizard, will not be triggered.
<rule>
<match>ffcpl:/trailmap/service/Text2PNG(.*)</match>
<to>active:dpml+operand@ffcpl:/resources/Text2PNG.idoc$1</to>
</rule>
You must cold restart NetKernel in order for the changes to the module.xml to be picked up.
The service is now configured and exported. You should now be able to call it with the parameters specified above. Any DPML process can be
exported as a REST service using this method of rewriting an external URI to an active URI DPML request.
Text2PNG DPML
<idoc> <seq>
<comment>
**************
Transform the parameter document into a Text2PNG parameter document
using an inline XSLT transform
**************
</comment>
<instr>
<type>xslt</type>
<operand>this:param</operand>
<operator>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:template match="/nvp">
<text2PNG>
<fontsize>
<!---->
<xsl:choose>
<xsl:when test="fontsize">
<xsl:value-of select="fontsize" />
</xsl:when>
<xsl:otherwise>12</xsl:otherwise>
</xsl:choose>
</fontsize>
</text2PNG>
</xsl:template>
</xsl:stylesheet>
</operator>
<target>var:Text2PNG</target>
</instr>
<comment>
**************
Invoke the Text2PNG accessor with the text referenced by xpointer
and the newly created Text2PNG specification doc.
**************
</comment>
<instr>
<type>org.ten60.util.image.text2PNG</type>
<operand>this:param#xpointer(/nvp/text)</operand>
<operator>var:Text2PNG</operator>
<target>this:response</target>
</instr>
<exception>
<comment>
**************
An exception will be thrown if we don't receive a parameter
so issue an HTML service description.
**************
</comment>
<instr>
<type>copy</type>
<operand>
<html>
<body>
<h2>Text2PNG Service Specification</h2>
<p>
This service generates a PNG bitmap image of a supplied text string. It supports an
optional fontsize argument. The service is exported with a URL /trailmap/service/Text2PNG. So an example
request on the interface would look like...
</p>
<p>
<code>http://localhost:8080/trailmap/service/Text2PNG?text=Hello%20World&fontsize=20</code>
<ul>
<li>
<b>text</b> is the URL escaped text string to render
</li>
<li>
<b>fontsize</b> is the optional font size to render the text with
</li>
</ul>
The service will support either GET or POST encoded form data requests.
</p>
<p>
If no query parameters are supplied this service interface description is served.
</p>
</body>
</html>
</operand>
<target>this:response</target>
</instr>
<instr>
<type>cast</type>
<operand>this:response</operand>
<operator>
<cast>
<mimetype>text/html</mimetype>
</cast>
</operator>
<target>this:response</target>
</instr>
</exception>
</seq>
</idoc>