From 42c9ed133c166de9c99b4837f834aa5cf465e9a2 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Sun, 1 Sep 2013 23:40:37 -0700 Subject: [PATCH] Environment redesign and test/examples review. English API revisions --- example/dbos/workdir/storage/Directory.hpp | 2 +- example/dbos/workdir/storage/File.hpp | 2 +- include/anna/comm/INetAddress.hpp | 62 +++++----- include/anna/comm/LiteTransport.hpp | 26 ++-- include/anna/comm/ReceiverFactory.hpp | 65 ++++------ include/anna/comm/SureTransport.hpp | 20 ++- include/anna/comm/Transport.hpp | 31 ++--- include/anna/comm/TransportFactoryImpl.hpp | 2 +- include/anna/comm/functions.hpp | 106 ++++++++-------- include/anna/core/util/Environment.hpp | 35 +++++- source/core/util/Environment.cpp | 137 ++++++++++++--------- test/core/SConscript | 4 +- test/core/main.cpp | 12 +- 13 files changed, 256 insertions(+), 248 deletions(-) diff --git a/example/dbos/workdir/storage/Directory.hpp b/example/dbos/workdir/storage/Directory.hpp index 88bd645..9f4f670 100644 --- a/example/dbos/workdir/storage/Directory.hpp +++ b/example/dbos/workdir/storage/Directory.hpp @@ -94,7 +94,7 @@ private: const filesystem::Directory* a_filesystemDirectory; int a_inode; - // No usa la base de datos => No requiere dbms::Statement + // dbms::Statement is not required dbms::Statement* initialize (dbms::Database&) throw (RuntimeException) { return NULL; } bool load (dbms::Connection*, const dbos::StorageArea*) throw (RuntimeException); }; diff --git a/example/dbos/workdir/storage/File.hpp b/example/dbos/workdir/storage/File.hpp index 239ea65..6cee61e 100644 --- a/example/dbos/workdir/storage/File.hpp +++ b/example/dbos/workdir/storage/File.hpp @@ -97,7 +97,7 @@ private: const filesystem::File* a_filesystemFile; int a_inode; - // No usa la base de datos => No requiere dbms::Statement + // dbms::Statement is not required dbms::Statement* initialize (dbms::Database&) throw (RuntimeException) { return NULL; } bool load (dbms::Connection*, const dbos::StorageArea*) throw (RuntimeException); }; diff --git a/include/anna/comm/INetAddress.hpp b/include/anna/comm/INetAddress.hpp index a592b53..6ca22f7 100644 --- a/include/anna/comm/INetAddress.hpp +++ b/include/anna/comm/INetAddress.hpp @@ -57,9 +57,7 @@ namespace comm { class Device; /** - Abstraccion de direcciones de red. - - Facilita el uso de las direcciones de red. + Network address abstraction. */ class INetAddress { public: @@ -70,84 +68,82 @@ public: /** Constructor. - \param device Instancia del dispositivo. - \param port Numero de puerto. + \param device Instance for device (address). + \param port Port number. */ INetAddress(const Device* device, const int port = -1) : a_device(device), a_port(port) {;} /** - Constructor copia. - \param other Direccin IP de la que obtener la informacin. + Copy constructor. + \param other Source network address. */ INetAddress(const INetAddress& other) : a_device(other.a_device) , a_port(other.a_port) {;} /** - Devuelve la direccion asociada a esta instancia. - \param exceptionWhenNull Indica si debemos lanzar una excepcion en caso de que el dispositivo - asociado sea NULL. - \return La direccion asociada a esta instancia. + Returns the device (address) associated to this instance. + \param exceptionWhenNull Exception is launched in case the device returned is NULL. + \return Device (address) associated to this instance. */ const Device* getDevice(const bool exceptionWhenNull = true) const throw(RuntimeException); /** - Devuelve el puerto asociada a esta instancia. - \return El puerto asociada a esta instancia. + Returns the port associated to this instance. + \return Port associated to this instance. */ int getPort() const throw() { return a_port; } /** - Establece la direccion IP correspondiente a este objeto. - \param device Dispositivo de red asociado a este objeto. + Sets the address for this instance. + \param device Address provided. */ void setAddress(const Device* device) throw() { a_device = device; } /** - Establece el puerto correspondiente a este objeto. - \param port Numero de puerto correspondiente a este objeto. + Sets the port for this instance. + \param port Port provided. */ void setPort(const int port) throw() { a_port = port; } /** - Operador copia. - \param right Direccin IP de la que obtener la informacin. + Copy operator. + \param right Source address to be copied. */ INetAddress& operator = (const INetAddress& right) throw() { a_device = right.a_device; a_port = right.a_port; return *this; } /** - Operador de comparacion. - \param right Direccion con la comparar. - @return \em true si la direccion recibida como parametro coincide con esta. - \em false en otro caso. + Comparison operator. + \param right Source address to be compared. + @return \em true when address provided is equal to this \em false in other case. */ bool operator == (const INetAddress& right) const throw() { return a_device == right.a_device && a_port == right.a_port; } /** - Devuelve el estado de inicializacin de esta direccin de red. - @return \em true si no ha sido inicializa o \em false en otro caso. + Returns the initialized state for this network address. + @return \em true when initialized, \em false when not. */ bool isNull() const throw() { return (a_device == NULL || a_port == -1); } /** - Elimina el contenido de esta instancia. + Clear the content for this instance. */ void clear() throw() { a_device = NULL; a_port = -1; } /** - Devuelve una cadena la informacion mas relevante de esta instancia. - @return Una cadena la informacion mas relevante de esta instancia. + Returns string with relevant information for this instance. + @return string with relevant information for this instance. */ std::string asString() const throw(); /** - Devuelve una cadena la informacion mas relevante de esta instancia en formato de bajo nivel. - @return Una cadena la informacion mas relevante de esta instancia en formato de bajo nivel. + Returns string with low-level format relevant information for this instance. + @return string with low-level format relevant information for this instance. */ std::string serialize() const throw(); /** - Devuelve un documento XML con la informacion mas relevante de esta instancia. - \param parent Nodo XML del que deben depender los datos a crear. - @return Un documento XML con la informacion mas relevante de esta instancia. + Returns XML document with relevant information for this instance. + \param parent XML node from which created data will depend on. + @return XML document with relevant information for this instance. */ xml::Node* asXML(xml::Node* parent) const throw(RuntimeException); diff --git a/include/anna/comm/LiteTransport.hpp b/include/anna/comm/LiteTransport.hpp index 8403a87..ce9365d 100644 --- a/include/anna/comm/LiteTransport.hpp +++ b/include/anna/comm/LiteTransport.hpp @@ -46,38 +46,32 @@ namespace anna { namespace comm { /** - Este protocolo esta orientado a intercambiar mensajes en una red interna, por lo que supone - que no van a existir errores en los mensajes recibidos y que los mensajes son suficientemente cortos - como poder representar su longitud en dos nicos bytes. + Protocol oriented to internal network messages interchange, supposing no errors in transmision and short-sized + messages which allows length representation in only two bytes. No synchronization mechanisms are provided, then + a single error could cause problems in our process. - Los supuestos bajo los que se dise este protocolo facilitan el desarrollo de clases que ofrecen un - gran rendimiento, pero imposibilitan el desarrollo del sistema de re-sincronizacin en caso de que alguno - de los mensajes no cumpla los supuestos. Es decir, si nos llega un mensaje errneo nuestro proceso no sera - capaz de volver a sincronizarse nunca mas. + @warning Encode mode does not provide enough information to allow re-synchronization in case of error - \warning Hay que tener en cuenta que el m�odo de codificacin no ofrece informacin - suficiente para poder re-sincronizar los procesos en caso de error. - - \see Transport. + \see Transport. */ class LiteTransport : public comm::Transport { public: static const int headerSize = sizeof(short int); /**< Tamao de la cabezera del mensaje */ /** - Destructor + Destructor. */ ~LiteTransport(); /** - Devuelve el literal que indentifica de esta clase. - \return el literal que indentifica de esta clase. + Returns the class name literal. + @return class name literal. */ static const char* className() throw() { return "anna::comm::LiteTransport"; } /** - Devuelve el gestor de capas de transporte asociado a esta clase. - \return El gestor de capas de transporte asociado a esta clase. + Returns the transport factory associated to this class + @return transport factory associated to this class */ static TransportFactory& getFactory() throw() { return st_transportFactory; } diff --git a/include/anna/comm/ReceiverFactory.hpp b/include/anna/comm/ReceiverFactory.hpp index 19578e5..3b44c8e 100644 --- a/include/anna/comm/ReceiverFactory.hpp +++ b/include/anna/comm/ReceiverFactory.hpp @@ -52,10 +52,9 @@ namespace comm { class Receiver; /** - Interfaz que deben cumplir las factorias de receptores. + Interface for receivers factories. - Es muy aconsejable que la implementacion particular de esta clase que use nuestra - aplicacion este basada en el uso de #Recycler. Como muestra el siguiente ejemplo: + Is recommended (performance issues) to use #Recycler as in the following example: \code @@ -74,8 +73,7 @@ class Receiver; \endcode - En la mayoria de los casos seria aconsejable usar un instancia de comm::ReceiverFactoryImpl - instanciado con el tipo de receptor usado por nuestra aplicacion. + In most of cases, is recommended to use an comm::ReceiverFactoryImpl instance with the receiver type used in our application. \see Receiver \see ReceiverFactoryImpl @@ -83,32 +81,30 @@ class Receiver; class ReceiverFactory : public Mutex { public: /** - Devuelve el nombre logico de este gestor de receptores. - \return el nombre logico de este gestor de receptores. + Returns the logical name for this receiver factory. + @return logical name for this receiver factory. */ const std::string& getName() const throw() { return a_name; } /** - Crea una instancia del receptor asociado a esta factoria, realiza las comprobaciones - necesarias para optimizar el reuso de instancias de la clase Receiver. + Creates a receiver instance for this factory. Reuse resources when needed. - \return La instancia de un nuevo receiver. - \warning Cada uno de los receptores obtenidos debera ser liberado invocando a #release. + \return The new receiver instance. + \warning All the receivers obtained shall be released by mean #release. */ Receiver* create() throw(RuntimeException); /** - Libera la instancia del receptor recibido como parametro. Realiza las comprobaciones - necesarias para optimizar el reuso de instancias de la clase Receiver. + Release the receiver instance provided. - \param receiver Instancia del receiver a liberar. - \warning El transporte recibido como parametro debera haberse obtenido mediante #create. + \return The receiver instance to be released. + \warning The parameter should have been obtained by mean #create. */ void release(Receiver* receiver) throw(); /** - Devuelve una cadena con la informacion relevante sobre esta instancia. - \return una cadena con la informacion relevante sobre esta instancia. + Returns a string with relevant information for this instance. + @return string with relevant information for this instance. */ std::string asString() const throw() { @@ -118,44 +114,33 @@ public: } /** - Devuelve un documento XML con la informacion relevante sobre esta instancia. - \param parent Nodo XML del que debe depender el documento generado. - \return un documento XML con la informacion relevante sobre esta instancia. + Returns a XML document with relevant information for this instance. + \param parent XML node from which created data will depend on. + @return a XML document with relevant information for this instance. */ xml::Node* asXML(xml::Node* parent) const throw(); protected: /** - Constructor - \param name Nombre logico de esta factoria de receptores. + Constructor. + \param name Logical name for this receivers factory. */ ReceiverFactory(const char* name); /** - Crea realmente la instancia del receptor asociado a esta factoria, solo sera invocado - en caso de que no haya ninguna otra instancia disponible que pueda ser reusada. + Creates the receiver instance if no other reusable instance is found. This method is + called from a critical section activated for this instance. - La invocacion a este metodo se realiza desde una seccion critica activada sobre esta - instancia. - - Es muy aconsejable que la implementacion particular de este metodo este basada en el - uso de #Recycler. - - \return La instancia de un nuevo receiver. - \warning Cada uno de los receptores obtenidos debera ser liberado invocando a #release. + \return The new receiver instance. + \warning All the receivers obtained shall be released by mean #release. */ virtual Receiver* do_create() throw() = 0; /** - Libera realmente la instancia del receptor recibido como parametro. - - Es muy aconsejable que la implementacion particular de este metodo este basada en el - uso de #Recycler. - - La invocacion a este metodo se realiza desde una seccion critica activada sobre esta - instancia. + Release the receiver provided. This method is called from a critical section activated + for this instance. - \param receiver Instancia del receiver a liberar. + \param The receiver instance to be released. */ virtual void do_release(Receiver* receiver) throw() = 0; diff --git a/include/anna/comm/SureTransport.hpp b/include/anna/comm/SureTransport.hpp index a4d132e..892d9e0 100644 --- a/include/anna/comm/SureTransport.hpp +++ b/include/anna/comm/SureTransport.hpp @@ -50,15 +50,9 @@ namespace comm { class Communicator; /** - Clase para definir la capa de transporte principal - - Estructura basica que nos permite ordenar el proceso de analizar un mensaje recibido desde - cualquier medio. El protocolo conoce los detalles semanticos del mensaje que ha recibido, - es decir, conoce como interpretar cada uno de los bytes que componen el mensaje, cuando un - mensaje esta completo. - - El principal problema de cualquier protocolo externo a la hora de recibir es conocer cual - es el tamao de un determinado mensaje. + Class to define main transport layer: message analysis received from any source. Protocol + knows about message semantics when this message is completed: the main problem is find + out the size. */ class SureTransport : public Transport { public: @@ -68,14 +62,14 @@ public: ~SureTransport(); /** - Devuelve el literal que indentifica de esta clase. - \return el literal que indentifica de esta clase. + Class name literal + \return Class name literal */ static const char* className() throw() { return "anna::comm::SureTransport"; } /** - Devuelve el gestor de capas de transporte asociado a esta clase. - \return El gestor de capas de transporte asociado a esta clase. + Returns the transport layer manager associated to this class. + \return transport layer manager associated to this class. */ static TransportFactory& getFactory() throw() { return st_transportFactory; } diff --git a/include/anna/comm/Transport.hpp b/include/anna/comm/Transport.hpp index 5c06baf..d4d5a29 100644 --- a/include/anna/comm/Transport.hpp +++ b/include/anna/comm/Transport.hpp @@ -72,8 +72,8 @@ class Message; class Transport { public: /** - Numero de bytes maximo que puede mantener cada ClientSocket sin identificar - un mensaje propio del protocolo. + Maximum number of bytes kept by each ClientSocket without identifying + a message for the own protocol. */ WHEN_SINGLETHREAD( static const int DefaultOverQuotaSize = 2048; @@ -83,38 +83,33 @@ public: ) /** - Devuelve \em true si la capa de transporte tiene activado el sistema de control - de temporizacion. + Returns true if the transport layer has a timming control system activated. */ bool enableTimeout() const throw() { return a_enableTimeout; } /** - Activa el control de temporización para los ClientSocket que fueron creados - usando esta capa de transporte. Los ClientSocket creados usando esta capa - de transporte se cerraran automaticamente si no se detecta actividad en - un determinado periodo de tiempo. - \see Communicator::setTimeout. + Activates the timming control system for the ClientSocket which were created + through this transport layer. They will be automatically closed if no activity + is detected in a time interval. + \see Communicator::setTimeout. */ void activateTimeout() throw() { a_enableTimeout = true; } /** - Activa el control de temporizacion para los ClientSocket que fueron creados - usando esta capa de transporte. + Deactivates the timming control system for the ClientSocket which were created + through this transport layer. */ void deactivateTimeout() throw() { a_enableTimeout = false; } - /** - Devuelve el mensaje de entrada asociado. - \return La instancia del mensaje de entrada asociado. - \warning Exclusivamente de uso interno. - */ + + // Internal use: returns associated input message Message* getInputMessage() throw(RuntimeException) { return (a_inputMessage == NULL) ? nullInputMessage() : a_inputMessage; } /** - Devuevel el numero bytes que reserva este protocolo para el buffer de memoria intermedia. - \return el numero bytes que reserva este protocolo para el buffer de memoria intermedia. + Returns the number of bytes reserved by this protocol for the intermediate buffer. + @return number of bytes reserved by this protocol for the intermediate buffer. */ int getOverQuotaSize() const throw() { return a_overQuotaSize; } diff --git a/include/anna/comm/TransportFactoryImpl.hpp b/include/anna/comm/TransportFactoryImpl.hpp index 795a5f7..d483141 100644 --- a/include/anna/comm/TransportFactoryImpl.hpp +++ b/include/anna/comm/TransportFactoryImpl.hpp @@ -48,7 +48,7 @@ namespace comm { class Transport; /** - Gestor de la capa de transporte ofrecida + Transport factory implementation */ template class TransportFactoryImpl : public TransportFactory { public: diff --git a/include/anna/comm/functions.hpp b/include/anna/comm/functions.hpp index 315897a..73d53b5 100644 --- a/include/anna/comm/functions.hpp +++ b/include/anna/comm/functions.hpp @@ -52,114 +52,106 @@ class Server; class Service; /** - functions - Metodos y variables + functions - Methods and variables */ struct functions : public anna::app::functions { /** - Devuelve el nombre de la maquina sobre la que estamos ejecutando. - \return El nombre de la maquina sobre la que estamos ejecutando. + Returns the host name over which we are executing our process. + @return host name over which we are executing our process. */ static std::string getHostName() throw(RuntimeException); /** - * Helper de uso externo que resuelve el nombre de la maquina recibido como parametro y devuelve - * la primera IP resuelta por el sistema. Tracea los aliases y sus direcciones IP, asi como el - * nombre oficial de la maquina. - * - * \param hostname Nombre logico del servidor que sera usado para resolver. Podria ser una cadena - * de la forma www.gopher.net - * - * \return Primera direccion IP retornadas por el sistema - * - * \see man gethostbyname. + Resolves the host name provided with the first IP returned by the system. + Aliases and their addresses are traced as well as the official host name. + + @param hostname Logical name for the server used to resolve (could be www.gopher.net i.e.). + + @return First IP address returned by the system. + + \see man gethostbyname. */ - static std::string resolveIP(const char* hostname) throw(RuntimeException); // EDU Nov 2012 + static std::string resolveIP(const char* hostname) throw(RuntimeException); /** - Devuelve la referencia de la instancia de nuestra aplicación para trabajar en la - capa anna.comm - @return La referencia de la instancia de nuestra aplicación para trabajar en la - capa anna.comm + Returns our application instance on anna.comm layer. + @return Reference to our application instance on anna.comm layer. */ static comm::Application& getApp() throw(RuntimeException); /** - Codifica un entero de 32 bits en buffer que debe tener, al menos, 4 bytes de longitud. - @param result Buffer donde vamos a codificar el número. - @param n Nmero a codificar. - \return El buffer que contiene el número aplanado. + Encodes an integer number with 32 bits over a buffer with at least 4 bytes of length. + @param result Buffer where the number is encoded. + @param n Number to encode. + \return Buffer with the encoded number. */ static const char* codeInteger(char* result, const int n) throw(); /** - Codifica un entero de 16 bits en buffer que debe tener 2 bytes de longitud. - @param result Buffer donde vamos a codificar el número. - @param n Nmero a codificar. - \return El buffer que contiene el número aplanado. + Encodes an integer number with 16 bits over a buffer with at least 2 bytes of length. + @param result Buffer where the number is encoded. + @param n Number to encode. + \return Buffer with the encoded number. */ static const char* codeShort(char* result, const short int n) throw(); /** - Codifica un entero de 64 bits en buffer que debe tener, al menos, 8 bytes de longitud. - @param result Buffer donde vamos a codificar el numero. - @param n numero a codificar. - \return El buffer que contiene el numero aplanado. + Encodes an integer number with 64 bits over a buffer with at least 8 bytes of length. + @param result Buffer where the number is encoded. + @param n Number to encode. + \return Buffer with the encoded number. */ static const char* codeInteger64(char* result, const Integer64 n) throw(); /** - * Codifica un número en coma flotante de 32 bits según el estandard IEEE-754, en un buffer - * que debe tener, al menos, 4 bytes de longitud. - * @param result Buffer donde vamos a codificar el número. - * @param n Nmero a codificar. - * \return El buffer que contiene el número aplanado. + Encodes a floating number with 32 bits (according to the standard IEEE-754) over a buffer with at least 4 bytes of length. + @param result Buffer where the number is encoded. + @param n Number to encode. + \return Buffer with the encoded number. */ static const char* codeFloat(char* result, const float n) throw(); /** - * Codifica un número en coma flotante de 64 bits según el estandard IEEE-754, en un buffer - * que debe tener, al menos, 8 bytes de longitud. - * @param result Buffer donde vamos a codificar el número. - * @param n Nmero a codificar. - * \return El buffer que contiene el número aplanado. + Encodes a floating number with 64 bits (according to the standard IEEE-754) over a buffer with at least 8 bytes of length. + @param result Buffer where the number is encoded. + @param n Number to encode. + \return Buffer with the encoded number. */ static const char* codeDouble(char* result, const double n) throw(); /** - Decodifica un numero entero de 32 bits contenido en un buffer de 4 bytes. - @param data Buffer que contiene el numero aplanado. - @return El valor del numero contenido en el buffer. + Decodes an 32 bits integer number contained in a 4-bytes buffer. + @param data Buffer with the encoded number. + @return Value for the number contained in the buffer. */ static int decodeInteger(const char* data) throw(); /** - Decodifica un numero entero de 16 bits contenido en un buffer de 2 bytes. - @param data Buffer que contiene el numero aplanado. - @return El valor del numero contenido en el buffer. + Decodes an 16 bits integer number contained in a 2-bytes buffer. + @param data Buffer with the encoded number. + @return Value for the number contained in the buffer. */ static short int decodeShort(const char* data) throw(); /** - Decodifica un numero entero de 64 bits contenido en un buffer de 8 bytes. - @param data Buffer que contiene el numero aplanado. - @return El valor del numero contenido en el buffer. + Decodes an 64 bits integer number contained in a 8-bytes buffer. + @param data Buffer with the encoded number. + @return Value for the number contained in the buffer. */ static Integer64 decodeInteger64(const char* data) throw(); /** - Decodifica un numero en coma flotante de 32 bits contenido en un buffer de 4 bytes, y codificado - según el estandard IEEE-754. - @param data Buffer que contiene el numero aplanado. - @return El valor del numero contenido en el buffer. + Decodes an 32 bits floating number (according to the standard IEEE-754) contained in a 4-bytes buffer. + @param data Buffer with the encoded number. + @return Value for the number contained in the buffer. */ static float decodeFloat(const char* data) throw(); /** - Decodifica un numero en coma flotante de 64 bits contenido en un buffer de 8 bytes, y codificado - según el estandard IEEE-754. - @param data Buffer que contiene el numero aplanado. - @return El valor del numero contenido en el buffer. + Decodes an 64 bits floating number (according to the standard IEEE-754) contained in a 8-bytes buffer. + @param data Buffer with the encoded number. + @return Value for the number contained in the buffer. */ static double decodeDouble(const char* data) throw(); }; diff --git a/include/anna/core/util/Environment.hpp b/include/anna/core/util/Environment.hpp index 02c6aba..7db6aa1 100644 --- a/include/anna/core/util/Environment.hpp +++ b/include/anna/core/util/Environment.hpp @@ -58,25 +58,48 @@ public: ~Environment() {;} /** - Parses the current environment data (all variables available) + Parses the environment data (all variables available) when process was started. + @param envp Environment array passed on main function as third argument. + Cache data is cleared if NULL passed, allowing to get current environment values for variables. */ - void initialize() throw(); + void initialize(char **envp = NULL) throw(); /** - Return associated value (could be empty). + Return associated value (could be empty). This value could be cached at initialization (envp array from main function), if not, would be + cached here. @param variableName Environment variable name. @param exceptionIfMissing When enabled, an exception is launched for missing variables. Empty string in other case (default behaviour). @return Environment value. */ - std::string getValue(const char* variableName, bool exceptionIfMissing = false) const throw(RuntimeException); + std::string getValue(const char* variableName, bool exceptionIfMissing = false) throw(RuntimeException); + std::string getValue(const std::string &variableName, bool exceptionIfMissing = false) throw(RuntimeException); + + /** + Sets an environment variable. If an empty variable name is provided, or environment set operation fails, + an exception will be launched. + + @param name Variable name. + @param value Variable value + @param overwrite Overwrite an existing variable name/value or keep old value if exists + */ + void setVariable(const std::string &name, const std::string &value, bool overwrite = true) throw(RuntimeException); + + /** + Unsets an environment variable (different than set empty string). If an empty variable name is provided, + or environment set operation fails, an exception will be launched. + + @param name Variable name. If empty, nothing is done. + */ + void unsetVariable(const std::string &name) throw(RuntimeException); + private: - std::map < std::string /* variable name */, std::string /* variable value */ > a_managedVars; + std::map < std::string /* variable name */, std::string /* variable value */ > a_vars; - Environment() { initialize();} + Environment() {;} friend class Singleton ; }; diff --git a/source/core/util/Environment.cpp b/source/core/util/Environment.cpp index 83ed2f8..820c9ab 100644 --- a/source/core/util/Environment.cpp +++ b/source/core/util/Environment.cpp @@ -39,90 +39,111 @@ #include #include #include +#include -#include -#include - - -#include +#include // getenv / setenv +#include using namespace std; using namespace anna; +extern int errno; - -void Environment::initialize() throw() { +void Environment::initialize(char **envp) throw() { LOGMETHOD(TraceMethod tm("Environment", "initialize", ANNA_FILE_LOCATION)); // clear data - a_managedVars.clear(); - // Register: - namespace po = boost::program_options; - po::options_description desc("Options"); - FILE *fp; - char c_var[256]; - std::string var; - /* Open the command for reading. */ - fp = popen("env | cut -d'=' -f1", "r"); - - if(fp == NULL) { - Logger::error("Failed to get environment variables list", ANNA_FILE_LOCATION); - return; + a_vars.clear(); + if (!envp) return; + + // register data + std::string assignment, var, val; + while (*envp) { + assignment = *envp; + std::size_t equalPos = assignment.find("="); + if (equalPos != string::npos) { // protection + var = assignment.substr(0, equalPos - 1); + val = assignment.substr(equalPos, assignment.size() - 1); + a_vars[var] = val; + } + envp++; } +} - /* Read the output a line at a time - output it. */ - while(fgets(c_var, sizeof(c_var) - 1, fp) != NULL) { - var = c_var; - boost::trim(var); - desc.add_options()(var.c_str(), var.c_str()); - a_managedVars[var] = ""; // temporary - } +std::string Environment::getValue(const char* variableName, bool exceptionIfMissing) throw(RuntimeException) { + if(!variableName) + throw RuntimeException("Invalid NULL variable name!", ANNA_FILE_LOCATION); + + std::string var = variableName; + + return getValue(var, exceptionIfMissing); +} - /* close */ - pclose(fp); - // Parsing: - po::variables_map vm; +std::string Environment::getValue(const std::string &variableName, bool exceptionIfMissing) throw(RuntimeException) { + std::string result = ""; - try { - po::store(po::parse_environment(desc, [](const std::string & variable) { return variable; }), vm); // can throw - std::map::const_iterator it; - std::string var, val; + std::map::const_iterator it = a_vars.find(variableName); - for(it = a_managedVars.begin(); it != a_managedVars.end(); it++) { - var = (*it).first; + if(it == a_vars.end()) { + char *current = getenv (variableName.c_str()); + if (!current) { + std::string msg = "The variable '"; + msg += variableName; + msg += "' is not defined in the environment."; + LOGDEBUG(Logger::debug(msg, ANNA_FILE_LOCATION)); - if(vm.count(var.c_str())) { // protection - val = vm[var.c_str()].as(); - a_managedVars[var] = val; - } + if(exceptionIfMissing) throw RuntimeException(msg, ANNA_FILE_LOCATION); } - - po::notify(vm); - } catch(po::error& e) { - Logger::error(e.what(), ANNA_FILE_LOCATION); + else { + // assignment + a_vars[variableName] = current; + result = current; + } + } + else { + result = it->second; } + + return result; } -std::string Environment::getValue(const char* variableName, bool exceptionIfMissing) const throw(RuntimeException) { - std::string result = ""; - if(!variableName) - throw RuntimeException("Invalid NULL variable name!", ANNA_FILE_LOCATION); +void Environment::setVariable(const std::string &name, const std::string &value, bool overwrite) throw(RuntimeException) { + + if (name == "") throw RuntimeException("Must provide non-empty variable name", ANNA_FILE_LOCATION); + + if(setenv(name.c_str(), value.c_str(), overwrite ? 1:0) != 0) { + std::string msg = "Cannot set the environment variable '"; + msg += name; + msg += "=\""; + msg += value; + msg += "\"'. The errno is "; + msg += anna::functions::asString(errno); + + throw RuntimeException(msg, ANNA_FILE_LOCATION); + } + + // optimization + if (overwrite) + a_vars[name] = value; +} + - std::map::const_iterator it = a_managedVars.find(variableName); +void Environment::unsetVariable(const std::string &name) throw(RuntimeException) { - if(it == a_managedVars.end()) { - std::string msg = "The variable '"; - msg += variableName; - msg += "' is not defined in the environment."; - LOGDEBUG(Logger::debug(msg, ANNA_FILE_LOCATION)); + if (name == "") throw RuntimeException("Must provide non-empty variable name", ANNA_FILE_LOCATION); - if(exceptionIfMissing) throw RuntimeException(msg, ANNA_FILE_LOCATION); + if(unsetenv(name.c_str()) != 0) { + std::string msg = "Cannot unset the environment variable named '"; + msg += name; + msg += "'. The errno is "; + msg += anna::functions::asString(errno); - return ""; + throw RuntimeException(msg, ANNA_FILE_LOCATION); } - return it->second; + std::map::iterator it = a_vars.find(name); + if(it != a_vars.end()) a_vars.erase(it); } diff --git a/test/core/SConscript b/test/core/SConscript index 4216c09..8b742c8 100644 --- a/test/core/SConscript +++ b/test/core/SConscript @@ -8,8 +8,8 @@ env.MergeFlags (boost_library) anna_library = { 'LIBS' : 'anna_core' } env.MergeFlags (anna_library) -system_library = { 'LIBS' : 'boost_program_options' } -env.MergeFlags (system_library) +#system_library = { 'LIBS' : 'boost_program_options' } +#env.MergeFlags (system_library) pwd = Dir ('.').abspath; current_directory = str (pwd); diff --git a/test/core/main.cpp b/test/core/main.cpp index 09d02ce..b6d4c73 100644 --- a/test/core/main.cpp +++ b/test/core/main.cpp @@ -129,9 +129,17 @@ BOOST_AUTO_TEST_CASE(configuration) { BOOST_AUTO_TEST_CASE(environment) { anna::Environment &env = anna::Environment::instantiate(); - BOOST_CHECK_THROW(env.getValue("WRONG_ENV_VAR", true /* exception if missing */), anna::RuntimeException); + env.initialize(); + BOOST_CHECK_THROW(env.getValue("WRONG_ENV_VAR", true), anna::RuntimeException); // true => exception if missing BOOST_CHECK_THROW(env.getValue(NULL), anna::RuntimeException); - BOOST_REQUIRE_EQUAL(env.getValue("HOME"), std::string("/home/eramos")); + + env.setVariable("TEST_VAR", "my test var value"); + BOOST_REQUIRE_EQUAL(env.getValue("TEST_VAR"), std::string("my test var value")); + env.setVariable("TEST_VAR", "my new test var value", false /* no overwritting */); + BOOST_REQUIRE_EQUAL(env.getValue("TEST_VAR"), std::string("my test var value")); + env.unsetVariable("TEST_VAR"); + BOOST_REQUIRE_EQUAL(env.getValue("TEST_VAR"), std::string("")); + BOOST_REQUIRE_EQUAL(env.getValue("MISSING_ENV_VAR"), std::string("")); } -- 2.20.1