Messages
What's a message in Yate
Messages are the main piece of Yate. We use them between modules insted of using functions. We do this mainly because if one module is changing we don't have to change all the other modules that are dependent, and also because we can debug what's happening in the engine much easier since we know which module passes which data to another module.
Components of a message
A message is a holder for several components:
- name - identifies the message type and allows matching it against the possible handlers
- return value - a string that is used as returned value by the emitter of the message
- time - the time the message was created. This is important if the messages are queued for an indefinite amount of time
- parameters - an arbitrary number (including zero) of string values, each having a name. Each handler can take action based on specific parameters and/or modify them. Unknown parameters must be ignored.
All the messages are binary inside of Yate. However you can use Rmanager to get them out in a human readable form.
The messages are passed using memory sharing. In this way we have a pretty optimized system. Other options like pipes or sockets have been considered, but failed to give us the flexibility and speed needed. When passed to external modules the messages are converted to an escaped string encoding that allows handling any special characters that may be present in the message's components.
Messages are processed by message handlers. These receive messages whose names match the handler's name. They can freely modify the message's components (parameters, returned value, even the name) and finally they can stop processing the message or let it slip to the next handler.
The order in which handlers are called to attempt to process a message is established when the handlers are inserted in the dispatcher. The ordering of handlers is by their priority, a handler with a lower numerical priority is inserted earlier in list and receives the message before a handler with a higher numerical priority.
There is NO GUARANTEE about the order between handlers of equal priority although currently the following rules apply:
- The order this does not change between messages.
- To avoid uncertainity and preserve order if a handler is removed and reinserted handlers of equal priority are sorted by their address.
Example of call.route message
A very good example about how the message system works is the "call.route" message:
When a call comes in at some point, a messages is generated like this:
Message *m = new Message("call.route"); m->addParam("driver","iax"); if (e->ies.calling_name) m->addParam("callername",e->ies.calling_name); else m->addParam("callername",e->session->callerid); if (e->ies.called_number) m->addParam("called",e->ies.called_number); else m->addParam("called",e->session->dnid);
Then we send the message to the Engine and we find out if some module did actually accept to handle it. We must also destruct the message.
if (Engine::dispatch(m)) Output("Routing returned: %s",m->retValue().c_str()); else Output("Nobody routed the call!"); m->destruct();
The Engine is getting the above message and is sending it to all the modules that have registered a "route" handler.
It is also possible to fire-and-forget messages. They are stored in a queue in the engine and dispatched later in threads that belong to the engine. Once such a message is dispatched it is destroyed by the engine.
Message *m = new Message("alert"); m->addParam("reason","Hardware malfunction"); Engine::enqueue(m);
To handle a route request (probably in another module) we first have to declare a class RouteHandler derived from MessageHandler.
class RouteHandler : public MessageHandler { public: RouteHandler(int prio) : MessageHandler("call.route",prio) { } virtual bool received(Message &msg); };
Then, because the received method is a pure virtual in the MessageHandler class, we must override it.
bool RouteHandler::received(Message &msg) { const char *driver = msg.getValue("driver") Debug(DebugInfo,"Route for driver %s",driver); // don't actually route anything, let message continue return false; }
And finally, in the plugin initialized method we can install the handler.
m_route = new RouteHandler(priority); Engine::install(m_route); =]
To get more references about messages read the Message class documentation at API::Message.