Nous allons voir commenter implémenter facilement un module dans cette section.
Il existe 6 types de modules :
Ils dérivent tous d'une interface / abstract. Une section sera dédiée pour l'implémentation de chacun de ces modules.
#ifndef API_V2_ICORE_HPP #define API_V2_ICORE_HPP #include <vector> #include "IModule.hpp" class IModuleLoader; class IConfig; namespace Module { class ICore { public: virtual bool Run(std::vector<std::string> const &opts, IModuleLoader *moduleLoader) = 0; virtual IConfig const &GetConfig() const = 0; public: virtual IModule *Get(std::string const &name) const = 0; virtual IModule *Get(IModule::Type type) const = 0; }; } #endif //API_V2_ICORE_HPP
Le module Core représente le coeur du programme. Il va être le premier module chargé, et s'occuper de lancer tous les modules obligatoires. Il va aussi se charger de lancer le module Config Loader.
Run()
Permet de lancer le fonctionnement du Core.
Il requiert les arguments du main, ainsi qu'un Module Loader.
GetConfig()
Permet de récupérer la configuration actuelle
du Core.
Get()
Permet de récupérer un module du Core.
Il est possible de retrouver un module par son nom, ou encore son Enum.
#ifndef API_V2_ILOGGING_HPP #define API_V2_ILOGGING_HPP #include <string> namespace Module { class ILogger { public: enum Type { Emergency, Alert, Critic, Error, Warning, Notice, Info, Debug }; virtual void Log(std::string const &msg, Type type = Type::Info) const = 0; }; } #endif //API_V2_ILOGGING_HPP
Ce module permet de tracer l'activité de chaque module couramment chargé par le Core.
enum Type
Permet de spécifier le degré d'importance
des messages affichés (Debug étant de basse priorité, et Emergency de niveau critique).
Log()
Affiche des messages, en prenant en compte
leur priorité définie précédement.
#ifndef API_V2_ICONFIGLOADER_HPP #define API_V2_ICONFIGLOADER_HPP #include <string> class IConfig {}; namespace Module { class IConfigLoader { public: virtual IConfig *LoadConfig(std::string const &path) = 0; }; } #endif //API_V2_ICONFIGLOADER_HPP
Ce module va s'occuper de charger la configuration par défault, ainsi que celle du fichier s'il est fourni.
LoadConfig()
Charge la configuration située au chemin
passé en argument, et renvoie un handler sur celle-ci.
#ifndef API_V2_INETWORK_HPP #define API_V2_INETWORK_HPP namespace HTTP { class Request; class Response; class ProcessingList; } namespace Module { class INetwork { public: virtual bool Start(HTTP::Request *req, HTTP::Response *res, HTTP::ProcessingList *pl) = 0; virtual void Poll() = 0; virtual void Run() = 0; }; } #endif //API_V2_INETWORK_HPP
Ce module va permettre toute la gestion des connexions réseau.
Start()
Permet de lancer le réseau.
Poll()
Gestion du réseau de manière non bloquante.
Run()
Gestion du réseau de manière bloquante.
#ifndef API_V2_IFILESERVE_HPP #define API_V2_IFILESERVE_HPP #include <string> #include <fstream> namespace Module { class IFileServe { public: virtual unsigned int GetFileSize(std::string const &path) const = 0; virtual std::ifstream &GetFile(std::string const &path) const = 0; virtual char *GetFileByRange(unsigned int offset, unsigned int size) const = 0; }; } #endif //API_V2_IFILESERVE_HPP
Ce module va chercher des fichiers en fonction des chemins qui lui sont fournis.
GetFileSize()
Récupère la taille du fichier situé au
chemin donné en paramètre.
GetFile()
Récupère le fichier situé au chemin
donné en paramètre, sous la forme d'un stream.
GetFileByRange()
Renvoie un ensemble de caractères
défini par un début et une taille.
#ifndef API_V2_IMODULE_HPP #define API_V2_IMODULE_HPP #include <string> namespace Module { class ICore; class IHTTPHandle { public: virtual bool Handle(HTTP::Request *req, HTTP::Response *res, HTTP::ProcessingList *pl) = 0; }; } #endif //API_V2_IMODULE_HPP
Il s'occupe de la gestion des modules liés au protocole HTTP.
Handle()
Il s'agit du callback appelé à chaque fois
que le module est invoqué par la processing list.
Maintenant que vous connaissez les caractériques de chaque module, voyons à présent une implémentation concrète d'un module avec la SaltAPI.
Tout d'abord, créons une classe HelloWord
qui nous servira d'exemple.
Son but va être de modifier le header en ajoutant le contenu Content-type: text/plain
,
changer le body par "HelloWorld"
, et finalement changer le status code à
200
.
Étant sur Windows, il ne faut pas oublier deux choses: nous allons créer des librairies
dynamiques, ce qui implique d'ajouter un extern "C"
afin de
correctement récupérer les symboles, et ajouter EXPORT
en incluant
le fichier "DllExport.hpp"
.
#include "Module/IHTTPHandle.hpp" #include "Module/AModule.hpp" #include "DllExport.hpp" extern "C" { class EXPORT HelloWorld : public Module::IHTTPHandle, public Module::AModule { public: HelloWorld(Module::ICore &core); virtual ~HelloWorld(); }; Module::IModule *LoadModule(Module::ICore &core); }
Une étape importante est l'implémentation du contructeur de la classe parent, Module::AModule
.
En effet il va à la fois déterminer le rôle de notre module, ainsi que son nom afin de plus
facilement le retrouver. Le notre interviendra en tant que module HTTP, et s'appelera "HelloWorld".
HelloWorld::HelloWorld(Module::ICore &core): Module::AModule(core, HTTP, "HelloWorld") {}
Implémentons maintenant la méthode bool Handle(HTTP::Request *req, HTTP::Response *res, HTTP::ProcessingList *pl);
(implémentée dans la classe Module::IHTTPModule). Celle-ci est très succinte :
bool HelloWorld::Handle(HTTP::Request *req, HTTP::Response *res, HTTP::ProcessingList *pl) { res->GetHeader().Add("Content-Type", "text/plain"); res->SetBody("HelloWorld"); res->SetStatusCode(200); return true; }
Seule une étape nous sépare maintenant d'un module fonctionnel ! Il nous manque la méthode capable
de cloner notre module, dont la signature Module::IModule *GetModule(Module::ICore &core) const;
est implémentée dans Module::AModule. Copiez les valeurs nécessaires à la construction
(dans l'exemple nous n'avons pas d'attributs).
Module::IModule* HelloWorld::GetModule(Module::ICore &core) const { return new HelloWorld(core); }
Félicitations ! Votre module est désormais fonctionnel. Un petit récapitulatif :
#ifndef API_V2_HELLOWORLD_HPP #define API_V2_HELLOWORLD_HPP #include "Module/IHTTPHandle.hpp" #include "Module/AModule.hpp" #include "DllExport.hpp" extern "C" { class EXPORT HelloWorld : public Module::IHTTPHandle, public Module::AModule { public: HelloWorld(Module::ICore &core); virtual ~HelloWorld(); public: bool Handle(HTTP::Request *req, HTTP::Response *res, HTTP::ProcessingList *pl); public: Module::IModule *GetModule(Module::ICore &core) const override; }; Module::IModule *LoadModule(Module::ICore &core); } #endif //API_V2_HELLOWORLD_HPP
#include "HelloWorld.hpp" #include "HTTP/Request.hpp" #include "HTTP/Response.hpp" #include "HTTP/ProcessingList.hpp" HelloWorld::HelloWorld(Module::ICore &core): Module::AModule(core, HTTP, "HelloWorld") { } HelloWorld::~HelloWorld() { } bool HelloWorld::Handle(HTTP::Request *req, HTTP::Response *res, HTTP::ProcessingList *pl) { res->GetHeader().Add("Content-Type", "text/plain"); res->SetBody("HelloWorld"); res->SetStatusCode(200); return true; } Module::IModule* HelloWorld::GetModule(Module::ICore &core) const { return new HelloWorld(core); } Module::IModule *LoadModule(Module::ICore &core) { return new HelloWorld(core); }