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 URIsThe 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 objectsThe
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 handlersHTTP 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
InputStream
s or
RenderedImage
objects.
SOAP handlersSOAP 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 handlersWhenever 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 handlersStylesheet 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 handlersPeriodic 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 handlersWhen 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.