Ukázka použití API knihovny libxr

Níže naleznete kompletní implementaci (v C) ukázkového serveru, který umožňuje heslem zabezpečený přístup k stavovým informacím Zbloku, přes HTTP/SSL pomocí XML-RPC. Zblok takovýto kód nepoužívá, je to jen ukázka malé části toho co libxr umí.
Samotná knihovna není až zas tak zajímavá. Implementuje běžný více-vláknový HTTP server. Server má frontou příchozích spojení, krmenou hlavním threadem a thread pool workerů, kteří frontu vyjídávají a obsluhují samotná připojení a jednotlivá vzdálená volání procedur.
Libxr se odlišuje od ostatních knihoven v tom, že nabízí kompilátor jazyka pro popis rozhraní (XDL). XDL kompilátor provádí generování nudného kódu, který převádí nativní datové typy jazyka C na objekty xr_value a zpět.
V tom je hlavní hodnota libxr pro programátora, protože:
- programátor se stará jen o akční kód metody, ne o lepidlo mezi akčním kódem a tím co vyžaduje knihovna
- minimum psaní, snadné ošetření chyb, snadná definice rozhraní a práce s daty (programátor se nemusí učit vnitřní datové typy knihovny, pracuje s tím co si sám nadefinuje a s běžnými datovými typy jazyka)
- místo vytváření nudných chyb v lepidle, se může programátor soustředit na daleko kreativnější chyby v akčním kódu
- co se týče snadnosti implementace XML-RPC serveru a klienta, libxr staví C na stejnou úroveň jaku mají dynamické jazyky
- XDL slouží jako specifikace a dokumentace komunikačního protokolu (kompilátor umí vygenerovat XDL s osekanou implementací pro účely dokumentace)
XDL soubor obsahující definici a implementaci rozhraní
namespace ZM; // Zblok Management Interface error AUTH_FAILED = 1; error NOT_AUTHORIZED = 2; struct User { string username; string realname; int mail_usage; } struct Folder { string name; string type; int size; } struct SystemStatus { int uptime; array<User> users; array<Folder> folders; } servlet Server { // Some code you want to get included at the top of servlet subs file. <% #include <string.h> %> // Servlet variables (This code is inserted into struct definition. You can // access these variables using _priv pointer, see below for example) __attrs__ <% int auth_ok; // True if client is authenticated char* username; // Contains username of the user (when authenticated) %> // This is called before first RPC call. __init__ <% _priv->auth_ok = FALSE; %> // Called when client disconnects. __fini__ <% g_free(_priv->username); %> // Called before RPC is processed using methods defined below. You may do some // common call processing here (authentication check, locking, etc.) Return // FALSE if you want to inhibit the actual RPC call. __pre_call__ <% const char* method = xr_call_get_method(_call); if (strcmp(method, "auth")) { if (!_priv->auth_ok) { char* msg = g_strdup_printf("Method %s requires authentication. (call auth please).", method); xr_call_set_error(_call, ZM_XMLRPC_ERROR_NOT_AUTHORIZED, msg); g_free(msg); return FALSE; } } return TRUE; %> // Called after each call is processed and before server sends response to the // client. __post_call__ <% xr_call_dump(_call, 0); %> boolean auth(string username, string password) <% g_free(_priv->username); _priv->username = NULL; _priv->auth_ok = FALSE; if (!strcmp(username, "bob") && !strcmp(password, "qwe")) { _priv->username = g_strdup(username); _priv->auth_ok = TRUE; return TRUE; } g_set_error(_error, 0, ZM_XMLRPC_ERROR_AUTH_FAILED, "Auth failed!"); %> SystemStatus getSystemStatus() <% ZMUser *user; ZMFolder *folder; // create SystemStatus object retval = ZMSystemStatus_new(); // create User object for bob user = ZMUser_new(); user->username = g_strdup("bob"); user->realname = g_strdup("Bobby"); user->mail_usage = 20; retval->users = g_slist_append(retval->users, user); // create Folder object folder = ZMFolder_new(); folder->name = g_strdup("data"); folder->type = g_strdup("shared"); folder->size = 1024*45; retval->folders = g_slist_append(retval->folders, folder); %> boolean changeUserPassword(string newpassword) <% if (strlen(newpassword) <= 5) { g_set_error(_error, 0, ZM_XMLRPC_ERROR_NOT_AUTHORIZED, "Password is too short!"); return FALSE; } //XXX: change password return TRUE; %> }
Kód XML-RPC serveru
#include "ZMServer.xrs.h" int main(int ac, char* av[]) { GError* err = NULL; xr_servlet_def* defs[2] = { __ZMServerServlet_def(), NULL }; g_thread_init(NULL); // start HTTPS server, with 5 threads in the pool, listening on port 1234, // and implementing ZMServer servlet. xr_server_simple("server.pem", 5, "*:1234", defs, &err); if (err) g_printerr("ERROR: %s\n", err->message); return !!err; }
Zjednodušený XDL soubor obsahující definici a implementaci rozhraní
Výše uvedený kód lze ještě více zjednodušit, když vynecháme
autentizaci. V C už to jednodušeji nejde. 
namespace ZM; // Zblok Management Interface struct User { string username; string realname; int mail_usage; } struct Folder { string name; string type; int size; } struct SystemStatus { int uptime; array<User> users; array<Folder> folders; } servlet Server { SystemStatus getSystemStatus() <% ZMUser *user; ZMFolder *folder; // create SystemStatus object retval = ZMSystemStatus_new(); // create User object for bob user = ZMUser_new(); user->username = g_strdup("bob"); user->realname = g_strdup("Bobby"); user->mail_usage = 20; retval->users = g_slist_append(retval->users, user); // create Folder object folder = ZMFolder_new(); folder->name = g_strdup("data"); folder->type = g_strdup("shared"); folder->size = 1024*45; retval->folders = g_slist_append(retval->folders, folder); %> boolean changeUserPassword(string newpassword) <% if (strlen(newpassword) <= 5) { g_set_error(_error, 0, -1, "Password is too short!"); return FALSE; } //XXX: change password return TRUE; %> }
VALA klient
Bonus závěrem.
XDL kompiláor jsme naučil generovat bindings pro nový
perspektivní jazyk VALA. Zde je krátká ukázka použití s výše uvedeným
rozhraním.
using GLib; using ZM; using XR; namespace ZM { // Print the list of zblok users static void sysinfo() throws GLib.Error { var s = new ZM.Server(); s.open("https://localhost:1234"); var data = s.getSystemStatus(); foreach (weak User u in data.users) stdout.printf("User %s '%s' (mail usage %d kB)\n", u.username, u.realname, u.mail_usage); } static int main() { XR.init(); try { sysinfo(); } catch(GLib.Error e) { stderr.printf("ERROR: %d: %s\n", e.code, e.message); } XR.fini(); return 0; } }
Alternativní XML-RPC knihovny napsané v C
- http://xmlrpc-c.sourceforge.net/ – Jeden ze stabilních a stále udržovaných konkurenčních projektů.
