Lua Engine

The Lua Engine

Introduction

The JSLEE scripting service provides full support for the Lua 5.2 language. This scripting engine is based on the luaj library.

To include the Lua engine in your JSLEE, configure the handler as follows:

{
    "selfcare": {
      "handler": "nz.co.nsquared.slee.scriptengine.LuaScriptEngineVerticle",
      "instance-count": 1,
      "configuration": {
        "scripts": [        
        ],
        "lib-path": [
                "lua/?.lua"
              , "lua/?/?.lua"
              , "lua/?/?/?.lua"
          ]
      }
    }
}

Library Path Configuration

Lua scripts can require() modules of code. The lua scripting engine supports module includes by searching the lib-path provided in the configuration. If a lib path is not provided, the system will search the path provided by the Java system property luaj.package.path.

lib-path may be either an array of paths, or a semi-colon separated string of paths. Note that to retrieve scripts in subdirectories these need to be explicitly listed using the form lua/?/?.lua.

There is no default for lib-path, however it is recommended that:

{
    "lib-path": [
            "lua/?.lua"
          , "lua/?/?.lua"
          , "lua/?/?/?.lua"
      ]
}

is used as the minimum, and any user-supplied library paths are included in this list. It is important that the JSLEE interface is included into your script.

Regardless of the path and what is require()d into scripts, the following standard libraries are included in every Lua script by default (no require() necessary):

Script Configuration

The scripts to run within the JSLEE are configured using the scripts array. An example configuration would be:

{
    "scripts": [
      {
        "class": "nz.co.nsquared.slee.smpp.event.SMPPMessageEvent",
        "script": "return JSLEE.handler ({ handle = function () print ('Hello, world!') end })"
      },
      {
        "name": "lua-from-file",
        "file": "/path/to/file.lua",
        "config": "return { a = 1 }"
      }    
    ]
}

To configure the JSLEE, either or both of a class or a name value must be provided, along with either an inline script or a file to compile.

If using class, this must be the fully qualified name of the event that will be listened for over the event bus, sent by another JSLEE service.

If using name, this must be a unique name that will be used as the endpoint address that other JSLEE services can send messages to directly.

Both name and class may be defined, however only one instance of each will be used after reading the configuration; warnings will be raised for duplicate values that are skipped. Should a message be received that qualifies for handling under both name and class matches, the script that is defined for the name will be chosen in preference.

Specifying a script allows for an inline Lua script to be provided. For very short scripts this might be reasonable, however in practice this is suggested only for debugging and testing (e.g. to dump the received message before passing on to the real endpoint).

For longer scripts, a file can be specified giving the path to a Lua file. The full content of the file referenced will be evaluated. In addition, specific information can be passed to the executed file in the optional config string, which is an arbitrary Lua string that is evaluated and has its return value passed to the script’s begin() function.

For example:

{
    "scripts": [
      {
        "class": "nz.co.nsquared.slee.smpp.event.SMPPMessageEvent",
        "file": "selfcare.lua",
        "config": "return { Timeout = 500 }"
      }    
    ]
}

In this example, the Lua table returned by this script will be passed in to the begin() method from selfcare.lua. This value can then be used as required at runtime, allowing for configuration outside the script.

Note that the config value is ignored if the script value is present.

Conforming to the API

The Lua provided must conform to the following API by providing a number of methods to perform actual tasks in a Lua table:

Method Required? Description
begin() no This method is called once after a successful compiliation of the Lua. This allows for initialisation of the script. This is optional
handle() yes This method is called for each incoming event the script engine receives of the JSLEE message type associated with the script.

A Lua table with these methods should be returned from any script compiled for the engine, however when creating this object, it is important to call:

return JSLEE.handler(yourhandler)

To ensure that the handler the Lua script engine actually gets conforms to our API requirements (in particular it wraps your handler in a coroutine).

The begin() Method

The begin() method receives the following arguments:

