X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=doc%2Fafb-plugin-writing.html;h=b80006f40c8cf3f9cb5ac9d47ed7572b0b0e17e7;hb=6914f7781b42972263a417484bbeb179efe66e78;hp=9873b6266cf68035224b7c38adb18ccba52ee604;hpb=d96d0533b8326570db57d13b8f808bc62d1a7fa4;p=src%2Fapp-framework-binder.git diff --git a/doc/afb-plugin-writing.html b/doc/afb-plugin-writing.html index 9873b626..b80006f4 100644 --- a/doc/afb-plugin-writing.html +++ b/doc/afb-plugin-writing.html @@ -8,7 +8,7 @@
version: 1
-Date: 27 mai 2016
+Date: 29 mai 2016
Author: José Bollo
@@ -18,14 +18,14 @@ Author: José Bollo
The binder afb-daemon serves files through -the HTTP protocol and offers access to API’s through +
The binder afb-daemon serves files through HTTP protocol +and offers to developers the capability to expose application APIs through HTTP or WebSocket protocol.
-The plugins are used to add API’s to afb-daemon. +
Binder plugins are used to add API to afb-daemon. This part describes how to write a plugin for afb-daemon. Excepting this summary, this part is intended to be read -by developpers.
+by developers. -Before going into details, through a tiny example, -a short overview plugins basis is needed.
+Before moving further through an example, here after +a short overview of binder plugins fundamentals.
A plugin is a separate piece of code made of a shared library. -The plugin is loaded and activated by afb-daemon when afb-daemon -starts.
+A plugin is an independent piece of software, self contain and expose as a dynamically loadable library. +A plugin is loaded by afb-daemon that exposes contained API dynamically at runtime.
-Technically, a plugin is not linked to any library of afb-daemon.
+Technically, a binder plugins does not reference and is not linked with any library from afb-daemon.
- -There is two kinds of plugins: application plugins and service -plugins.
+Application binder supports two kinds of plugins: application plugins and service +plugins. Technically both class of plugin are equivalent and coding API is shared. Only sharing mode and security context diverge.
- -Application plugins are intended to be instanciated for each -application: when an application using that plugin is started, -its binder starts a new instance of the plugin.
+Application-plugins implements the glue in between application’s UI and services. Every AGL application +has a corresponding binder that typically activates one or many plugins to interface the application logic with lower platform services. +When an application is started by AGL application framework, a dedicate binder is started that loads/activates application plugin(s). +The API expose by application-plugin are executed within corresponding application security context.
-It means that the application plugins mainly have only one -context to manage for one client.
+Application plugins generally handle a unique context for a unique client. As the application framework start +a dedicated instance of afb_daemon for each AGL application, if a given plugin is used within multiple application each of those +application get a new and private instance of this “shared” plugin.
- -Service plugins are intended to be instanciated only one time -only and connected to many clients.
+Service-plugins enable API activation within corresponding service security context and not within calling application context. +Service-plugins are intended to run as a unique instance that is shared in between multiple clients.
-So either it does not manage context at all or otherwise, -if it manages context, it should be able to manage one context -per client.
+Service-plugins can either be stateless or manage client context. When managing context each client get a private context.
-In details, it may be useful to have service plugins at a user -level.
+Sharing may either be global to the platform (ie: GPS service) or dedicated to a given user (ie: preference management)
- -The plugins are loaded and activated when afb-daemon starts.
+Application and service plugins are loaded and activated each time a new afb-daemon is started.
-At start, the plugin initialise itself. -If it fails to initialise then afb-daemon stops.
+At launch time, every loaded plugin initialise itself. +If a single plugin initialisation fail corresponding instance of afb-daemon self aborts.
-Conversely, if it success to initialize, it must declare -a name, that must be unique, and a list of API’s verbs.
+Conversely, when plugin initialisation succeeds, it should register +its unique name and the list of API verbs it exposes.
-When initialized, the functions implementing the API’s verbs -of the plugin are activated on call.
+When initialised, on request from clients plugin’s function corresponding to expose API verbs +are activated by the afb-daemon instance attached to the application or service.
-At the end, nothing special is done by afb-daemon. -Consequently, developpers of plugins should use ‘atexit’ -or ‘on_exit’ during initialisation if they need to -perform specific actions when stopping.
+At exit time, no special action is enforced by afb-daemon. When a specific actions is required at afb-daemon stop, +developers should use ‘atexit/on_exit’ during plugin initialisation sequence to register a custom exit function.
- -For afb-daemon, a plugin contains 2 different -things: names and functions.
+Afb-daemon’s plugin register two classes of objects: names and functions.
-There is two kind of names: - - the name of the plugin, - - the names of the verbs.
+Plugins declare categories of names: + - A unique plugin name, + - Multiple API verb’s names.
-There is two kind of functions: - - the initialisation function - - functions implementing verbs
+Plugins declare two categories of functions: + - initialisation function + - API functions implementing verbs
-Afb-daemon translates the name of the method that is -invoked to a pair of API and verb names. For example, -the method named foo/bar translated to the API -name foo and the verb name bar. -To serve it, afb-daemon search the plugin that record -the name foo and if it also recorded the verb bar, -it calls the implementation function declared for this verb.
+Afb-daemon parses URI requests to extract plugin name and API verb. +As an example, URI foo/bar translates to API verb named bar within plugin named foo. +To serve such a request, afb-daemon looks for an active plugin named foo and then within this plugin for an API verb named bar. +When find afb-daemon calls corresponding function with attached parameter if any.
-Afb-daemon make no distinction between lower case -and upper case when searching for a method. -Thus, The names TicTacToe/Board and tictactoe/borad -are equals.
+Afb-daemon ignores letter case when parsing URI. Thus TicTacToe/Board and tictactoe/board are equivalent.
The names of the verbs are not checked.
However, the validity rules for verb’s names are the -same as for API’s names except that the dot (.) character +same as for API names except that the dot (.) character is forbidden.
Afb-daemon make no distinction between lower case @@ -612,12 +601,20 @@ failure replies.
* The status of the reply is automatically set to "success". * Its send the object 'obj' (can be NULL) with an * informationnal comment 'info (can also be NULL). + * + * For conveniency, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. */ void afb_req_success(struct afb_req req, struct json_object *obj, const char *info); /* * Same as 'afb_req_success' but the 'info' is a formatting * string followed by arguments. + * + * For conveniency, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. */ void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...); @@ -633,16 +630,29 @@ void afb_req_success_f(struct afb_req req, struct json_object *obj, const char * * Note that calling afb_req_fail("success", info) is equivalent * to call afb_req_success(NULL, info). Thus even if possible it * is strongly recommanded to NEVER use "success" for status. + * + * For conveniency, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. */ void afb_req_fail(struct afb_req req, const char *status, const char *info); /* * Same as 'afb_req_fail' but the 'info' is a formatting * string followed by arguments. + * + * For conveniency, the function calls 'json_object_put' for 'obj'. + * Thus, in the case where 'obj' should remain available after + * the function returns, the function 'json_object_get' shall be used. */ void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...); ++For conveniency, these functions call json_object_put to release the object obj +that they send. Then obj can not be used after calling one of these reply functions. +When it is not the expected behaviour, calling the function json_object_get on the object obj +before cancels the effect of json_object_put.
+Be aware, as for reply functions, the object is automatically released using +json_object_put by the function. Then call json_object_get before +calling afb_daemon_broadcast_event to keep object available +after the returning of the function.
In fact the event name received by the listener is prefixed with the name of the plugin. So when the change occurs after a move, the reason is move and then the clients receive the event tictactoe/move.
@@ -1261,31 +1280,106 @@ Thus it is safe to compare event using a case sensitive comparison./ - * signals a change of the board - / -static void changed(struct board board, const char reason) +
The tic-tac-toe example allows two clients or more to share the same board. +This is implemented by the verb join that illustrated partly the how to +retrieve arguments.
+ +When two or more clients are sharing a same board, one of them can wait +until the state of the board changes. (This coulded also be implemented using +events because an even is generated each time the board changes).
+ +In this case, the reply to the wait is sent only when the board changes. +See the diagram below:
+ +CLIENT A CLIENT B TIC-TAC-TOE
+ | | |
+ +--------------|----------------->| wait . . . . . . . .
+ | | | .
+ : : : .
+ : : : .
+ | | | .
+ | +----------------->| move . . . .
+ | | | V .
+ | |<-----------------+ success of move .
+ | | | .
+ |<-------------|------------------+ success of wait <
+
+
+Here, this is an invocation of the plugin by an other client that +unblock the suspended wait call. +But in general, this will be a timer, a hardware event, the sync with +a concurrent process or thread, …
+ +So the case is common, this is an asynchronous implementation.
+ +Here is the listing of the function wait:
+ +static void wait(struct afb_req req)
{
- struct waiter waiter, next;
- struct json_object *description;
-
-/* get the description */
-description = describe(board);
-
-waiter = board->waiters;
-board->waiters = NULL;
-while (waiter != NULL) {
- next = waiter->next;
- afb_req_success(waiter->req, json_object_get(description), reason);
- afb_req_unref(waiter->req);
- free(waiter);
- waiter = next;
+ struct board *board;
+ struct waiter *waiter;
+
+ /* retrieves the context for the session */
+ board = board_of_req(req);
+ INFO(afbitf, "method 'wait' called for boardid %d", board->id);
+
+ /* creates the waiter and enqueues it */
+ waiter = calloc(1, sizeof *waiter);
+ waiter->req = req;
+ waiter->next = board->waiters;
+ afb_req_addref(req);
+ board->waiters = waiter;
}
+
-afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description);
+After retrieving the board, the function adds a new waiter to the
+current list of waiters and returns without sending a reply.
+
+Before returning, it increases the reference count of the
+request req using the function afb_req_addref.
+
+When the implentation of a verb returns without sending a reply,
+it MUST increment the reference count of the request
+using afb_req_addref. If it doesn’t bad things can happen.
+
+Later, when the board changes, it calls the function changed
+of tic-tac-toe with the reason of the change.
+
+Here is the full listing of the function changed:
+
+/*
+ * signals a change of the board
+ */
+static void changed(struct board *board, const char *reason)
+{
+ struct waiter *waiter, *next;
+ struct json_object *description;
+
+ /* get the description */
+ description = describe(board);
+
+ waiter = board->waiters;
+ board->waiters = NULL;
+ while (waiter != NULL) {
+ next = waiter->next;
+ afb_req_success(waiter->req, json_object_get(description), reason);
+ afb_req_unref(waiter->req);
+ free(waiter);
+ waiter = next;
+ }
+
+ afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description);
+}
-}
+The list of waiters is walked and a reply is sent to each waiter.
+After the sending the reply, the reference count of the request
+is decremented using afb_req_unref to allow its resources to be freed.
+
+The reference count MUST be decremented using afb_req_unref because,
+otherwise, there is a leak of resources.
+It must be decremented AFTER the sending of the reply, because, otherwise,
+bad things may happen.
How to build a plugin