Latest ESXX Release


Sunday, February 8, 2009

JavaScript web services, the ESXX way

By "web service", we mean a service that is intended to be used by a program, not directly by a human surfing the web with a web browser.

To use ESXX as a web service engine, you first need to define what request handlers should be available. ESXX currently provides six kinds of handlers: HTTP, SOAP, stylesheet, timer, exit and error handlers. Handlers are defined using a section of the main document within the http://esxx.org/1.0/ namespace:
<esxx xmlns="http://esxx.org/1.0/">
<handlers>
<http method={http-method} uri={path-info} handler={object-and-method} />
<soap action={soap-action} uri={path-info} object={object} />

<error handler={object-and-method} />
<stylesheet media-type={content-type} uri={path-info} href={xslt-file} type="text/xsl" />
<timer delay={delay-in-seconds} period={period-in-seconds} handler={object-and-method} />
<exit handler={object-and-method} />
</handlers>

</esxx>
HTTP handlers
HTTP handlers are JavaScript functions that are triggered by a normal HTTP request. What handler to call is defined by the request method and the part of the URI that follows the .esxx file, which defines the web application.
SOAP handlers
SOAP handlers are JavaScript objects that are triggered by SOAP request, which means a POST request with the SOAPAction HTTP header set (SOAP 1.2 is not yet supported but will be in the future). The object to act upon is defined by the SOAP action and the part of the URI that follows the .esxx file, which defines the web application. What method to call on the object is determined from the SOAP request's Body element.
Error handlers
Error handlers are JavaScript functions that will be called whenever a HTTP or SOAP handler throws an exception and gives the application a chance to recover or to produce a more good-looking error message than ESXX's default error message.
Stylesheet handlers
Stylesheet handlers are XSLT stylesheets that are triggered by XML response from the HTTP, SOAP and error handlers. What stylesheet to execute is defined by the content type and the part of the URI that follows the .esxx file, which defines the web application.
Timer handlers
Timer handlers are JavaScript functions that will be triggered once the application has been loaded and the periodically for the lifetime of the app.
Exit handlers
Exit handlers are JavaScript functions will be executed by ESXX when an application is unloaded. Applications are unloaded if they have been idle for a while, if any of the source code files they were compiled from change or as a direct command via the JMX interface.
Methods, SOAP actions, Media types and URIs
The attributes method, action, media-type and uri are all interpreted as Java (not JavaScript!) regular expressions, with one important addition: they allow named groups using the (?{name}...) syntax. More on that in the next section.

Multiple handlers of a given type are matched in the order they appear in the document. When an application is first loaded, all handler rules of a given type are merged and compiled into a single NFA, which is then used for each request to determine what handler to call.

(Normal Java regex recommendations apply: try to use non-greedy multipliers and use expression like [^a]*a instead of .*a wherever possible, in order to limit the amount of backtracking required.)

Here are a few examples:
...
<http method="GET|HEAD" handler="global.getHanlder" />

<http method="POST" uri="/upload" handler="myUploadHandler" />
<soap uri="/soap" object="soapObject" />

<soap action="urn:xmethods-delayed-quotes" uri="/" object="stocks" />
<stylesheet media-type="application/xml" uri="(?!soap$).*" href="anything-but-soap.xslt" />

...
Handlers, objects, hrefs and Request objects
The handler, object and href attribute specify JavaScript functions, JavaScript object and XSLT stylesheets, respectively. Named parameters from the attributes method, action, media-type and uri attributes may be inserted by using the {name} syntax. For instance, you the following HTTP handler invokes one of the following methods on object: handleDELETE() handleGET(), handleHEAD(), handlePUT():
<http method="(?{verb}GET|HEAD|PUT|DELETE)" handler="object.handle{verb}" />
Additionally, all parameters are available in the args property of the Request object that is passed to the matched JavaScript handler. Not only does this allow full JavaScript control of how to handle a request, it also means that it's easy to pass parameters in the URI:
<http method="GET" uri="/products/(?{category}\w+/(?{id}\d+)" handler="shop.getProductInfo" />
When the method shop.getProductInfo(req) is triggered by an HTTP GET request to .../app.esxx/products/games/10, req.args.category will refer to a word indicating the category ("games") and req.args.id will be a sequence of decimal digits indicating the item ID (10).

