Etiquetas

A preview of Node.js adapters in Worklight 6.0.0

Edit: In versions 6.1.0 and 6.2.0 this functionality no longer works. This blog page will be updated when it is available again.

The original post is below:

IBM recently released the latest version of the Worklight mobile application development platform. Among other things, Worklight 6.0.0 contains a preview of an experimental feature - adapters based on Node.js.

Background

Classic Worklight adapters are executed with Mozzila's Rhino engine. This engine compiles JavaScript code to JVM bytecode, which is run in the same process as the Worklight server. This approach

allows the use of Java libraries in adapter functions and

enables easy communication with resources of the Worklight server.

The adapter code, as it usually is in Java, is fully synchronous.

However, this method

provides no real sandboxing

or debugging support.

Communication to backend systems is done either through one of the predefined connectivity interfaces, or with custom Java libraries.

Node.js adapters, by contrast, are executed directly by Node.js and its underlying JavaScript engine, V8. The execution takes place in a separate process, currently one process per adapter.

When using Node.js adapters

function invocation and communication with the Worklight server is done by exchanging HTTP requests.

No Java libraries can be used and the code is completely asynchronous.

However, Node.js adapters provide the following significant advantages:

Much stronger sandboxing is achieved

The process is potentially highly scalable

A huge selection of third-party modules extending connectivity and providing implementations of various common backend constructs

Debugging is supported!

Edit: Java libraries can not be used out of the box, but there are third-party tools like node-java that can serve as bridge to Java.

How it works

The Worklight server must be configured (in the app's web.xml) with the location of the Node.js executable, the NodeCallbackServlet URL, the path to a work area, and the port range. The work area is a folder that must be manually prepared in advance (see the “Configuring it” section).

Every Node.js adapter must use and initialize a special module, responsible for communication with the Worklight server. This module needs to be installed in the configured work area.

When needed, the Worklight server deploys an adapter script into the work area and starts an instance of Node.js running this adapter. When the server is stopped, or the adapter is undeployed, this running instance is killed by the Worklight server.

Each adapter listens for invocation instructions issued by the Worklight server on a port taken from the configured port range.

The Worklight module supplies an implementation of the Worklight server API (WL.Server.*, WL.Logger.*). Unlike classic adapters' API, however, the invocation of these API calls is asynchronous. Such invocations issue HTTP requests to the Worklight server. This asynchrony influences the programming style of adapters. See the “What's different” section for more details.

Configuring it

Create a work area folder (in the sample below: “C:\TestNode\workspace”). Open a shell in that folder and run:Edit: Added "install" directive in the two commands below.

npm install wl-<version>.tgz

For example:

npm install wl-0.0.1a.tgz

This installs the module itself and an MIT-licensed “q” module from https://npmjs.org/package/q, one of the implementations of the CommonJS promises spec for Node.js and a dependency of the Worklight module.

Add the following lines to the web.xml of your project's WAR file. (Adjust for your environment.)

Creating an adapter

To produce a valid and correct Node.js adapter, several rules must be followed:

An adapter must require the wl module and initialize it.

The initialization is done by invoking the init function and passing it an object containing handler functions. The name of the functions in the object must match those defined in the adapter's XML file.

If event sources are defined by the adapter, they must be created before the initialization call. The function createEventSource returns the module for easier chaining of multiple createEventSource and init calls (as mentioned before, init must be the last call).

Unlike with Rhino adapters, there is no global WL object. Instead, each adapter function must be ready to receive a WL object as its first argument. The function may choose to receive additional parameters as well. These will be the parameters supplied by clients.

Worklight's adapter API is invoked through this supplied WL object.

Unless a function is fully synchronous, the return value/error is returned by calling functions WL.success or WL.err.

Every asynchronous function must eventually call to WL.success or WL.err. Only one such call should be made.

Mind the timeouts!

Calls to the WL.Server.* and WL.Logger.* functions don't accept callbacks, they produce promises instead. The promises are implemented by the q library. See https://github.com/kriskowal/q and samples for additional information.

Unlike Rhino, global variables are no longer kept in a user's session state. Therefore, to maintain a separate and persistent (between function invocations) state, you should use the new API functions WL.Server.writeSessionState and WL.Server.readSessionState.

Samples

Download the sample project TestNode from here. It contains several simple applications that use adapters and can demonstrate various aspects of the Node.js adapter development.

API description

What's different

The majority of functions in the Node.js adapter API have the same signature and semantics as their counterparts in the Rhino API. One major difference is that Node.js API calls are asynchronous, and as such don't return values immediately. Instead, they produce promises, which, in turn, can be chained with handlers to run once a value is available and with error handlers.
For example, rather than writing

The “.then” part of the expression above defines the handler for the promise returned by the WL.Server.getActiveUser invocation. The first function receives the value in the event of a success, and the second function (WL.err) handles error. In this case, we chose to terminate our function and return the error should one occur during API invocation. If there are several API calls one after the other, the “.then” clauses can be chained, and the error handler can be omitted until the last clause, when it would catch errors thrown by all the calls. Here’s an example:

You don't have to use the WL.err as an error handler (you can implement one of your own instead), but do make sure that either WL.err or WL.success are called in all possible flows, otherwise an adapter invocation won't terminate and you’ll have to rely on timeouts to release resources.

What's missing

Node.js adapters don't have the same notion of connectivity that Rhino adapters do. Instead, you can use third-party modules to connect with any of the supported backends. As a result, functions responsible for the invocation of a request to a predefined connection point, such as “invokeHttp” and “invokeCastIron”, are not available.

Some functions, at least currently, have been omitted because of the additional complexity of transferring pure Java objects from a Java environment to a pure JavaScript environment. Omitted functions include those related to the original HTTP request.

Functions related to the direct invocation of Java methods are, of course, not available either.

Some of the notification functions and GEO API are also not available.

What's there

Function

Description

WL.Server.configuration

Not a function call, but an object – thus, access is synchronous.

WL.Server.getActiveUser

WL.Server.setActiveUser

WL.Server.invokeProcedure

WL.Server.writeSessionState

Receives an object or a string, and stores it on the user’s session in the Worklight server.

WL.Server.readSessionState

Returns the previously written state or an empty object if no state was yet written.