Singleton (separate) message handler context in Javascript

From Yate Documentation
Jump to: navigation, search

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

Personal tools
Namespaces

Variants
Actions
Preface
Configuration
Administrators
Developers