Regular expressions and named groups are very powerful, but can be error prone and hard to read. For large web applications with lots of defined handlers, XML entities can be used to create aliases for common URI parameters that are used in many handlers:
<?xml version="1.0" ?>

<!DOCTYPE esxx [
<!ENTITY user_id "(?{user_id}\d+)">
<!ENTITY email "(?{email}[a-zA-Z][\p{Alnum}_\-.+]*@([\p{Alnum}_\-]+\.)+[a-zA-Z]{2,6})">
]>

...

<esxx>
<handlers>
<http method="POST" uri="/users/&user_id;/sendmail/&email;" handler="sendMail" />

...
</handlers>
</esxx>
HTTP handlers
HTTP handlers handle plain HTTP requests based on the request method and the URI. The handler specifies a JavaScript function (like myHandler) or object and method (like object.myMethod), which will be called with an initialized object as single parameter.

As noted above, any groups in the regular expressions that caused the handler to be called are available in the args property of the ESXX.Request object. The contentType property will contain the basic content type (without parameters) and message property will contain the parsed request body.

The handler should either return an ESXX.Response object, an Array (whose elements will be used as constructor parameters to initialize a new ESXX.Response object), a number (which indicates the HTTP status code and an empty body) or any other supported object, which will be automatically wrapped in an ESXX.Response object with HTTP status 200 and no extra headers (the content-type will be guessed based on the object's type).
  • If the payload of the ESXX.Response object is an E4X or DOM node, a stylesheet handler will be looked up and if found, the XSLT stylesheet will be used to transform the node.
  • Otherwise, the ESXX.Response payload will simply be serialized and sent to the client.
  • If the handler throws an exception, the error handler, if defined, will be invoked.
The most common form of return values are XML nodes ("application/xml") or plain JavaScript objects ("application/json"), but it's also possible to return Java InputStreams or RenderedImage objects.

SOAP handlers
SOAP handlers handle SOAP 1.1 requests based on the SOAPAction HTTP header and the URI. The handler specifies a JavaScript object and the local-name of the first child of the Body element is used to determine what method to call on the object. The method will be called with an initialized ESXX.Request object as first parameter and the first child of the Body element (as an E4X node) as the second parameter.

As noted above, any groups in the regular expressions that caused the handler to be called are available in the args property of the ESXX.Request object.

The soapAction property will contain the value of the SOAPAction HTTP header and the message propery will contain a Java javax.xml.soap.SOAPMessage object.
  • The return value is handled just like for HTTP handlers and the payload should be an XML node. Unlike HTTP handler responses, however, if the XML node's local-name is not "Envelope", a SOAP envelope will automatically be added.
  • If the handler throws an exception, the error handler, if defined, will be invoked.
The implementation is very simple, but should be quite usable for basic SOAP requests. If a real SOAP service is required, you should use normal HTTP handlers and call the appropriate Java SOAP framework from there. (Or, you could just reconsider and use HTTP the way it was intended to be used.)

Error handlers
Whenever an HTTP or SOAP handler throws an exception, the error handler, if defined is invoked. The handler specifies a JavaScript function (like myHandler) or object and method (like object.myMethod), which will be called with the original ESXX.Request object as first parameter and the exception object as the second parameter.

The handler may return null or undefined to act as if there were no handler installed in the first place, or return a new response in the same way HTTP handlers return responses.

This handler should not throw exceptions.

Stylesheet handlers
Stylesheet handlers handle responses from HTTP or SOAP handlers based on the content type and the URI. The handler specifies an XSLT stylesheet that will be executed if the reponse was an XML node.

Please stay tuned for more information about the stylesheet handlers. I'll post more information about them later.

Timer handlers
Periodic or one-shot timers may be installed by specifying timer handlers. The delay and period is specified in seconds (decimal numbers are accepted). The handler specifies a JavaScript function (like myHandler) or object and method (like object.myMethod), which will be called with a single Date parameter, which indicates the time when the handler was scheduled to be executed.

A timer handler is never executed more than once at a given time. If a timer handler takes too long to complete, subsequent events will be discarded until the handler returns.

No return value is expected. This handler should not throw exceptions.

Exit handlers
When an ESXX application is about to be unloaded from memory, an exit handler may be called to perfrom final cleanup. The handler specifies a JavaScript function (like myHandler) or object and method (like object.myMethod), which will be called with no arguments.

No return value is expected. This handler should not throw exceptions.

No comments: