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.
Handle callback function is called in this context.
The context is deleted after message is handled.
Message singleton related functions (e.g. Message.installsingleton()) are not available in:
- When running in a singleton context.
- When running a script configured with multiple instances
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"); var 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"); var 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 script is configured to use multiple instances it can only install singleton for the first one // It makes no sense to configure multiple instances for it! 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