This documentation gives instructions how to develop App Services in C++, that can run on the innovaphone App Platform. The header files for interfaces and libraries provided by the SDK can be found in the subfolders of sdk/common.
For each application a main function is needed that initializes the application and all of its dependencies. This main function should be located in an applicationname-main.cpp file in the root folder of the project.
The main file includes all needed header files in the following order:
In the main() function all dependencies of the application are initialized using the corresponding Create() function. Then the application is started. All dependencies are passed to the constructor of the application using pointers to their interface class (dependency injection).
As the main file is not shared across platforms it may use system calls, if needed.
The following code snippet shows the example-main.cpp of an application called Example that is dependent of ''IoMux'', ''WebserverPluginProvider'', ''PostgreSQLDatabaseProvider'' and a ''TCPSocketProvider''.
#include "platform/platform.h #include <string.h> #include <sys/un.h> #include <unistd.h> #include "common/os/iomux.h" #include "common/interface/webserver_plugin.h" #include "common/interface/database.h" #include "common/interface/socket.h" #include "common/interface/httpfile.h" #include "common/lib/appservice.h" #include "common/lib/appwebsocket.h" #include "example/example.h" int main(void) { // dependencies class IIoMux * iomux = IIoMux::Create(); IWebserverPluginProvider * webserverPluginProvider = CreateWebserverPluginProvider(); IDatabaseProvider * databaseProvider = CreatePostgreSQLDatabaseProvider(); ISocketProvider * socketProvider = CreateTCPSocketProvider(); // command line arguments AppServiceArgs serviceArgs; serviceArgs.Parse(argc, argv); // start application class Example * service = new Example(iomux, webserverPluginProvider, databaseProvider, socketProvider, &serviceArgs); iomux->Run(); // clean-up dependencies delete service; delete socketProvider; delete databaseProvider; delete webserverPluginProvider delete iomux; delete debug; // return from application return 0; }
Each app should have a main class appservicename (App Service class) which is derived from the base class AppService (found in appservice.h). This base class deals with the comunication with the App Platform Manager by establishing a local unix domain socket. Creation of new instances, stopping and starting them and enabling of log flags options are the core of such information exchange. This class also defines and saves the main provided apps and provided plugins (PBX Manager plugins, if applicable)
class ExampleService : public AppService { //dependencies class IIoMux * iomux; class ISocketProvider * localSocketProvider; class ISocketProvider * tcpSocketProvider; class ISocketProvider * tlsSocketProvider; class IWebserverPluginProvider * webserverPluginProvider; class IDatabaseProvider * databaseProvider; public: ExampleService (class IIoMux * const iomux, class ISocketProvider * localSocketProvider, class ISocketProvider * tcpSocketProvider, class ISocketProvider * tlsSocketProvider, IWebserverPluginProvider * const webserverPluginProvider, class IDatabaseProvider * databaseProvider, AppServiceArgs * args); ~ ExampleService (); //creates the app instance class class AppInstance * CreateInstance(AppInstanceArgs * args) override; // creates and saves the provided apps void AppServiceApps(istd::list* appList) override; //creates and saves the app plugins void AppInstancePlugins(istd::list * pluginList) override; };
The ExampleService::CreateInstance(AppInstanceArgs * args) function is then used to create a new app instance with a main class that is derived from the class AppInstance (found in appservice.h).
class AppInstance * ExampleService::CreateInstance(AppInstanceArgs * args) { return new Example(iomux, localSocketProvider, tcpSocketProvider, tlsSocketProvider, webserverPluginProvider, databaseProvider, args); }
The new app instance class listens to incoming WebSocket messages. Therefore, WebSocket functions along with database related methods should be implemented in this class.
class Example: public UWebserverPlugin, public UDatabase, public AppInstance { //dependencies class IIoMux * iomux; class ISocketProvider * tcpSocketProvider; class ISocketProvider * tlsSocketProvider; class IWebserverPlugin * webserverPlugin; public: Example (IIoMux * const iomux, ISocketProvider * localSocketProvider, class ISocketProvider * tcpSocketProvider, class ISocketProvider * tlsSocketProvider, IWebserverPluginProvider * const webserverPluginProvider, IDatabaseProvider * databaseProvider, struct AppInstanceArgs * args); ~ Example (); void Stop() override; //websocket and database methods void WebserverPluginWebsocketListenResult(IWebserverPlugin * plugin, const char * path, const char * registeredPathForRequest, const char * host) override; void DatabaseConnectComplete(IDatabase * const database) override; void DatabaseShutdown(IDatabase * const database, db_error_t reason) override; void DatabaseError(IDatabase * const database, db_error_t error) override; };
When an app is stopped, the function Example::Stop() is called. This function should be implemented in the main class and should close all WebSocket, database or HTTP connections. When all the connections are terminated, a call to AppService::AppStopped(class AppInstance * instance) is needed indicating that the app has stopped completely.
The App hosted by the App Service is a web application that consists of a number of static files. That files need to be included in the binary of the App Service. In order to achieve that you need to
Example:
$(OUTDIR)/obj/apps.cpp: apidemo/apps/*.* $(IP_SRC)/exe/httpfiles -k -d apidemo/apps -t $(OUTDIR) -o $(OUTDIR)/obj/apps.cpp \ apidemo.htm,0,HTTP_GZIP \ apidemo.png,0,HTTP_GZIP \ innovaphone.apidemo.js,0,HTTP_GZIP
Classes and function names use UpperCamelCase.
Variables and parameters use lowerCamelCase.
In general opening braces are put on the same line. Closing braces are put on a line on their own.
if (x > 3) { ... } else { ... }
However, there is one special case, namely functions: they have the opening brace at the beginning of the next line.
int MyFunction(int x) { ... }
Indentations are 4 spaces. Tabs are not to be used. There is no common definition what a tab is, if looking at code with an editor with a different understanding of tabs, tabs in the code can make code almost unreadable.
Additional spaces do not necessarily make code more readable. It is better to use spaces the same way as in common written language or in mathematical expressions.
int MyFunction(int a, int b, int c) { ... } if (a == b || a == c) { ... } for (int i = 0; i < 1000; i++) { ... }
To seperate functional blocks in the code a single empty line is good enough.
if (x == y) { ... } else if (x > y) { ... } else { .... }
switch (c) { case 1: ... break; case 2: { int x; ... } break; default: ... }
do { ... } while (x < y)
for (int i = 0; i < 1000; i++) { ... }
class x : public y { ... public: ... };
All system interfaces are designed in a way that there are abstract classes, with a name starting with 'I' which provide assynchronous services for applications. These classes are typcally allocated with a 'Create' member function. In case of the main class of an interface this is a static Create function.
The interfaces also define abstract 'User' classes, with a name starting with 'U'. The application uses these as base classes to own classes. These classes define callback functions which are called by the interface to signal events like command completion or incoming requests.
Interfaces must be asynchronous. That means that calls to an 'I' class return immediately. If the function call triggers a lengthy action, the application is notified when the action is finished using a callback to the corresponding 'U' class. Calls to functions in an 'I' class should not trigger callbacks to the 'U' class, before the interface call returns. All interfaces in common/interface are guaranteed to follow that behaviour.
Example:
class ITask { public: virtual void Start(class UTask * user) = 0; }; class UTask { public: virtual void TaskComplete(class ITask * const task) = 0; virtual void TaskFailed(class ITask * const task) = 0; };