AppWebsocket connections use JSON messages for communication. Those messages are normaly received and processed by the application. But sometimes it is wanted to build modules for re-occurring communication types that can be reused. This can be done using JSON APIs. The idea is that many JSON APIs can be attached to a single AppWebsocket connection and each of them processes the JSON messages with a specific "api" attribute.
This framework provides base classes and interfaces that are needed to implement JSON APIs and attach them to a connection.
File | common/interface/json_api.h |
Classes |
JsonApiContext UJsonApiContext JsonApi IJsonApiConnection |
class JsonApiContext {
public:
JsonApiContext() {
providers = 0;
}
void RegisterJsonApi(class UJsonApiContext * provider) {
provider->next = providers;
providers = provider;
}
class JsonApi * CreateJsonApi(const char * name, class IJsonApiConnection * connection, class json_io & msg, word base) {
for (class UJsonApiContext * p = providers; p; p = p->next) {
if (!strcmp(name, p->Name())) return p->CreateJsonApi(connection, msg, base);
}
return 0;
}
class JsonApi * JsonApiRequested(const char * name, class IJsonApiConnection * connection) {
for (class UJsonApiContext * p = providers; p; p = p->next) {
if (!strcmp(name, p->Name())) return p->JsonApiRequested(connection);
}
return 0;
}
class UJsonApiContext * providers;
};
The class JsonApiContext must be used as base class by an object using other Json Api implementations. Typically the instance class of an app uses this class as base class. This class is passed as argument on the constructor of AppWebsocket so that for incoming messages an api implementation can be found.
class UJsonApiContext {
public:
virtual class JsonApi * CreateJsonApi(class IJsonApiConnection * connection, class json_io & msg, word base) = 0;
virtual class JsonApi * JsonApiRequested(class IJsonApiConnection * connection) { return 0; };
virtual const char * Name() = 0;
class UJsonApiContext * next;
};
This class is used as base class by an api implementation. With this class the api is added to the list of providers for a given JsonApiContext.
class JsonApi {
public:
virtual ~JsonApi() {};
virtual const char * Name() = 0;
virtual void JsonApiStart() {};
virtual void Message(class json_io & msg, word base, const char * mt, const char * src) = 0;
virtual void JsonApiConnectionClosed() = 0;
};
This is the base class for JSON APIs. One instance can be attached to one connection. So for each connection the application has to create a new JsonApi instance.
class json_io & msg | The JSON structure containing the message. |
word base |
The ID of the base element of the message inside the msg structure. Pass it to the get functions of json_io to read the attributes of the message.
Example:
const char * text = msg.get_string(base, "text"); |
const char * mt | The message type of the message. |
const char * src | The src is used for multiplexing on the remote side. If specified, all answers to this message should contain the same src value. |
class IJsonApiConnection {
public:
virtual ~IJsonApiConnection() {}
virtual void RegisterJsonApi(class JsonApi * api) = 0;
virtual void UnRegisterJsonApi(class JsonApi * api) = 0;
virtual void JsonApiMessage(class json_io & msg, char * buffer) = 0;
virtual void JsonApiMessageComplete() = 0;
virtual bool JsonApiPermission(const char * api) = 0;
virtual const char * JsonApiUserDomain() = 0;
virtual const char * JsonApiUserSip() = 0;
virtual const char * JsonApiUserDn() = 0;
virtual const char * JsonApiUserGuid() = 0;
virtual const char * JsonApiApp() = 0;
virtual const char * JsonApiInfo() = 0;
virtual bool JsonApiUnlicensed() = 0;
};
This interface represents a websocket connection that supports plugging-in JsonApi instances. A well-known implementation of the interface is AppWebsocket.
class JsonApi * api | The JsonApi object that shall be attached. |
class JsonApi * api | The JsonApi object that shall be removed. |
class json_io & msg | A JSON structure containing the message. |
char * buffer | A buffer that is big enough to contain the whole encoded message including NULL termination. |
const char * api | The name of the API. |
The class EchoJsonApi derived from JsonApi and the corresponding class EchoJsonApiContext derived from UJsonApiContext implements a simple API for demonstation purposes. The method EchoJsonApi::Message responds on an incoming message type "Ping" with a message type "Pong".
class EchoJsonApiContext : public UJsonApiContext {
class JsonApiContext * jsonApiContext;
char * name;
class EchoJsonApi * echoJsonApi;
public:
EchoJsonApiContext(const char * name, JsonApiContext * jsonApiContext);
virtual ~EchoJsonApiContext();
class JsonApi * CreateJsonApi(class IJsonApiConnection * connection, class json_io & msg, word base) override;
class JsonApi * JsonApiRequested(class IJsonApiConnection * connection) override;
const char * Name() override;
};
class EchoJsonApi : public JsonApi {
char * name;
IJsonApiConnection * connection;
public:
EchoJsonApi(const char * name, IJsonApiConnection * connection);
virtual ~EchoJsonApi();
const char * Name() override;
void Message(class json_io & msg, word base, const char * mt, const char * src) override;
void JsonApiConnectionClosed() override;
};
#include "platform/platform.h"
#include "common/interface/json_api.h"
#include "common/ilib/json.h"
#include "jsonapiexample_echo.h"
EchoJsonApiContext::EchoJsonApiContext(const char * name, JsonApiContext * jsonApiContext)
{
this->name = _strdup(name);
this->jsonApiContext = jsonApiContext;
this->jsonApiContext->RegisterJsonApi(this);
}
EchoJsonApiContext::~EchoJsonApiContext()
{
free(name);
delete this->echoJsonApi;
}
class JsonApi * EchoJsonApiContext::CreateJsonApi(IJsonApiConnection * connection, json_io & msg, word base)
{
return 0;
}
class JsonApi * EchoJsonApiContext::JsonApiRequested(IJsonApiConnection * connection)
{
this->echoJsonApi = new EchoJsonApi(name, connection);
return this->echoJsonApi;
}
const char * EchoJsonApiContext::Name()
{
return name;
}
EchoJsonApi::EchoJsonApi(const char * name, IJsonApiConnection * connection)
{
this->name = _strdup(name);
this->connection = connection;
}
EchoJsonApi::~EchoJsonApi()
{
free(name);
}
const char * EchoJsonApi::Name()
{
return name;
}
void EchoJsonApi::Message(json_io & msg, word base, const char * mt, const char * src)
{
if (!strcmp(mt, "Ping")) {
const char * text = msg.get_string(base, "text");
char sb[200];
class json_io send(sb);
word base = send.add_object(0xFFFF, 0);
send.add_string(base, "api", name);
send.add_string(base, "mt", "Pong");
if (text) send.add_string(base, "text", text);
if (src) send.add_string(base, "src", src);
connection->JsonApiMessage(send, sb);
}
connection->JsonApiMessageComplete();
}
void EchoJsonApi::JsonApiConnectionClosed()
{
delete this;
}
A new EchoJsonApiContext object is instantiated directly after the call to the RegisterJsonApi(this); function in the app class constructor. This will register the API "EchoApi" at the JsonApiContext of the app class. As soon as an incoming message adressing the "EchoApi" arrives at the AppWebsocket, the AppWebsocket will call JsonApiRequested function and instantiate an EchoJsonApi object.
...
#include "jsonapiexample_echo.h"
...
jsonapiexample::jsonapiexample(IIoMux * const iomux, class jsonapiexampleService * service, AppInstanceArgs * args) : AppInstance(service, args), AppUpdates(iomux), ConfigContext(nullptr, this)
{
...
RegisterJsonApi(this);
this->echoJsonApiContext = new EchoJsonApiContext("EchoApi", this);
Log("App instance started");
}
jsonapiexample::~jsonapiexample()
{
delete this->echoJsonApiContext;
}