Argument Type Description
logger LoggerDelegate A delegate object that wraps a org.slf4j.Logger interface object.
config any The result of the execution of the script embedded in the JSLEE script engine service configuration.

The begin() method must complete successfully otherwise it will cause the shutdown of the JSLEE. Further, this method cannot interact with other service - the JSLEE doesn’t provide the appropriate interfaces or structure. All actions of the begin() method must be pure Lua code.

The handle() Method

The handle method receives the following arguments:

Argument Type Description
uuid String The unique ID of the event that the handler is handling.
event any A reference to the object the handler is for (e.g. SMPPMessage) that the handler should handle. If the Lua scripting engine is aware of the message type, this will be wrapped in a Lua based delegate object with a more useful Lua interface, and be unwrapped from the message event used to actually send the message over the VertX event bus.
message Event The actual event sent over the VertX event bus.
fromAddress Address The Vert.X event bus address of the sending service/endpoint.

The handle() method is responsible for handling the event to the point where it can respond back to the source of the event with a success or failure.

The handle method may do this by calling JSLEE.succeeded(result) or JSLEE.failed(errorCode, errorMessage). In either case this will result in the provided result information being sent back to the source as either success or failure.

Only the first call to either of these methods is used. Subsequent calls to these methods are ignored.

Be sure to provide a result that the source of the handled message will understand - e.g. it is not useful to send an EDR back to the SMPP service.

Note that a Lua handler may reply to the source of the event before completing its processing. E.g. a SMPP handler may immediately reply to the source, then perform the business logic to actually complete processing of the SMS.

Calling the Lua error() method will, if it is the first result provided, cause a failure to be sent back to the calling method. If a result has already been provided, then the error is not sent back. In any case however, error() calls result in an error message being printed to the system log.

The handle() method may send messages to other JSLEE service (and receive replies) using the JSLEE.send method.

Logging from Lua Scripts

Logging from Lua scripts can be done using the print() method, which prints to stdout. However this is usually not the best approach. Instead, use the logger provided as the begin() method’s first argument, or call JSLEE.logger() to get a logger in the handle() method, and use this.

The Logger returned in each case supports the following methods, which are delegated to the org.slf4j.Logger object. The following methods are available:

EDRs from Lua Scripts

Lua scripts can create EDRs using the JSLEE.createSuccessEdr or JSLEE.createFailureEdr functions. Refer to the JSLEE module documentation for usage details for these functions.

Note that EDR configuration must be enabled on the Lua handler service for any EDR output of any type to occur.

Lua Script Example

The following Lua script declares the framework for a Lua handler:

local JSLEE = require("n2.jslee.JSLEE")

-- Our table which holds our begin and handle methods
local h = {}

-- Begin method. JSLEE methods can't be used here unless
-- explicitly mentioned.
function h.begin (logger, userConfig)
end

function h.handle (uuid, event)
    logger = JSLEE.logger()
    log:debug("[myhandler] Received request to handle event " .. uuid)
    -- actual handling
    -- return an appropriate value to the source of the event.
    JSLEE.succeeded (0) 
end

return JSLEE.handler(h)

The JSLEE Lua API

Each JSLEE Lua script must include and use the JSLEE API to perform the appropriate actions within the script. The available methods are documented in the JSLEE Lua library.

Lua API Wrappers

To provide more effective APIs to use from within Lua, some objects have wrappers which are used by the Lua script engine.

For example, if handling a SMPPMessageEvent, the JSLEE Lua API will automatically extract out the SMPPMessage object and wrap it in the SMPPMessage Lua API available in lib/n2/jslee/SMPPMessage.lua.

Wrappers:

Wrapper Description
SMPPMessage The SMPPMessage wrapper wraps the Java SMPPMessage object. If your handler is for a SMPPMessageEvent, this API is what you receive as the event object automatically.

See the associated Lua API object for documentation of methods.