+// ANNA - Anna is Not Nothingness Anymore //
+// //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
+// //
+// See project site at http://redmine.teslayout.com/projects/anna-suite //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
+
+
+#ifndef anna_diameter_comm_Entity_hpp
+#define anna_diameter_comm_Entity_hpp
+
+
+// STL
+#include <string>
+#include <vector>
+
+#include <anna/config/defines.hpp>
+
+#include <anna/core/RuntimeException.hpp>
+#include <anna/core/util/Millisecond.hpp>
+
+#include <anna/diameter.comm/ClassCode.hpp>
+
+
+namespace anna {
+class DataBlock;
+namespace xml {
+class Node;
+}
+}
+
+
+
+namespace anna {
+
+namespace diameter {
+
+namespace comm {
+
+class Engine;
+class Server;
+class Response;
+class Message;
+class ClientSession;
+
+
+/**
+ Generic diameter server list (N-servers entity)
+*/
+class Entity {
+
+public:
+
+ /**
+ * Sessions based models type
+ */
+ struct SessionBasedModelsType { enum _v { RoundRobin, SessionIdOptionalPart, SessionIdHighPart, SessionIdLowPart /* default */ }; };
+
+private:
+
+
+ std::vector<Server*> a_servers;
+ int a_maxServers; // -1 means "no limit to add servers"
+ std::string a_description;
+ int a_category;
+ std::vector<Server*>::iterator a_deliveryIterator;
+ Server *a_lastUsedResource;
+
+ // Engine
+ Engine *a_engine;
+
+ // Balance and socket selection:
+ bool a_balance;
+ SessionBasedModelsType::_v a_sessionBasedModelsType;
+
+ // Availability
+ bool a_available; // any of the servers must be bound
+ void availabilityLost() throw();
+ void availabilityRecovered() throw();
+ bool refreshAvailability() throw(); // return true if change
+ void assertReady() throw(anna::RuntimeException);
+ void initialize() throw();
+ void childIdle() const throw();
+
+ // Selected for remove
+ bool a_deprecated; // entity has been selected as deprecated (will be removed when idle)
+
+ // internal helpers
+ std::string a_socketListLiteral; // ADDRESS:PORT space-separated list
+ std::string a_primarySocketLiteral; // ADDRESS:PORT for primary (used on standard entities)
+ std::string a_secondarySocketLiteral; // ADDRESS:PORT for primary (used on standard entities)
+
+
+ // Activity
+ anna::Millisecond a_lastIncomingActivityTime; // last unix timestamp (in milliseconds) when message reception was managed over this entity
+ anna::Millisecond a_lastOutgoingActivityTime; // last unix timestamp (in milliseconds) when message sending was managed over this entity
+ void updateIncomingActivityTime() throw();
+ void updateOutgoingActivityTime() throw();
+
+ // Private close/destroy method
+ void close(bool destroy) throw(anna::RuntimeException);
+
+ // helpers
+ static const char* asText(const SessionBasedModelsType::_v) throw();
+
+public:
+
+
+ /**
+ * Default constructor. Entities should be create through diameter::comm::Engine
+ * @param maxServers Maximum number of servers managed by the entity. Default is 2 (standard/dual entity).
+ * The value -1, means no limit to add servers.
+ */
+ Entity(int maxServers = 2) : a_maxServers(maxServers) { initialize(); }
+
+
+ /**
+ * Sets the maximum number of servers managed by the entity.
+ *
+ * @param maxServers Maximum number of servers managed by the entity.
+ */
+ void setMaxServers(int maxServers) throw() { a_maxServers = maxServers; }
+
+ /**
+ * Gets the maximum number of servers managed by the entity.
+ *
+ * @return Maximum number of servers managed by the entity.
+ */
+ int getMaxServers() const throw() { return a_maxServers; }
+
+
+ /**
+ * Sets the entity description.
+ *
+ * @param description Entity description. Empty by default.
+ */
+ void setDescription(const std::string &description) throw() { a_description = description; }
+
+ /**
+ * Gets the entity description.
+ *
+ * @return Entity description.
+ */
+ const std::string & getDescription() const throw() { return a_description; }
+
+
+ /**
+ * Sets the diameter::comm::Engine
+ * @param e Diameter::comm::Engine
+ */
+ void setEngine(Engine *e) throw() { a_engine = e; }
+
+ /**
+ * Gets the diameter::comm::Engine
+ * @return Diameter::comm::Engine
+ */
+ Engine *getEngine() const throw() { return a_engine; }
+
+ /**
+ * Add a server to the entity and create all the servers configured at #setSocketsPerDiameterServer within that server.
+ *
+ * \param serverId Diameter server ip/port.
+ *
+ * @return Returns success on send operation over any server within the entity
+ */
+ void addServer(const socket_t & serverId) throw(anna::RuntimeException);
+
+ /**
+ * Binds entity servers.
+ *
+ * @return Returns true if all client-session were successfully bound
+ */
+ bool bind() throw(anna::RuntimeException);
+
+ /**
+ * Propagate auto recovery configuration to servers within entity
+ *
+ * @param autoRecovery Auto recovery indicator. True by default.
+ */
+ void raiseAutoRecovery(bool autoRecovery = true) throw(anna::RuntimeException);
+
+ /**
+ Sets timeout for wait responses for any class code request for all entity servers
+
+ \param v Diameter request type.
+ \param millisecond Milliseconds for timeout
+ */
+ void setClassCodeTimeout(const ClassCode::_v v, const anna::Millisecond & millisecond) throw();
+
+ /**
+ Sets entity category. Used by application in order to categorize or clasify.
+
+ @param category Entity category
+ */
+ void setCategory(int category) throw() { a_category = category; }
+
+ /**
+ Gets entity category. Used by application in order to categorize or clasify.
+
+ @return Entity category
+ */
+ int getCategory() const throw() { return a_category; }
+
+ /**
+ Gets the last used resource (server) during sending.
+ Broadcast doesn't updates this information.
+ */
+ Server *getLastUsedResource() const throw() { return (a_lastUsedResource); }
+
+
+ /**
+ * Balance over entity servers or use standard behaviour (first primary, secondary if fails, etc.).
+ * New created entities have balance disabled.
+ *
+ * @param balance True or false to enable or disable.
+ */
+ void setBalance (bool balance) throw() { a_balance = balance; }
+
+ /**
+ * Gets the balance mode
+ *
+ * @return True if balance over entity servers is enabled, false if standard behaviour is configured (default).
+ */
+ bool getBalance() const throw() { return a_balance; }
+
+
+// SIMPLE BALANCE or STANDARD documentation version
+// /**
+// Sent a message to the entity. First uses primary server, secondary if fails and so on to the
+// last defined resource (server) within entity. Another sending algorithm (non standard) could
+// be enabled (balance boolean parameter): it consist in round-robin server selection without
+// trying any one if fails (standard behaviour tries all servers from FIRST defined).
+// Anyway, last used delivery resource could be known through #getLastUsedResource().
+//
+// When the message is a request, a timer will be set automatically to control the response time.
+// If expires, the ResultCode Timeout will be finally notified on #Entity::eventResponse. This
+// timeout value will be configured at #setClassCodeTimeout.
+//
+// \param message Message sent.
+// \param balance False by default (standard beaviour), but useful to balance over servers within entity.
+//
+// @return Boolean about success in send operation. Standard behaviour (no balance) implies true result when any
+// of the entity servers could send the message, and false when neither of the servers was available or fail to
+// send the message. Broadcast try to send all over the resources in spite of any fail, and balanced sendings
+// fails when next selected resource fail to send the message (and no alarm or error counter is generated in
+// this case because it can't be understood/ensured as entity-sending fail.
+// */
+
+// OJO: en el caso estandard, no se prueban todas las sessiones de un servidor si tiene mas de una, luego la alarma
+// generada en caso de error, presupone que las sessiones no usadas, tambi�n dar�an error, lo cual no tiene porque
+// ser cierto. En condiciones normales, los servidores tienen una session, con lo que lo anterior es cierto y el
+// la practica es lo mas normal.
+
+ /**
+ Sent a message to the entity. First uses primary server, secondary if fails and so on to the
+ last defined resource (server) within entity. Another sending algorithm (non standard) could
+ be enabled (@setBalance): it consist in round-robin server selection to set the first resource
+ in a complete cycle (standard behaviour tries all servers from FIRST defined).
+ Anyway, last used delivery resource could be known through #getLastUsedResource().
+
+ When the message is a request, a timer will be set automatically to control the response time.
+ If expires, the ResultCode Timeout will be finally notified on #Entity::eventResponse. This
+ timeout value will be configured at #setClassCodeTimeout.
+
+ \param message Message sent.
+
+ @return Boolean about success in send operation. Implies true result when any of the entity servers could
+ send the message, and false when neither of the servers was available or fail to send the message (an alarm
+ and error counter will be generated in this case). Broadcast try to send all over the resources in spite of
+ any fail.
+ */
+ bool send(const Message *message) throw(anna::RuntimeException);
+ bool send(const Message &message) throw(anna::RuntimeException) { return send(&message); }
+
+ /**
+ * Sets sessions based models type.
+ * \param sessionBasedModelsType Session based models type: RoundRobin, SessionIdLowPart (default), SessionIdHighPart or SessionIdOptionalPart.
+ */
+ void setSessionBasedModelsType(const SessionBasedModelsType::_v sessionBasedModelsType) throw() { a_sessionBasedModelsType = sessionBasedModelsType; }
+
+ /**
+ * Returns sessions based models type.
+ * \return Session based models type: RoundRobin, SessionIdLowPart, SessionIdHighPart or SessionIdOptionalPart.
+ */
+ SessionBasedModelsType::_v getSessionBasedModelsType() const throw() { return a_sessionBasedModelsType; }
+
+
+ /**
+ Before sending a message over each entity server, socketId could be specified to select
+ which client session within such server will manage the message.
+
+ Default implementation has been focused on charging applications but any other kind of
+ application could re-implement this method and change the behaviour:
+
+ <pre>
+ Charging involves two peers:
+
+ Charging Trigger Function (CTF): Makes decisions on how to charge the user for specific
+ services, issues requests to the server (OCF).
+ Online Charging Function (OCF): Performs actual charging based on received message type,
+ service logic and user profile information.
+
+ There are three main scenarios:
+
+ Immediate Event Charging (IEC): The CTF sends a one time request. This request contains
+ a predefined set of AVPs indicating which service has
+ been activated or how many units have been consumed
+ (this depends on application logic).
+
+ Event Charging with Unit Reservation (ECUR): The CTF issues a request which indicates the
+ desired unit count for reservation. On service
+ delivery, the CTF issues another request which
+ indicates how many units were actually consumed.
+ Units can be of any type, as they are application
+ specific.
+
+ Session Charging with Unit Reservation(SCUR): As above, however reservation can happen more than once.
+
+ In order to minimize race conditions within trasactions, default implementation will select the connection
+ fixed by an stickiness value (Session-Id will be used) for ECUR and SCUR models (Data, Voice and Content
+ traffic). IEC model, represented by SMS ans MMS traffic will use round-robin between sockets. The type
+ of traffic will be analyzed by mean Service-Context-Id AVP. If traffic type is not reconized, value of
+ '-1' will be returned doing round-robin socket selection within active server.
+
+ Diameter session-oriented load balancing enables to distribute Diameter signaling traffic across
+ multiple client sessions for the selected entity server. Application could re-implement different
+ load balancing policies, including support for session stickiness and business decisions, and
+ subscriber and service aware contextual load balancing strategies. By default, Session-Id avp
+ is used to select the resource. Session-Id is split into 4 sections: diameter identity, high
+ part, low part and optional part. Default implementation analizes 'low' part, returning its
+ value as reference for socket selection. Use @setSessionBasedModelsType to change this behaviour.
+ </pre>
+
+ When server is configured as single client session (max client sessions equal to 1), entity will ignore
+ this method being more efficient, because former algorithms would not affect the session selection.
+
+ \param message Message which is being sent.
+ \param maxClientSessions Number of client-sessions on specific server prepared for send.
+
+ @return Socket-id used within range [0,maxClientSessions-1]. Value '-1' if round-robin is desired.
+ If socket-id is return out of range, send procedure will throw an exception.
+ */
+ virtual int readSocketId(const Message *message, int maxClientSessions) const throw();
+
+
+ /**
+ Sent a message to all the entity servers.
+ It is used, i.e., in Disconnect-Peer-Request procedure over a certain entity.
+
+ \param message Message which is being sent.
+
+ @return Returns true (success) only when broadcast is success over all the entity servers. If any server fails,
+ then false is returned.
+ */
+ bool broadcast(const Message *message) throw(anna::RuntimeException);
+ bool broadcast(const Message &message) throw(anna::RuntimeException) { return broadcast(&message); }
+
+
+ /**
+ Returns true when any of the entity servers is Bound. False when all not-bound.
+ */
+ bool isAvailable() const throw() { return a_available; }
+
+ /**
+ Returns true when the entity has been selected as deprecated
+ */
+ bool isDeprecated() const throw() { return a_deprecated; }
+
+ /**
+ Sets the entity deprecated state
+ */
+ void setDeprecated(bool deprecated = true) throw() { a_deprecated = deprecated; }
+
+
+ /**
+ Gets the number of requests messages over-the-air.
+
+ @return OTA messages.
+ */
+ int getOTARequests() const throw();
+
+ /**
+ Returns idle state (no pending answers).
+
+ @return Idle state.
+ */
+ bool idle() const throw() { return (getOTARequests() == 0); }
+
+
+ std::vector<Server*>::iterator begin() throw() { return a_servers.begin(); }
+ std::vector<Server*>::iterator end() throw() { return a_servers.end(); }
+ std::vector<Server*>::const_iterator begin() const throw() { return a_servers.begin(); }
+ std::vector<Server*>::const_iterator end() const throw() { return a_servers.end(); }
+
+ /**
+ Close all the entity servers (close client-sessions within them). Depending on client-session configuration ('OnDisconnect' behaviour),
+ pending answers will be wait (graceful) or ignored (immediate-abrupt close).
+ Resources are not destroyed.
+ */
+ void close() throw(anna::RuntimeException) { close(false /* no destroy */); }
+
+ // helpers
+
+ /**
+ Number of currently configured servers
+ */
+ int getNumberOfServers() const throw() { return a_servers.size(); }
+
+ /**
+ Number of maximum allowed servers
+ */
+ int getMaxServerss() const throw() { return a_maxServers; }
+
+ /**
+ List of (address,port) pairs defining entity servers
+ */
+ socket_v getAddressPortList() const throw();
+
+
+ /**
+ Deny resources for delivery restriction.
+ Deny all its servers
+ */
+ void hide() throw();
+
+ /**
+ Allow resource for delivery permission.
+ Allow all its servers
+ */
+ void show() throw();
+
+ /**
+ Returns true when all its servers resources are hidden for application messages delivery
+ */
+ bool hidden() const throw();
+
+ /**
+ Returns true when all its servers resources are shown for application messages delivery
+ */
+ bool shown() const throw();
+
+
+ /**
+ Class string representation
+ \return String with relevant information for this instance.
+ */
+ std::string asString() const throw();
+
+ /**
+ Class xml representation
+ \param parent Parent XML node on which hold this instance information.
+ \return XML document with relevant information for this instance.
+ */
+ anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+
+protected:
+
+ /**
+ Handler about event break connection from diameter server (server) over this entity.
+ When notified, ANNA.diameter.comm generates an diameter::comm::Entity::eventResponse for every request with pending answers.
+ Default implementation traces warning event
+ \param clientSession ClientSession from which shutdown has been received
+ */
+ virtual void eventPeerShutdown(const ClientSession* clientSession) throw();
+
+ /**
+ Handler about a request retransmission over the session.
+ Default implementation traces warning event
+ \param clientSession ClientSession from which retransmission happened
+ \param request Retransmitted request message
+ */
+ virtual void eventRequestRetransmission(const ClientSession* clientSession, Message *request) throw();
+
+ /**
+ Handler for diameter server (server) responses
+
+ \param response Answer container object for corresponding diameter request
+ */
+ virtual void eventResponse(const Response& response) throw(anna::RuntimeException) = 0;
+
+ /**
+ Handler for diameter server (server) requests
+
+ \param clientSession ClientSession from which request has been received
+ \param request Diameter request message received
+ */
+ virtual void eventRequest(ClientSession* clientSession, const anna::DataBlock &request) throw(anna::RuntimeException) = 0;
+ //virtual void eventRequest(ClientSession* clientSession, const Message& request) throw(anna::RuntimeException) = 0;
+
+ /**
+ Handler for diameter session responses out of context
+
+ \param clientSession ClientSession from which request has been received
+ \param response Answer data block object without context match
+ */
+ virtual void eventUnknownResponse(ClientSession *clientSession, const anna::DataBlock& response) throw(anna::RuntimeException) = 0;
+
+ /**
+ Handler for diameter session Disconnect-Peer-Answer messages
+
+ \param clientSession ClientSession from which request has been received
+ \param response Answer data block object without context match
+ */
+ virtual void eventDPA(ClientSession *clientSession, const anna::DataBlock& response) throw(anna::RuntimeException) = 0;
+
+
+
+
+ friend class Engine;
+ friend class Server;
+};
+
+}
+}
+}
+
+#endif
+