Singleton (separate) message handler context in Javascript
From Yate Documentation
A regular message handler callback is called by javascript when script context is unlocked.
Script context is locked during message processing.
Script global data is available in message handler callback function.
A singleton message handler may be used when application is expected to process a high number of messages and want to avoid contention due to javascript serialization.
When a singleton message handler is installed the javascript module will build and initialize a separate context each time a message is handled.
Handler callback function is called in this context.
The context is deleted after message is handled.
See Message() for singleton functions availability.
Sample
Engine.debugName("handler_singleton_sample");
Message.trackName("handler_singleton_sample");
// Callback for call.route. 'context' value is the second parameter passed to Message.installSingleton()
function onCallRoute(msg,context)
{
if (debug)
Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context);
// Increment some shared variable to be reported in status
var perf = new Engine.SharedVars("handler_singleton_sample_perf");
perf.inc("handled_" + context);
if (!msg.called)
return false;
var shared_objects = new Engine.SharedObjects;
var data = shared_objects.get("handler_singleton_sample_route_data");
if (!data)
return false;
if (data.local_route_called) {
// Sample: route local using called party match
var match = msg.called.match(data.local_route_called);
if (match[1]) {
perf.inc("routed_local_route_called");
msg.retValue("sip/sip:" + match[1] + "@127.0.0.1");
return true;
}
}
if ("object" == typeof data.database) {
// Sample: route local using database query
var m = new Message("database",false,data.database);
m.module = Engine.debugName();
if (m.dispatch(true) && !m.error) {
var row = m.getRow(0);
if (row.target) {
perf.inc("routed_database");
msg.retValue(row.target);
return true;
}
}
else
perf.inc("database_failure");
}
return false;
}
function onTest(msg,context)
{
if (debug) {
var shared_objects = new Engine.SharedObjects;
var data = shared_objects.get("handler_singleton_sample_route_data");
var s = "\r\n-----\r\n" + Engine.dump_var_r(data) + "\r\n-----";
Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context,"route_data:" + s);
}
// Increment some shared variable to be reported in status
perf = new Engine.SharedVars("handler_singleton_sample_perf");
perf.inc("handled_" + context);
return true;
}
if (!Engine.scriptType)
initialize(true);
else if (Engine.ScriptTypeMsgHandler !== Engine.scriptType())
initialize(true);
else {
// We are in singleton message handler context
// NOTE: debug (including level) is propagated from main script to singleton
// context by javascript module
debug = Engine.debugEnabled();
}
//
// From this point the code is used only if script is loaded as static script
//
function onStatus(msg)
{
if (msg.module && ("handler_singleton_sample" != msg.module))
return false;
var str = "";
var perf = new Engine.SharedVars("handler_singleton_sample_perf");
perf = perf.getVars();
for (var name in perf)
str += "," + name + "=" + perf[name];
msg.retValue(msg.retValue() + "name=handler_singleton_sample,type=misc;" + str.substr(1) + "\r\n");
return !!msg.module;
}
function onDebug(msg)
{
if (var dbg = msg.line) {
Engine.setDebug(dbg);
debug = Engine.debugEnabled();
}
msg.retValue("Sample Singleton Message Handler debug " + Engine.debugEnabled() + " level " +
Engine.debugLevel() + "\r\n");
return true;
}
function onReload(msg)
{
if (msg.plugin && ("handler_singleton_sample" != msg.plugin))
return false;
initialize();
return !!msg.plugin;
}
function initialize(first)
{
Engine.output("Initializing module Sample Singleton Message Handler");
if (first) {
if (!Message.installSingleton) {
Engine.debug(Engine.DebugConf,"Refusing to start Sample Singleton Message Handler",
"singleton handler install not available");
return;
}
// Initial debug
Engine.setDebug("level 10");
debug = Engine.debugEnabled();
// Message.installSingleton(func,context,name[,priority[,filterName[,filterValue[,params]]])
// Do not load extensions (default: javascript.conf global setup)
// Extensions are loaded using a script.init message. C++ modules may push javascript objects in script context
var params = {load_extensions:false};
// Sample call.route filter. Handle if not explicitly disabled. Use Engine.MatchingItem
var mi = {name:"handler_singleton_sample",value:/^$(true|yes|)$/};
var filter = new Engine.MatchingItem(mi);
Message.installSingleton(onCallRoute,"call_route","call.route",30,filter,undefined,params);
// Sample test filter. Handle if not explicitly disabled. Use basic
Message.installSingleton(onTest,"test","test",70,"handler_singleton_sample",/^$(true|yes|)$/,params);
// Module messages
Message.install(onStatus,"engine.status");
Message.install(onDebug,"engine.debug",150,"module","handler_singleton_sample");
Message.install(onReload,"engine.init");
}
// Init and set shared object holding route data
var routeData = {
local_route_called: /^(test|other)$/,
database: {account:"some_db_account",query:"select target from route"},
};
var shared_objects = new Engine.SharedObjects;
shared_objects.set("handler_singleton_sample_route_data",routeData);
}
See also