*.old
.cproject
.project
+/Debug/
#include <anna/xml/xml.hpp>
#include <anna/diameter/stack/Engine.hpp>
#include <anna/diameter/codec/Engine.hpp>
+#include <anna/diameter/codec/EngineManager.hpp>
#include <anna/diameter/codec/Message.hpp>
//#include <anna/diameter/codec/functions.hpp> // ApplicationId anna::diameter::codec::functions::getApplicationId(const anna::DataBlock &) throw(anna::RuntimeException);
using namespace anna;
using namespace anna::diameter;
-anna::diameter::codec::Message *G_codecMsg;
-anna::diameter::codec::Engine *G_codecEngine;
+anna::diameter::codec::Message G_codecMsg;
bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) throw() {
// Get hex string
// Decodes a diameter message coming from a datablock
void decodeDataBlock(const anna::DataBlock &db/*, unsigned int & detectedApplicationId*/) throw() {
try {
- //detectedApplicationId = anna::diameter::codec::functions::getApplicationId(db);
- //G_codecEngine->setDictionary(detectedApplicationId); we enabled this feature in the codec engine: selectStackWithApplicationId(true);
- G_codecMsg->decode(db);
+ G_codecMsg.decode(db);
} catch(RuntimeException &ex) {
_exit(ex.asString());
}
bool processHex = xmlOnly ? false:true;
Logger::setLevel(debug ? Logger::Debug:Logger::Warning);
Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
- G_codecEngine = new anna::diameter::codec::Engine("MyCodecEngine");
- G_codecMsg = G_codecEngine->createMessage();
anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
+ anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+ anna::diameter::codec::Engine *ce;
+ unsigned int appid = 0;
+
// Register stacks:
- bool multistack = false;
try {
anna::Tokenizer stacksTok;
stacksTok.apply(stacks, "#");
if(stackTok.size() == 1) {
if(stacksTok.size() != 1)
throw anna::RuntimeException("Application Id value is mandatory when more than one stack is going to be configured", ANNA_FILE_LOCATION);
-
- anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* no matter */, stack); // the stack is the dictionary
- G_codecEngine->setDictionary(d);
+ anna::diameter::stack::Dictionary *d = stackEngine.createDictionary(appid, stack); // the stack is the dictionary
+ ce = new anna::diameter::codec::Engine("CodecEngineForUniqueStackId_0", d);
+ em.registerCodecEngine(0, ce);
break;
}
if(stackTok.size() != 2)
throw anna::RuntimeException("Each stack must be in the form '<application-id>,<xml dictionary pathfile>'", ANNA_FILE_LOCATION);
- multistack = true;
stack_it = stackTok.begin();
unsigned int stackId = atoll(anna::Tokenizer::data(stack_it));
stack_it++;
std::string file = anna::Tokenizer::data(stack_it);
- anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(stackId, file);
+ anna::diameter::stack::Dictionary *d = stackEngine.createDictionary(stackId, file);
+ std::string codecEngineName = anna::functions::asString("CodecEngineForStackId_%llu", stackId);
+ ce = new anna::diameter::codec::Engine(codecEngineName.c_str(), d);
+ em.registerCodecEngine(stackId, ce);
}
- // Auto stack selection based on Application-ID:
- if (multistack) G_codecEngine->selectStackWithApplicationId(true);
-
std::cout << "Stacks provided: " << std::endl;
std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
std::cout << std::endl;
}
// Validation kindness
- G_codecEngine->setFixMode(anna::diameter::codec::EngineImpl::FixMode::Never); // we will encode "as is" (because --no-validation is assumed as user desire)
- G_codecEngine->setValidationDepth(anna::diameter::codec::EngineImpl::ValidationDepth::Complete); // complete validation for better reports
- if(no_validation) G_codecEngine->setValidationMode(anna::diameter::codec::EngineImpl::ValidationMode::Never);
- if(ignore_flags) G_codecEngine->ignoreFlagsOnValidation(true);
+ for (anna::diameter::codec::appid_codec_engines_it it = em.begin(); it != em.end(); it++) {
+ ce = it->second;
+ ce->setFixMode(anna::diameter::codec::EngineImpl::FixMode::Never); // we will encode "as is" (because --no-validation is assumed as user desire)
+ ce->setValidationDepth(anna::diameter::codec::EngineImpl::ValidationDepth::Complete); // complete validation for better reports
+ if(no_validation) ce->setValidationMode(anna::diameter::codec::EngineImpl::ValidationMode::Never);
+ if(ignore_flags) ce->ignoreFlagsOnValidation(true);
+ }
// Auxiliary variables:
anna::DataBlock db_aux(true);
// Write conversion:
std::string outputFile = entry + ".as.xml";
std::ofstream out(outputFile.c_str(), std::ifstream::out);
- out << G_codecMsg->asXMLString();
+ out << G_codecMsg.asXMLString();
out.close();
anyHexConverted = true;
LOGDEBUG(anna::Logger::debug(entry + " is being converted to hex", ANNA_FILE_LOCATION));
// Load file:
- G_codecMsg->loadXML(entry);
+ G_codecMsg.loadXML(entry);
// Write conversion:
- std::string hexString = anna::functions::asHexString(G_codecMsg->code());
+ std::string hexString = anna::functions::asHexString(G_codecMsg.code());
std::string outputFile = entry + ".as.hex";
std::ofstream out(outputFile.c_str(), std::ifstream::out);
out.write(hexString.c_str(), hexString.size());
// Project
#include <anna/timex/Engine.hpp>
#include <anna/statistics/Engine.hpp>
+#include <anna/diameter/codec/functions.hpp>
#include <anna/diameter/codec/Engine.hpp>
+#include <anna/diameter/codec/EngineManager.hpp>
#include <anna/http/Transport.hpp>
#include <anna/diameter/stack/Engine.hpp>
#include <anna/diameter/helpers/base/functions.hpp>
#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.out"
-
const char *ServicesDTD = "\
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<!-- Diameter services DTD -->\n\
<!ELEMENT services (stack*, node*)>\n\
\n\
<!ELEMENT stack EMPTY>\n\
-<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>\n\
+<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED validationMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED validationDepth (Complete | FirstError) #IMPLIED fixMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED ignoreFlagsOnValidation (yes | no) #IMPLIED>\n\
<!--\n\
Stack record\n\
\n\
- id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
- (in multistack applications, it shall be mandatory respect such association to know the stack used\n\
- for processed messages).\n\
- dictionary: Path to the dictionary file\n\
+ id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
+ (in multistack applications, it shall be mandatory respect such association to know the stack used\n\
+ for processed messages).\n\
+ dictionary: Path to the dictionary file.\n\
+ validationMode: Sets the validation mode. Default is 'AfterDecoding'.\n\
+ validationDepth: Sets the validation depth. Default is 'FirstError'.\n\
+ fixMode: Sets the fix mode. Default is 'BeforeEncoding'.\n\
+ ignoreFlagsOnValidation: Ignore flags during message validation. Default is 'no'.\n\
-->\n\
\n\
<!ELEMENT node EMPTY>\n\
-<!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
+<!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
<!--\n\
Node record\n\
\n\
originRealm: Node identifier (Origin-Realm name).\n\
+ applicationId: The Application-Id provided must exists as a registered 'stack id'.\n\
originHost: Diameter application host name (system name). If missing, process sets o.s. hostname\n\
Note that if you have two or more realms, the names must be different.\n\
cer: User defined CER path file to be encoded to establish diameter connections.\n\
Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) {
- a_codecEngine = new anna::diameter::codec::Engine("MyCodecEngine");
- a_baseProtocolDictionary = NULL;
a_timeEngine = NULL;
a_counterRecorder = NULL;
a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second;
void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
//<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
- const anna::xml::Attribute *id, *dictionary;
+ const anna::xml::Attribute *id, *dictionary;
- // <!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
- const anna::xml::Attribute *originRealm, *originHost, *cer, *dwr, *allowedInactivityTime, *tcpConnectDelay,
- *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
- *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
- *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
+ // <!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
+ const anna::xml::Attribute *originRealm, *appId, *originHost, *cer, *dwr, *allowedInactivityTime, *tcpConnectDelay,
+ *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
+ *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
+ *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
// Never clear services content from here (append new data from xml). At the moment no node removing is implemented in this process
// Stacks
anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
anna::diameter::stack::Dictionary *d;
+ const anna::diameter::stack::Dictionary *bpd = NULL; // base protocol dictionary
+
+ // Codec engine manager:
+ anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+ anna::diameter::codec::Engine *ce;
+
///////////////////////////////////////////
// APPLICATION MESSAGE OAM MODULE SCOPES //
///////////////////////////////////////////
static int scope_id = 3;
bool id_0_registered = false;
unsigned int id_value;
+ std::string codecEngineName;
for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
std::string nodeName = (*it)->getName();
try {
d = stackEngine.createDictionary(id_value, dictionary->getValue());
- getCodecEngine()->setDictionary(d);
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("Created dictionary (%p) for stack id %llu", d, id_value), ANNA_FILE_LOCATION));
// OAM module for counters:
appMsgOamModule.createStackCounterScope(scope_id, id_value /* application-id */);
throw ex;
}
- if (id_value == 0)
+ if (id_value == 0) {
id_0_registered = true;
- a_baseProtocolDictionary = d;
+ bpd = d;
+ }
+
+ // Create codec engine and register it in the codec engine manager:
+ codecEngineName = anna::functions::asString("CodecEngineForStackId_%llu", id_value);
+ ce = new anna::diameter::codec::Engine(codecEngineName.c_str(), d);
+ em.registerCodecEngine(id_value, ce);
+
+ // Codec engine configuration:
+ const anna::xml::Attribute *vm_attr = (*it)->getAttribute("validationMode", false /* no exception */);
+ const anna::xml::Attribute *vd_attr = (*it)->getAttribute("validationDepth", false /* no exception */);
+ const anna::xml::Attribute *fm_attr = (*it)->getAttribute("fixMode", false /* no exception */);
+ const anna::xml::Attribute *if_attr = (*it)->getAttribute("ignoreFlags", false /* no exception */);
+
+ std::string vm_value = vm_attr ? vm_attr->getValue() : "AfterDecoding";
+ std::string vd_value = vd_attr ? vd_attr->getValue() : "FirstError";
+ std::string fm_value = fm_attr ? fm_attr->getValue() : "BeforeEncoding";
+
+ anna::diameter::codec::Engine::ValidationMode::_v vm;
+ if (vm_value == "BeforeEncoding") vm = anna::diameter::codec::Engine::ValidationMode::BeforeEncoding;
+ else if (vm_value == "AfterDecoding") vm = anna::diameter::codec::Engine::ValidationMode::AfterDecoding;
+ else if (vm_value == "Always") vm = anna::diameter::codec::Engine::ValidationMode::Always;
+ else if (vm_value == "Never") vm = anna::diameter::codec::Engine::ValidationMode::Never;
+ ce->setValidationMode(vm);
+
+ anna::diameter::codec::Engine::ValidationDepth::_v vd;
+ if (vd_value == "Complete") vd = anna::diameter::codec::Engine::ValidationDepth::Complete;
+ else if (vd_value == "FirstError") vd = anna::diameter::codec::Engine::ValidationDepth::FirstError;
+ ce->setValidationDepth(vd);
+
+ anna::diameter::codec::Engine::FixMode::_v fm;
+ if (fm_value == "BeforeEncoding") fm = anna::diameter::codec::Engine::FixMode::BeforeEncoding;
+ else if (fm_value == "AfterDecoding") fm = anna::diameter::codec::Engine::FixMode::AfterDecoding;
+ else if (fm_value == "Always") fm = anna::diameter::codec::Engine::FixMode::Always;
+ else if (fm_value == "Never") fm = anna::diameter::codec::Engine::FixMode::Never;
+ ce->setFixMode(fm);
+
+ bool if_value = (if_attr ? (if_attr->getValue() == "yes") : false);
+ ce->ignoreFlagsOnValidation(if_value);
}
}
std::cout << "Stacks currently loaded:" << std::endl;
std::cout << anna::functions::tab(stackEngine.asString(false /* light */)) << std::endl;
-
- // Codec engine adjustments:
- // Auto stack selection based on Application-ID:
+ // Basic checking for multistack:
bool multistack = (stackEngine.stack_size() > 1);
if (multistack) {
- getCodecEngine()->selectStackWithApplicationId(true);
- // In multistack, id = 0 MUST be registered:
- if (!id_0_registered)
+ if(!id_0_registered)
throw anna::RuntimeException("In multistack applications is mandatory register a stack id = 0 using a dictionary which contains the needed elements to build base protocol messages (CER/A, DWR/A, DPR/A, STR/A, etc.)", ANNA_FILE_LOCATION);
}
- else {
- a_baseProtocolDictionary = d;
+ else { // monostack
+ if (!bpd)
+ bpd = ce->getDictionary(); // in mono-stack applications, we understand the existing stack as the used
+ // for base protocol, regardless if it is registered with stack id 0 or not
}
+ // REALMS:
for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
std::string nodeName = (*it)->getName();
if(nodeName == "node") {
// Input data:
originRealm = (*it)->getAttribute("originRealm");
+ appId = (*it)->getAttribute("applicationId");
originHost = (*it)->getAttribute("originHost", false /* no exception */);
cer = (*it)->getAttribute("cer", false /* no exception */);
dwr = (*it)->getAttribute("dwr", false /* no exception */);
throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
}
+ unsigned int applicationId = appId->getIntegerValue();
+ if (!stackEngine.getDictionary(applicationId)) {
+ std::string msg = "Cannot found a registered stack id with the value of applicationId provided: "; msg += appId->getValue();
+ throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
// Engine time measures checking & assignment:
anna::Millisecond allowedInactivityTimeMs(90000);
anna::Millisecond tcpConnectDelayMs(200);
}
// Create new Node instance /////////////////////////////////////////////////////////////////
- a_workingNode = new RealmNode(originRealm->getValue(), a_codecEngine, a_baseProtocolDictionary);
+ a_workingNode = new RealmNode(originRealm->getValue(), applicationId, bpd);
MyDiameterEngine *commEngine = a_workingNode->getMyDiameterEngine();
/////////////////////////////////////////////////////////////////////////////////////////////
// Lazy initialization for comm engine:
if (eventOperation) commEngine->lazyInitialize();
- // New Node assignment //////////////////////////////////////////////////////////////////////
+ // Node and Codec Engine registration ///////////////////////////////////////////////////////
a_nodes[originRealm->getValue()] = a_workingNode;
/////////////////////////////////////////////////////////////////////////////////////////////
}
}
+ if (!uniqueRealm())
+ a_workingNode = NULL; // by default, mode auto
+
// Diameter comm engines which are loaded after application start (via management operation 'services') are not really started,
// but this don't care because application startComponents() -> initialize() -> do_initialize() -> do nothing.
// And when stopped, running state is not checked and it will be stopped anyway.
}
LOGDEBUG(
- std::string trace = "Loading ADML services file '";
- trace += xmlPathFile;
- trace += "'";
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ std::string trace = "Loading ADML services file '";
+ trace += xmlPathFile;
+ trace += "'";
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
);
anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
anna::xml::DTDMemory xmlDTD;
}
catch (anna::RuntimeException &ex) {
LOGWARNING(
- std::string msg = "Services DTD schema:\n\n";
- msg += ServicesDTD;
- anna::Logger::warning(msg, ANNA_FILE_LOCATION);
+ std::string msg = "Services DTD schema:\n\n";
+ msg += ServicesDTD;
+ anna::Logger::warning(msg, ANNA_FILE_LOCATION);
);
throw ex;
}
LOGDEBUG(
- std::string trace = "Loaded XML file (";
- trace += xmlPathFile;
- trace += "):\n";
- trace += anna::xml::Compiler().apply(rootNode);
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ std::string trace = "Loaded XML file (";
+ trace += xmlPathFile;
+ trace += "):\n";
+ trace += anna::xml::Compiler().apply(rootNode);
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
);
servicesFromXML(rootNode, eventOperation);
}
throw RuntimeException(msg, ANNA_FILE_LOCATION);
}
-RealmNode *Launcher::getWorkingNode() const throw(anna::RuntimeException) {
-
- if (!a_workingNode)
- throw RuntimeException("No services yet loaded. Try 'services' operation (via management interface), or restart process using command-line 'services' parameter", ANNA_FILE_LOCATION);
-
- return a_workingNode;
-}
-
bool Launcher::setWorkingNode(const std::string &name) throw() {
bool result = false;
- realm_nodes_nc_it nodeIt = a_nodes.find(name);
+ realm_nodes_it nodeIt = a_nodes.find(name);
if (nodeIt == a_nodes.end()) {
LOGWARNING(
- std::string msg = "Unknown node with name '"; msg += name; msg += "'. Ignoring ...";
- anna::Logger::warning(msg, ANNA_FILE_LOCATION);
+ std::string msg = "Unknown node with name '"; msg += name; msg += "'. Ignoring ...";
+ anna::Logger::warning(msg, ANNA_FILE_LOCATION);
);
}
else {
- a_workingNode = nodeIt->second;
+ a_workingNode = const_cast<RealmNode*>(nodeIt->second);
result = true;
}
return result;
}
-RealmNode *Launcher::getRealmNode(const std::string &realmName) const throw() {
+RealmNode *Launcher::getRealmNode(const std::string &realmName) const throw(anna::RuntimeException) {
realm_nodes_it it = a_nodes.find(realmName);
if (it != a_nodes.end()) return it->second;
+ throw anna::RuntimeException(anna::functions::asString("There is no realm node registered as '%s' (set Origin-Realm avp correctly or force a specific realm with 'node' operation)", realmName.c_str()), ANNA_FILE_LOCATION);
+}
+
+RealmNode *Launcher::getRealmNode(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException) {
+ std::string originRealm = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
+ return (getRealmNode(originRealm));
+}
- return NULL; // this never happens
+void Launcher::updateOperatedRealmNodeWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException) {
+ if (!a_operatedRealm) // priority for working node by mean 'node' operation
+ a_operatedRealm = getRealmNode(message);
+}
+
+RealmNode *Launcher::getOperatedRealm() const throw(anna::RuntimeException) {
+ if(!a_operatedRealm)
+ throw anna::RuntimeException("Realm Node not identified (try to force a specific realm with 'node' operation)", ANNA_FILE_LOCATION);
+
+ return a_operatedRealm;
+}
+
+MyDiameterEntity *Launcher::getOperatedEntity() const throw(anna::RuntimeException) {
+ MyDiameterEntity *result = getOperatedRealm()->getEntity();
+ if (!result)
+ throw anna::RuntimeException("No entity configured for the operated Realm Node", ANNA_FILE_LOCATION);
+ return result;
+}
+
+MyLocalServer *Launcher::getOperatedServer() const throw(anna::RuntimeException) {
+ MyLocalServer *result = getOperatedRealm()->getDiameterServer();
+ if (!result)
+ throw anna::RuntimeException("No local server configured for the operated Realm Node", ANNA_FILE_LOCATION);
+ return result;
+}
+
+MyDiameterEngine *Launcher::getOperatedEngine() const throw(anna::RuntimeException) {
+ return getOperatedRealm()->getMyDiameterEngine(); // never will be NULL
}
void Launcher::initialize()
anna::statistics::Engine::instantiate().enable();
LOGINFORMATION(
- // Help on startup traces:
- anna::Logger::information(help(), ANNA_FILE_LOCATION);
- // Test messages dtd:
- std::string msg = "\n ------------- TESTMESSAGES DTD -------------\n";
- msg += anna::diameter::codec::MessageDTD;
- anna::Logger::information(msg, ANNA_FILE_LOCATION);
+ // Help on startup traces:
+ anna::Logger::information(help(), ANNA_FILE_LOCATION);
+ // Test messages dtd:
+ std::string msg = "\n ------------- TESTMESSAGES DTD -------------\n";
+ msg += anna::diameter::codec::MessageDTD;
+ anna::Logger::information(msg, ANNA_FILE_LOCATION);
);
// HTTP Server:
a_timeEngine->activate(a_counterRecorderClock); // start clock
}
-
- // Integration (validation 'Complete' for receiving messages) and debugging (validation also before encoding: 'Always').
- // If missing 'integrationAndDebugging', default behaviour at engine is: mode 'AfterDecoding', depth 'FirstError':
- if(cl.exists("integrationAndDebugging")) {
- getCodecEngine()->setValidationMode(anna::diameter::codec::Engine::ValidationMode::Always);
- getCodecEngine()->setValidationDepth(anna::diameter::codec::Engine::ValidationDepth::Complete);
- }
-
- // Fix mode
- if(cl.exists("fixMode")) { // BeforeEncoding(default), AfterDecoding, Always, Never
- std::string fixMode = cl.getValue("fixMode");
- anna::diameter::codec::Engine::FixMode::_v fm;
- if (fixMode == "BeforeEncoding") fm = anna::diameter::codec::Engine::FixMode::BeforeEncoding;
- else if (fixMode == "AfterDecoding") fm = anna::diameter::codec::Engine::FixMode::AfterDecoding;
- else if (fixMode == "Always") fm = anna::diameter::codec::Engine::FixMode::Always;
- else if (fixMode == "Never") fm = anna::diameter::codec::Engine::FixMode::Never;
- else LOGINFORMATION(anna::Logger::information("Unreconized command-line fix mode. Assumed default 'BeforeEncoding'", ANNA_FILE_LOCATION));
- getCodecEngine()->setFixMode(fm);
- }
-
- getCodecEngine()->ignoreFlagsOnValidation(cl.exists("ignoreFlags"));
-
-
// Log statistics concepts
if(cl.exists("logStatisticSamples")) {
std::string list = cl.getValue("logStatisticSamples");
// Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
LOGDEBUG(
- std::string msg = "Hex string (remove colons if exists): ";
- msg += hexString;
- anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+ std::string msg = "Hex string (remove colons if exists): ";
+ msg += hexString;
+ anna::Logger::debug(msg, ANNA_FILE_LOCATION);
);
anna::functions::fromHexString(hexString, db);
// Close file
}
void Launcher::resetStatistics() throw() {
- getWorkingNode()->getMyDiameterEngine()->resetStatistics();
+ if (a_workingNode) {
+ a_workingNode->getMyDiameterEngine()->resetStatistics();
+ }
+ else {
+ for (realm_nodes_it it = a_nodes.begin(); it != a_nodes.end(); it++) {
+ it->second->getMyDiameterEngine()->resetStatistics();
+ }
+ }
}
void Launcher::resetCounters() throw() {
void Launcher::signalUSR2() throw(anna::RuntimeException) {
LOGNOTICE(
- std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
- msg += SIGUSR2_TASKS_INPUT_FILENAME;
- msg += "' (results will be written at '";
- msg += SIGUSR2_TASKS_OUTPUT_FILENAME;
- msg += "')";
- anna::Logger::notice(msg, ANNA_FILE_LOCATION);
+ std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
+ msg += SIGUSR2_TASKS_INPUT_FILENAME;
+ msg += "' (results will be written at '";
+ msg += SIGUSR2_TASKS_OUTPUT_FILENAME;
+ msg += "')";
+ anna::Logger::notice(msg, ANNA_FILE_LOCATION);
);
// Operation:
while(getline(in_file, line)) {
LOGDEBUG(
- std::string msg = "Processing line: ";
- msg += line;
- anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+ std::string msg = "Processing line: ";
+ msg += line;
+ anna::Logger::debug(msg, ANNA_FILE_LOCATION);
);
try {
result += "\n <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
result += "\n";
result += "\n <!-- Nodes -->";
- result += "\n <node originRealm=\"ADML-client\" entity=\"localhost:3868\"/>";
+ result += "\n <node originRealm=\"ADML-client\" applicationId=\"0\" entity=\"localhost:3868\"/>";
result += "\n</services>";
result += "\n";
result += "\nServer configuration:";
result += "\n <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
result += "\n";
result += "\n <!-- Nodes -->";
- result += "\n <node originRealm=\"ADML-server\" diameterServer=\"localhost:3868\"/>";
+ result += "\n <node originRealm=\"ADML-server\" applicationId=\"0\" diameterServer=\"localhost:3868\"/>";
result += "\n</services>";
result += "\n";
result += "\nIf you act as a proxy or a translation agent, you need to combine both former setups, and probably";
result += "\n <stack id=\"0\" dictionary=\"dictionary_base.xml\"/>";
result += "\n";
result += "\n <!-- Nodes -->";
- result += "\n <node originRealm=\"ADML-Rx-client\" entity=\"localhost:3868\" cer=\"cer_Rx.xml\"/>";
- result += "\n <node originRealm=\"ADML-Gx-client\" entity=\"localhost:3868\" cer=\"cer_Gx.xml\"/>";
+ result += "\n <node originRealm=\"ADML-Rx-client\" applicationId=\"16777236\" entity=\"localhost:3868\" cer=\"cer_Rx.xml\"/>";
+ result += "\n <node originRealm=\"ADML-Gx-client\" applicationId=\"16777238\" entity=\"localhost:3868\" cer=\"cer_Gx.xml\"/>";
result += "\n</services>";
result += "\n";
result += "\n";
result += "\n";
result += "\nhelp This help.";
result += "\n";
- result += "\n---------------------------------------------------------------------------------------- Node selection";
- result += "\n";
- result += "\nnode[|<name>] Select current working node by mean the registered name.";
- result += "\n All the subsequent operations will be referred to this node.";
- result += "\n Without argument, the current node information is retrieved.";
+ result += "\n--------------------------------------------------------------------------------------- Node management";
+ result += "\n";
+ result += "\nnode[|<name>] Selects a context working node by mean a registered name.";
+ result += "\n All the subsequent operations will be forced to work with";
+ result += "\n this node, which makes possible some rare scenarios like";
+ result += "\n sending unexpected messages on remote peers. This is also";
+ result += "\n useful for some operations in order to restrict the scope";
+ result += "\n of action (statistics, communication visibility, etc.).";
+ result += "\n Empty parameter will show the current configuration.";
+ result += "\n";
+ result += "\nnode_auto Returns to the default behaviour (smart node selection).";
+ result += "\n Depending on the operation, this could imply a global";
+ result += "\n action scope, affecting to all the registered realms.";
+ result += "\n This should be the normal configuration. Take into";
+ result += "\n account that if you fix the working node, this could";
+ result += "\n affect to things like test programming: communication";
+ result += "\n resources will override those which would be inferred";
+ result += "\n from programmed messages Origin-Realm avps.";
result += "\n";
result += "\n------------------------------------------------------------------------------------ Parsing operations";
result += "\n";
result += "\n default '/var/tmp/anna.context.<pid>' will be used.";
result += "\ncollect Reset statistics and counters to start a new test stage of";
result += "\n performance measurement. Context data can be written at";
- result += "\n '/var/tmp/anna.context.<pid>' by mean 'kill -10 <pid>'";
+ result += "\n '/var/tmp/anna.context.<pid>' by mean 'kill -10 <pid>'.";
result += "\n or sending operation 'context|[target file]'.";
+ result += "\n This operation applies over all the registered realm nodes";
+ result += "\n except if one specific working node has been set.";
result += "\nforceCountersRecord Forces dump to file the current counters of the process.";
result += "\n";
result += "\n<visibility action>[|<address>:<port>][|socket id]";
result += "\n";
result += "\nsendxml2e|<source_file> Sends xml source file (pathfile) through configured entity.";
result += "\nsendxml2c|<source_file> Sends xml source file (pathfile) to client.";
- result += "\nsendxml|<source_file> Same as 'sendxml2e'.";
result += "\nanswerxml2e[|source_file] Answer xml source file (pathfile) for incoming request with same code from entity.";
result += "\n The answer is stored in a FIFO queue for a specific message code, then there are";
result += "\n as many queues as different message codes have been programmed.";
result += "\nanswerxml2c[|source_file] Answer xml source file (pathfile) for incoming request with same code from client.";
result += "\n The answer is stored in a FIFO queue for a specific message code, then there are";
result += "\n as many queues as different message codes have been programmed.";
- result += "\nanswerxml[|source_file] Same as 'answerxml2c'.";
- result += "\nanswerxml(2e/2c) List programmed answers (to entity/client) if no parameter provided.";
- result += "\nanswerxml(2e/2c)|dump Write programmed answers (to entity/client) to file 'programmed_answer.<message code>.<sequence>',";
+ result += "\nanswerxml<2e/2c> List programmed answers (to entity/client) if no parameter provided.";
+ result += "\nanswerxml<2e/2c>|dump Write programmed answers (to entity/client) to file 'programmed_answer.<message code>.<sequence>',";
result += "\n where 'sequence' is the order of the answer in each FIFO code-queue of programmed answers.";
- result += "\nanswerxml(2e/2c)|clear Clear programmed answers (to entity/client).";
- result += "\nanswerxml(2e/2c)|exhaust Disable the corresponding queue rotation, which is the default behaviour.";
- result += "\nanswerxml(2e/2c)|rotate Enable the corresponding queue rotation, useful in performance tests.";
+ result += "\nanswerxml<2e/2c>|clear Clear programmed answers (to entity/client).";
+ result += "\nanswerxml<2e/2c>|exhaust Disable the corresponding queue rotation, which is the default behaviour.";
+ result += "\nanswerxml<2e/2c>|rotate Enable the corresponding queue rotation, useful in performance tests.";
result += "\n Rotation consists in add again to the queue, each element retrieved for answering.";
result += "\n";
result += "\nSend operations are available using hexadecimal content (hex formatted files) which also allow to test";
result += "\n";
result += "\nsendhex2e|<source_file> Sends hex source file (pathfile) through configured entity.";
result += "\nsendhex2c|<source_file> Sends hex source file (pathfile) to client.";
- result += "\nsendhex|<source_file> Same as 'sendhex2e'.";
result += "\n";
result += "\nAnswer programming in hexadecimal is not really neccessary (you could use send primitives) and also";
result += "\n is intended to be used with decoded messages in order to replace things like hop by hop, end to end,";
result += "\n subscriber id, session id, etc. Anyway you could use 'decode' operation and then program the xml created.";
result += "\n";
- result += "\nIf a request is received, answer map (built with 'answerxml<[2c] or 2e>' operations) will be";
+ result += "\nIf a request is received, answer map (built with 'answerxml<2e/2c>' operations) will be";
result += "\n checked to find a corresponding programmed answer to be replied(*). If no ocurrence is found,";
result += "\n or answer message was received, the message is forwarded to the other side (entity or client),";
result += "\n or nothing but trace when no peer at that side is configured. Answer to client have sense when";
result += "\n";
result += "\n(*) sequence values (hop-by-hop and end-to-end), Session-Id and Subscription-Id avps, are mirrored";
result += "\n to the peer which sent the request. If user wants to test a specific answer without changing it,";
- result += "\n use sendxml/sendhex operations better than programming.";
+ result += "\n use sendxml<2e/2c>/sendhex<2e/2c> operations better than programming.";
result += "\n";
result += "\nBalance ('--balance' command line parameter) could be used to forward server socket receptions through";
result += "\n entity servers by mean a round-robin algorithm. Both diameter server socket and entity targets should";
result += "\n";
result += "\n------------------------------------------------------------------------------------------- Burst tests";
result += "\n";
+ result += "\nIn order to simplify user experience, burst category operations are only allowed in single realm node";
+ result += "\n configuration. Indeed, you could send messages with incorrect Origin-Realm, and no warning is shown.";
+ result += "\nAll the operations are performed through the unique realm: if you need to use more interfaces, you may";
+ result += "\n launch different ADML instances. Is nonsense to allow burst in a multi-realm configured ADML, because";
+ result += "\n this feature is not able to coordinate the messages.";
+ result += "\n";
result += "\nburst|<action>[|parameter] Used for performance testing, we first program diameter requests";
result += "\n messages in order to launch them from client side to the configured";
result += "\n diameter entity. We could start the burst with an initial load";
result += "\n Adds a new step to the test case with provided identifier. If provided identifier";
result += "\n is not registered yet, a new test case will be created with that value and the";
result += "\n step will be added as the first. For a specific 'id', the steps are stored in";
- result += "\n order as they are programmed";
+ result += "\n order as they are programmed. Check possible runtime exceptions when adding a";
+ result += "\n new step because those which fail, will be ignored/skipped during test case";
+ result += "\n programming giving an incomplete sequence invalid for the testing purpose.";
result += "\n";
result += "\n <id>: integer number, normally monotonically increased for each test case. Some external";
result += "\n script/procedure shall clone a test case template in order to build a collection";
result += "\n a real unexpected message).";
// TODO(***)
-// result += "\n The way to identify the test case, is through registered Session-Id values for";
-// result += "\n programmed requests. But this depends on the type of node. Acting as clients,";
-// result += "\n requests received have Session-Id values which are already registered with";
-// result += "\n one test case, causing an error if not found. Acting as servers, requests are";
-// result += "\n received over a diameter local server from a client which are generating that";
-// result += "\n Session-Id values. Then we know nothing about such values. The procedure in";
-// result += "\n this case is find out a test case not-started containing a condition which";
-// result += "\n comply with the incoming message, and reactivates it.";
+ // result += "\n The way to identify the test case, is through registered Session-Id values for";
+ // result += "\n programmed requests. But this depends on the type of node. Acting as clients,";
+ // result += "\n requests received have Session-Id values which are already registered with";
+ // result += "\n one test case, causing an error if not found. Acting as servers, requests are";
+ // result += "\n received over a diameter local server from a client which are generating that";
+ // result += "\n Session-Id values. Then we know nothing about such values. The procedure in";
+ // result += "\n this case is find out a test case not-started containing a condition which";
+ // result += "\n comply with the incoming message, and reactivates it.";
// The other solution: register Session-Id values for answers send to client from a local diameter server.
result += "\n How to answer: a wait condition for a request will store the incoming message";
result += "\n";
result += "\n Condition format:";
result += "\n";
- result += "\n [code]|[bitR]|[ResultCode]|[sessionId]|[hopByHop]|[msisdn]|[imsi]|[serviceContextId]";
+ result += "\n [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]";
result += "\n";
result += "\n code: integer number";
result += "\n bitR: 1 (request), 0 (answer)";
- result += "\n ResultCode: integer number";
- result += "\n sessionId: string";
result += "\n hopByHop: integer number or request send step reference: #<step number>";
result += "\n";
result += "\n Using the hash reference, you would indicate a specific wait condition";
result += "\n This 'hop-by-hop' variant eases the wait condition for answers in the";
result += "\n safest way.";
result += "\n";
+ result += "\n applicationId: integer number";
+ result += "\n sessionId: string";
+ result += "\n resultCode: integer number";
result += "\n msisdn: string";
result += "\n imsi: string";
result += "\n serviceContextId: string";
result += "\n";
result += "\n test|1|timeout|5000 (step 1: whole time requirement is 5 seconds)";
result += "\n test|1|sendxml2e|CCR-I.xml (step 2: imagine this xml uses the Session-Id 'SGx')";
- result += "\n test|1|waitfe|272|0|2001|SGx (step 3: waits the CCA for the CCR-I with Result-Code = DIAMETER_SUCCESS)";
+ result += "\n test|1|waitfe|272|0|||SGx|2001 (step 3: waits the CCA for the CCR-I with Result-Code = DIAMETER_SUCCESS)";
result += "\n test|1|sendxml2e|AAR-flows.xml (step 4: imagine this xml uses the Session-Id 'SRx')";
- result += "\n test|1|waitfe|265|0|2001|SRx (step 5: waits the AAA for the AAR-flows with Result-Code = DIAMETER_SUCCESS)";
- result += "\n test|1|waitfe|258|1||SGx (step 6: waits the RAR (install policies) from the PCRF server)";
+ result += "\n test|1|waitfe|265|0|||SRx|2001 (step 5: waits the AAA for the AAR-flows with Result-Code = DIAMETER_SUCCESS)";
+ result += "\n test|1|waitfe|258|1|||SGx (step 6: waits the RAR (install policies) from the PCRF server)";
result += "\n test|1|sendxml2e|RAA-install.xml|6 (step 7: sends the response for the RAR)";
result += "\n test|1|sendxml2e|CCR-T.xml (step 8: termination of the Gx session, imagine this xml puts hop-by-hop 'H1')";
- result += "\n test|1|waitfe|272|0|2001|SGx|H1 (step 9: waits the CCA for the CCR-T with Result-Code = DIAMETER_SUCCESS and hop-by-hop 'H1')";
- result += "\n test|1|waitfe|258|1||SGx (step 10: waits the RAR (remove policies) from the PCRF server)";
+ result += "\n test|1|waitfe|272|0|H1||SGx|2001 (step 9: waits the CCA for the CCR-T with Result-Code = DIAMETER_SUCCESS and hop-by-hop 'H1')";
+ result += "\n test|1|waitfe|258|1|||SGx (step 10: waits the RAR (remove policies) from the PCRF server)";
result += "\n test|1|sendxml2e|RAA-remove.xml|10 (step 11: sends the response for the RAR)";
result += "\n";
result += "\n Notes: We added an additional condition in step 9: the hop-by-hop. When we program the corresponding";
result += "\n";
result += "\n Other simplifications: the steps 3, 5 and 9 can be replaced by";
result += "\n";
- result += "\n test|1|waitfe||0|||#2";
- result += "\n test|1|waitfe||0|||#4";
- result += "\n test|1|waitfe||0|||#8";
+ result += "\n test|1|waitfe||0|#2";
+ result += "\n test|1|waitfe||0|#4";
+ result += "\n test|1|waitfe||0|#8";
result += "\n";
result += "\n which means that hop-by-hop must be retrieved from steps 2, 4 and 8 respectively,";
result += "\n and the expected message shall be an answer. Normally you will add other conditions,";
void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
if (operation == "") return; // ignore
-
- CommandLine& cl(anna::CommandLine::instantiate());
- TestManager &testManager = TestManager::instantiate();
LOGDEBUG(anna::Logger::debug(operation, ANNA_FILE_LOCATION));
// Default response:
response_content = "Operation processed with exception (see traces): ";
response_content += operation;
-
-
std::string opt_response_content = ""; // aditional response content
anna::DataBlock db_aux(true);
+ anna::diameter::codec::Message codecMsg; // auxiliary codec message
+
+ // Singletons:
+ CommandLine& cl(anna::CommandLine::instantiate());
+ TestManager &testManager = TestManager::instantiate();
+
///////////////////////////////////////////////////////////////////
// Simple operations without arguments:
// Get the operation type and parameters:
Tokenizer::const_iterator tok_iter = params.begin();
std::string opType = Tokenizer::data(tok_iter);
- std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10;
+ std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11;
if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); }
if(numParams >= 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); }
if(numParams >= 3) { tok_iter++; param3 = Tokenizer::data(tok_iter); }
if(numParams >= 8) { tok_iter++; param8 = Tokenizer::data(tok_iter); }
if(numParams >= 9) { tok_iter++; param9 = Tokenizer::data(tok_iter); }
if(numParams >= 10) { tok_iter++; param10 = Tokenizer::data(tok_iter); }
+ if(numParams >= 11) { tok_iter++; param11 = Tokenizer::data(tok_iter); }
// Remove '<null>' artificial token to ease further checkings:
if (param1 == "<null>") param1 = "";
if (param2 == "<null>") param2 = "";
if (param8 == "<null>") param8 = "";
if (param9 == "<null>") param9 = "";
if (param10 == "<null>") param10 = "";
+ if (param11 == "<null>") param11 = "";
// No operation has more than 2 arguments except 'test' ...
if(opType != "test" && numParams > 2)
bool wrongBody = false;
if((opType == "node") && (numParams > 1)) wrongBody = true;
+ if((opType == "node_auto") && (numParams > 0)) wrongBody = true;
if(((opType == "code") || (opType == "decode")) && (numParams != 2)) wrongBody = true;
- if(((opType == "sendxml") || (opType == "sendxml2e") || (opType == "sendhex") || (opType == "sendhex2e")) && (numParams != 1)) wrongBody = true;
+ if(((opType == "sendxml2e") || (opType == "sendhex2e")) && (numParams != 1)) wrongBody = true;
if((opType == "burst") && (numParams < 1)) wrongBody = true;
// Realm switch:
if(opType == "node") {
if (param1 != "") {
- if (setWorkingNode(param1)) response_content = anna::functions::asString("Current node is now '%s'", param1.c_str());
+ if (setWorkingNode(param1)) response_content = anna::functions::asString("Forced node is now '%s'", param1.c_str());
}
else {
- response_content = getWorkingNode()->asXMLString();
+ if (a_workingNode) {
+ response_content = "Working node is forced to be: \n\n";
+ response_content += a_workingNode->asXMLString();
+ }
+ else {
+ response_content = "Working node is automatic";
+ }
}
return;
}
+ if(opType == "node_auto") {
+ a_workingNode = NULL;
+ response_content = "Working node has been set to automatic";
+ return;
+ }
- // Diameter endpoints:
- MyDiameterEntity *entity = getWorkingNode()->getEntity();
- MyDiameterEngine *commEngine = getWorkingNode()->getMyDiameterEngine();
- MyLocalServer *localServer = getWorkingNode()->getDiameterServer();
- anna::diameter::codec::Message codecMsg(getCodecEngine());
+ // Operated realm from possible forced-working node:
+ a_operatedRealm = a_workingNode ? a_workingNode /* priority */: NULL /* auto */;
+ // Use later:
+ // If any message is managed: updateOperatedRealmNodeWithMessage(codecMessage)
+ // To operate, use the exception-protected methods which never will return NULL:
+ // getOperatedRealm(), getOperatedEntity(), getOperatedServer(), getOperatedEngine()
if(opType == "code") {
outfile.write(xmlString.c_str(), xmlString.size());
outfile.close();
} else if((opType == "hide") || (opType == "show") || (opType == "hidden") || (opType == "shown")) {
- if(!entity) throw anna::RuntimeException("No entity configured to send messages", ANNA_FILE_LOCATION);
if(param1 != "") {
if(param2 != "") {
key += "|";
key += param2;
- if(opType == "hide") commEngine->findClientSession(key)->hide();
+ if(opType == "hide") getOperatedEngine()->findClientSession(key)->hide();
- if(opType == "show") commEngine->findClientSession(key)->show();
+ if(opType == "show") getOperatedEngine()->findClientSession(key)->show();
- if(opType == "hidden") opt_response_content = commEngine->findClientSession(key)->hidden() ? "true" : "false";
+ if(opType == "hidden") opt_response_content = getOperatedEngine()->findClientSession(key)->hidden() ? "true" : "false";
- if(opType == "shown") opt_response_content = commEngine->findClientSession(key)->shown() ? "true" : "false";
+ if(opType == "shown") opt_response_content = getOperatedEngine()->findClientSession(key)->shown() ? "true" : "false";
} else {
std::string address;
int port;
anna::functions::getAddressAndPortFromSocketLiteral(param1, address, port);
- if(opType == "hide") commEngine->findServer(address, port)->hide();
+ if(opType == "hide") getOperatedEngine()->findServer(address, port)->hide();
- if(opType == "show") commEngine->findServer(address, port)->show();
+ if(opType == "show") getOperatedEngine()->findServer(address, port)->show();
- if(opType == "hidden") opt_response_content = commEngine->findServer(address, port)->hidden() ? "true" : "false";
+ if(opType == "hidden") opt_response_content = getOperatedEngine()->findServer(address, port)->hidden() ? "true" : "false";
- if(opType == "shown") opt_response_content = commEngine->findServer(address, port)->shown() ? "true" : "false";
+ if(opType == "shown") opt_response_content = getOperatedEngine()->findServer(address, port)->shown() ? "true" : "false";
}
} else {
- if(opType == "hide") entity->hide();
+ if(opType == "hide") getOperatedEntity()->hide();
- if(opType == "show") entity->show();
+ if(opType == "show") getOperatedEntity()->show();
- if(opType == "hidden") opt_response_content = entity->hidden() ? "true" : "false";
+ if(opType == "hidden") opt_response_content = getOperatedEntity()->hidden() ? "true" : "false";
- if(opType == "shown") opt_response_content = entity->shown() ? "true" : "false";
+ if(opType == "shown") opt_response_content = getOperatedEntity()->shown() ? "true" : "false";
}
- } else if((opType == "sendxml") || (opType == "sendxml2e") || (opType == "sendhex") || (opType == "sendhex2e")) {
- if(!entity) throw anna::RuntimeException("No entity configured to send the message", ANNA_FILE_LOCATION);
- anna::diameter::comm::Message *msg = getWorkingNode()->createCommMessage();
+ } else if((opType == "sendxml2e") || (opType == "sendhex2e")) {
+ anna::diameter::comm::Message *msg;
- if((opType == "sendxml") || (opType == "sendxml2e")) {
+ if(opType == "sendxml2e") {
codecMsg.loadXML(param1);
+ updateOperatedRealmNodeWithMessage(codecMsg);
+ msg = getOperatedRealm()->createCommMessage();
msg->clearBody();
try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); } // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
-
msg->setBody(codecMsg.code());
} else {
// Get DataBlock from file with hex content:
if(!getDataBlockFromHexFile(param1, db_aux))
throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
-
+ msg = getOperatedRealm()->createCommMessage();
msg->setBody(db_aux);
+ try { if(getOperatedRealm()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
}
- bool success = entity->send(msg, cl.exists("balance"));
- getWorkingNode()->releaseCommMessage(msg);
+ bool success = getOperatedEntity()->send(msg, cl.exists("balance"));
+ getOperatedRealm()->releaseCommMessage(msg);
// Detailed log:
- if(getWorkingNode()->logEnabled()) {
- anna::diameter::comm::Server *usedServer = entity->getLastUsedResource();
+ if(getOperatedRealm()->logEnabled()) {
+ anna::diameter::comm::Server *usedServer = getOperatedEntity()->getLastUsedResource();
anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
- getWorkingNode()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
+ getOperatedRealm()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
}
} else if((opType == "burst")) {
- if(!entity) throw anna::RuntimeException("No entity configured to use burst feature", ANNA_FILE_LOCATION);
+
+ if (!uniqueRealm())
+ throw anna::RuntimeException("Burst category operations are only allowed in single realm node configuration. This is only to simplify user experience.", ANNA_FILE_LOCATION);
// burst|clear clears all loaded burst messages.
// burst|load|<source_file> loads the next diameter message into launcher burst.
if(param1 == "clear") {
opt_response_content = "removed ";
- opt_response_content += anna::functions::asString(getWorkingNode()->clearBurst());
+ opt_response_content += anna::functions::asString(getOperatedRealm()->clearBurst());
opt_response_content += " elements";
} else if(param1 == "load") {
if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
codecMsg.loadXML(param2);
-
if(codecMsg.isAnswer()) throw anna::RuntimeException("Cannot load diameter answers for burst feature", ANNA_FILE_LOCATION);
try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); } // at least we need to see validation errors although it will continue loading (see validation mode configured in launcher)
- int position = getWorkingNode()->loadBurstMessage(codecMsg.code());
+ int position = getOperatedRealm()->loadBurstMessage(codecMsg.code());
opt_response_content = "loaded '";
opt_response_content += param2;
opt_response_content += "' file into burst list position ";
if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
int initialLoad = atoi(param2.c_str());
- int processed = getWorkingNode()->startBurst(initialLoad);
+ int processed = getOperatedRealm()->startBurst(initialLoad);
if(processed > 0) {
opt_response_content = "initial load completed for ";
} else if(param1 == "push") {
if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
- int pushed = getWorkingNode()->pushBurst(atoi(param2.c_str()));
+ int pushed = getOperatedRealm()->pushBurst(atoi(param2.c_str()));
if(pushed > 0) {
opt_response_content = "pushed ";
if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
int releaseLoad = atoi(param2.c_str());
- int popped = getWorkingNode()->popBurst(releaseLoad);
+ int popped = getOperatedRealm()->popBurst(releaseLoad);
if(popped > 0) {
opt_response_content = "burst popped for ";
opt_response_content += anna::functions::entriesAsString(popped, "message");
}
} else if(param1 == "stop") {
- int left = getWorkingNode()->stopBurst();
+ int left = getOperatedRealm()->stopBurst();
if(left != -1) {
opt_response_content += anna::functions::entriesAsString(left, "message");
if(param2 == "") param2 = "yes";
bool repeat = (param2 == "yes");
- getWorkingNode()->repeatBurst(repeat);
+ getOperatedRealm()->repeatBurst(repeat);
opt_response_content += (repeat ? "repeat enabled" : "repeat disabled");
} else if(param1 == "send") {
if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
- int sent = getWorkingNode()->sendBurst(atoi(param2.c_str()));
+ int sent = getOperatedRealm()->sendBurst(atoi(param2.c_str()));
if(sent > 0) {
opt_response_content = "sent ";
} else if(param1 == "goto") {
if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
- opt_response_content = getWorkingNode()->gotoBurst(atoi(param2.c_str()));
+ opt_response_content = getOperatedRealm()->gotoBurst(atoi(param2.c_str()));
} else if(param1 == "look") {
int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
opt_response_content = "\n\n";
- opt_response_content += getWorkingNode()->lookBurst(order);
+ opt_response_content += getOperatedRealm()->lookBurst(order);
} else {
throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). See help", ANNA_FILE_LOCATION);
}
if(id < 0)
throw anna::RuntimeException("Invalid test case identifier: must be a non-negative number", ANNA_FILE_LOCATION);
- // PARAM: 1 2 3 4 5 6 7 8 9 10
+ // PARAM: 1 2 3 4 5 6 7 8 9 10 11
// test|<id>|<command>
// timeout| <msecs>
// sendxml2e| <file>[|<step number>]
// sendxml2c| <file>[|<step number>]
// delay| [msecs]
- // wait<fe/fc>|[code]|[bitR]|[ResultCode]|[sessionId]|[hopByHop]|[msisdn]|[imsi]|[serviceContextId]
+ // wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
// wait<fe/fc>-answer|<step number>
// wait<fe/fc>-regexp|<regexp>
if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
else {
if (param4 == "") LOGWARNING(anna::Logger::warning("Step number has not been provided. Take into account that this answer message will be sent 'as is' and sequence information could be wrong at the remote peer", ANNA_FILE_LOCATION));
}
+ updateOperatedRealmNodeWithMessage(codecMsg);
int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
- std::string originRealm = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
- RealmNode *realm = getRealmNode(originRealm);
- if (!realm)
- throw anna::RuntimeException("Cannot identify the realm node for the manager message. Check the Origin-Realm avp value (use the realm node name)", ANNA_FILE_LOCATION);
if (param2 == "sendxml2e")
- testManager.getTestCase(id)->addSendxml2e(codecMsg.code(), realm, stepNumber); // creates / reuses
+ testManager.getTestCase(id)->addSendxml2e(codecMsg.code(), getOperatedRealm(), stepNumber); // creates / reuses
else
- testManager.getTestCase(id)->addSendxml2c(codecMsg.code(), realm, stepNumber); // creates / reuses
+ testManager.getTestCase(id)->addSendxml2c(codecMsg.code(), getOperatedRealm(), stepNumber); // creates / reuses
}
else if (param2 == "delay") {
if (numParams > 3)
testManager.getTestCase(id)->addDelay(delay); // creates / reuses
}
else if ((param2 == "waitfe")||(param2 == "waitfc")) {
- if (numParams > 10)
+ if (numParams > 11)
throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
- if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "") {
+ if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
bool fromEntity = (param2.substr(4,2) == "fe");
- testManager.getTestCase(id)->addWait(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10);
+ testManager.getTestCase(id)->addWait(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
}
else {
throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
}
} else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
- if(!localServer) throw anna::RuntimeException("No local server configured to send the message", ANNA_FILE_LOCATION);
- anna::diameter::comm::Message *msg = getWorkingNode()->createCommMessage();
+ anna::diameter::comm::Message *msg;
if(opType == "sendxml2c") {
codecMsg.loadXML(param1);
+ updateOperatedRealmNodeWithMessage(codecMsg);
+ msg = getOperatedRealm()->createCommMessage();
msg->clearBody();
try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); } // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
// Get DataBlock from file with hex content:
if(!getDataBlockFromHexFile(param1, db_aux))
throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
-
+ msg = getOperatedRealm()->createCommMessage();
msg->setBody(db_aux);
}
- bool success = localServer->send(msg);
- getWorkingNode()->releaseCommMessage(msg);
+ bool success = getOperatedServer()->send(msg);
+ getOperatedRealm()->releaseCommMessage(msg);
// Detailed log:
- if(getWorkingNode()->logEnabled()) {
- anna::diameter::comm::ServerSession *usedServerSession = localServer->getLastUsedResource();
+ if(getOperatedRealm()->logEnabled()) {
+ anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
- getWorkingNode()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
+ getOperatedRealm()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
}
} else if(opType == "loadxml") {
codecMsg.loadXML(param1);
return;
} else if(opType == "diameterServerSessions") {
int diameterServerSessions = atoi(param1.c_str());
+ getOperatedServer()->setMaxConnections(diameterServerSessions);
- if(localServer)
- localServer->setMaxConnections(diameterServerSessions);
- else
- LOGWARNING(anna::Logger::warning("To update the number of sessions, you must configure the process diameter local server: you could also launch it with no sessions (disabled)", ANNA_FILE_LOCATION));
-
- } else if((opType == "answerxml") || (opType == "answerxml2c")) {
- if(!localServer)
- throw anna::RuntimeException("Operation not applicable (no own diameter server has been configured)", ANNA_FILE_LOCATION);
-
+ } else if(opType == "answerxml2c") {
if(param1 == "") { // programmed answers FIFO's to stdout
- response_content = localServer->getReactingAnswers()->asString("ANSWERS TO CLIENT");
+ response_content = getOperatedServer()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
return;
} else if (param1 == "rotate") {
- localServer->getReactingAnswers()->rotate(true);
+ getOperatedServer()->getReactingAnswers()->rotate(true);
} else if (param1 == "exhaust") {
- localServer->getReactingAnswers()->rotate(false);
+ getOperatedServer()->getReactingAnswers()->rotate(false);
} else if (param1 == "clear") {
- localServer->getReactingAnswers()->clear();
+ getOperatedServer()->getReactingAnswers()->clear();
} else if (param1 == "dump") {
- localServer->getReactingAnswers()->dump();
+ getOperatedServer()->getReactingAnswers()->dump();
} else {
- anna::diameter::codec::Message *message = getCodecEngine()->createMessage(param1);
- LOGDEBUG
- (
- anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION);
- );
+ codecMsg.loadXML(param1);
+ updateOperatedRealmNodeWithMessage(codecMsg);
+ anna::diameter::codec::Message *message = getOperatedRealm()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
+ LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
if(message->isRequest())
throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
int code = message->getId().first;
LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
- localServer->getReactingAnswers()->addMessage(code, message);
+ getOperatedServer()->getReactingAnswers()->addMessage(code, message);
}
} else if(opType == "answerxml2e") {
- if(!entity)
- throw anna::RuntimeException("Operation not applicable (no diameter entity has been configured)", ANNA_FILE_LOCATION);
if(param1 == "") { // programmed answers FIFO's to stdout
- response_content = entity->getReactingAnswers()->asString("ANSWERS TO ENTITY");
+ response_content = getOperatedEntity()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
return;
} else if (param1 == "rotate") {
- entity->getReactingAnswers()->rotate(true);
+ getOperatedEntity()->getReactingAnswers()->rotate(true);
} else if (param1 == "exhaust") {
- entity->getReactingAnswers()->rotate(false);
+ getOperatedEntity()->getReactingAnswers()->rotate(false);
} else if (param1 == "clear") {
- entity->getReactingAnswers()->clear();
+ getOperatedEntity()->getReactingAnswers()->clear();
} else if (param1 == "dump") {
- entity->getReactingAnswers()->dump();
+ getOperatedEntity()->getReactingAnswers()->dump();
} else {
- anna::diameter::codec::Message *message = getCodecEngine()->createMessage(param1);
- LOGDEBUG
- (
- anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION);
- );
+ codecMsg.loadXML(param1);
+ updateOperatedRealmNodeWithMessage(codecMsg);
+ anna::diameter::codec::Message *message = getOperatedRealm()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
+ LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
if(message->isRequest())
throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
int code = message->getId().first;
LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
- entity->getReactingAnswers()->addMessage(code, message);
+ getOperatedEntity()->getReactingAnswers()->addMessage(code, message);
}
} else {
throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
result->createAttribute("StartTime", a_start_time.asString());
result->createAttribute("SecondsLifeTime", anna::time::functions::lapsedMilliseconds() / 1000);
// Diameter:
- getCodecEngine()->asXML(result);
for (realm_nodes_it it = a_nodes.begin(); it != a_nodes.end(); it++) {
it->second->asXML(result);
}
#include <anna/core/core.hpp>
#include <anna/comm/comm.hpp>
#include <anna/time/Date.hpp>
+#include <anna/diameter/codec/Message.hpp>
// Process
#include <MyCommunicator.hpp>
}
class TestManager;
+class RealmNode;
+class MyDiameterEntity;
+class MyLocalServer;
+class MyDiameterEngine;
// RealmNode resources
class RealmNode;
// Core engines:
MyCommunicator *a_communicator;
- anna::diameter::codec::Engine *a_codecEngine;
- anna::diameter::stack::Dictionary *a_baseProtocolDictionary;
anna::timex::Engine* a_timeEngine;
MyCounterRecorder *a_counterRecorder;
anna::Millisecond a_admlMinResolution;
// Nodes deployment:
realm_nodes_t a_nodes;
RealmNode *a_workingNode;
+ RealmNode *a_operatedRealm; // auxiliary for eventOperation
// comm resources:
anna::comm::ServerSocket* a_httpServerSocket; // HTTP
public:
Launcher();
+ //~Launcher(); TODO
void loadServices(const std::string & xmlPathFile, bool eventOperation = false) throw(anna::RuntimeException);
void startServices() throw(anna::RuntimeException);
- anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
- //anna::diameter::stack::Dictionary *getBaseProtocolDictionary() const throw() { return a_baseProtocolDictionary; }
- RealmNode *getWorkingNode() const throw(anna::RuntimeException); // management operations working node
- bool setWorkingNode(const std::string &name) throw(); // we could update ignoreFlagsOnValidation/integrationAndDebugging over the global codec engine
- // but finally, that configuration issues will be global to the process.
-
- RealmNode *getRealmNode(const std::string &realmName) const throw();
+ bool setWorkingNode(const std::string &name) throw();
+ RealmNode *getRealmNode(const std::string &realmName) const throw(anna::RuntimeException);
+ RealmNode *getRealmNode(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException);
+ bool uniqueRealm() const throw() { return (a_nodes.size() == 1); }
+
+ // Operated realm for communication resources smart assignment ////////////////////////////////////////////////////////////
+ void updateOperatedRealmNodeWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException);
+ RealmNode *getOperatedRealm() const throw(anna::RuntimeException);
+ MyDiameterEntity *getOperatedEntity() const throw(anna::RuntimeException);
+ MyLocalServer *getOperatedServer() const throw(anna::RuntimeException);
+ MyDiameterEngine *getOperatedEngine() const throw(anna::RuntimeException);
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MyCommunicator *getCommunicator() throw() { return a_communicator; }
Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
- // Testing:
- TestManager::instantiate().receiveMessage(message, clientSession);
-
// CommandId:
anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
LOGDEBUG
ex.trace();
}
}
+
+ // Testing:
+ TestManager::instantiate().receiveMessage(message, my_node, clientSession);
}
void MyDiameterEntity::eventResponse(const anna::diameter::comm::Response &response)
bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable);
bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success);
- // Testing:
- TestManager::instantiate().receiveMessage(*message, clientSession);
-
// CommandId:
anna::diameter::CommandId request_cid = request->getCommandId();
LOGDEBUG
// Triggering burst:
if(isOK || contextExpired) my_node->sendBurstMessage();
+
+ // Testing:
+ TestManager::instantiate().receiveMessage(*message, my_node, clientSession);
}
void MyDiameterEntity::eventUnknownResponse(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
class MyDiameterEntity : public anna::diameter::comm::Entity {
- anna::diameter::codec::Engine * a_codecEngine; // for automatic answers (failed-avp), write logs, etc.
bool a_balance; // Balance over entity servers instead of doing standard behaviour (first primary, secondary if fails, etc.). Default: false.
std::string a_sessionBasedModelsType;
public:
MyDiameterEntity() {
- a_codecEngine = NULL;
a_balance = false;
a_sessionBasedModelsType = "SessionIdLowPart";
}
ProgrammedAnswers a_reactingAnswers;
- void setCodecEngine(anna::diameter::codec::Engine *codecEngine) throw() { a_codecEngine = codecEngine; a_reactingAnswers.setCodecEngine(codecEngine); }
ProgrammedAnswers *getReactingAnswers() throw() { return (ProgrammedAnswers *)&a_reactingAnswers; }
// Additional configuration parameters:
// Performance stats:
Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
-
- // Testing:
- TestManager::instantiate().receiveMessage(message, serverSession);
+ anna::diameter::codec::Engine *codecEngine = my_node->getCodecEngine();
// CommandId:
anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
my_node->writeLogFile(message, (success ? "fwd2e" : "fwd2eError"), detail); // forwarded
}
+
+ // Testing:
+ TestManager::instantiate().receiveMessage(message, my_node, serverSession);
+
return;
}
// Error analisys:
bool analysisOK = true; // by default
anna::diameter::codec::Message *answer_message = NULL;
- anna::diameter::codec::Message codecMsg(a_codecEngine);
- anna::diameter::codec::Message codecAnsMsg(a_codecEngine);
+ anna::diameter::codec::Message codecMsg;
+ anna::diameter::codec::Message codecAnsMsg;
CommandLine& cl(anna::CommandLine::instantiate());
if(!cl.exists("ignoreErrors")) { // Error analysis
answer_message = programmed_answer;
// Prepare answer:
my_app.getCommunicator()->prepareAnswer(answer_message, message);
- } else return; // nothing done
+ } else {
+
+ // Testing:
+ TestManager::instantiate().receiveMessage(message, my_node, serverSession);
+
+ return; // nothing done
+ }
}
- anna::diameter::codec::Engine::ValidationMode::_v backupVM = a_codecEngine->getValidationMode();
+ anna::diameter::codec::Engine::ValidationMode::_v backupVM = codecEngine->getValidationMode();
if(!analysisOK)
- a_codecEngine->setValidationMode(anna::diameter::codec::Engine::ValidationMode::Never);
+ codecEngine->setValidationMode(anna::diameter::codec::Engine::ValidationMode::Never);
anna::diameter::comm::Message *msg;
try {
my_node->releaseCommMessage(msg);
// Restore validation mode
- a_codecEngine->setValidationMode(backupVM);
+ codecEngine->setValidationMode(backupVM);
// Pop front the reacting answer:
if(analysisOK && programmed) a_reactingAnswers.nextMessage(code);
+
+ // Testing:
+ TestManager::instantiate().receiveMessage(message, my_node, serverSession);
}
void MyLocalServer::eventResponse(const anna::diameter::comm::Response &response)
bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable);
bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success);
- // Testing:
- TestManager::instantiate().receiveMessage(*message, serverSession);
-
// CommandId:
anna::diameter::CommandId request_cid = request->getCommandId();
LOGDEBUG
my_node->releaseCommMessage(request);
}
}
+
+ // Testing:
+ TestManager::instantiate().receiveMessage(*message, my_node, serverSession);
}
void MyLocalServer::eventUnknownResponse(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message)
class MyLocalServer : public anna::diameter::comm::LocalServer {
- anna::diameter::codec::Engine * a_codecEngine; // for automatic answers (failed-avp), write logs, etc.
-
void eventResponse(const anna::diameter::comm::Response&) throw(anna::RuntimeException);
void eventRequest(anna::diameter::comm::ServerSession *, const anna::DataBlock&) throw(anna::RuntimeException);
void eventUnknownResponse(anna::diameter::comm::ServerSession *, const anna::DataBlock&) throw(anna::RuntimeException);
public:
ProgrammedAnswers a_reactingAnswers;
- void setCodecEngine(anna::diameter::codec::Engine *codecEngine) throw() { a_codecEngine = codecEngine; a_reactingAnswers.setCodecEngine(codecEngine); }
ProgrammedAnswers *getReactingAnswers() throw() { return (ProgrammedAnswers *)&a_reactingAnswers; }
};
// 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 //
-
// Standard
#include <string>
#include <fstream>
// Project
+#include <anna/diameter/codec/Message.hpp>
#include <anna/diameter/codec/Engine.hpp>
+#include <anna/diameter/codec/EngineManager.hpp>
// Process
#include <ProgrammedAnswers.hpp>
-void ProgrammedAnswers::clear () throw() {
- for (reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) {
- a_codecEngine->releaseMessage(*(it->second->begin()));
- delete(it->second);
+void ProgrammedAnswers::clear() throw () {
+ try {
+ anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+ for (reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) {
+ anna::diameter::codec::Message *message = *(it->second->begin());
+ em.getCodecEngine(message->getApplicationId())->releaseMessage(message);
+ delete (it->second);
+ }
+ a_deques.clear();
+ }
+ catch (anna::RuntimeException &ex) {
+ ex.trace();
}
- a_deques.clear();
}
-void ProgrammedAnswers::dump () throw() {
+void ProgrammedAnswers::dump() throw () {
std::string outfilename, xmlString;
- for(reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) {
- int sequence = 1;
- for(codec_messages_deque_const_iterator itm = it->second->begin(); itm != it->second->end(); itm++) {
- // programmed_answer.<code>.<sequence>
- outfilename = "programmed_answer.";
- outfilename += anna::functions::asString(it->first);
- outfilename += ".";
- outfilename += anna::functions::asString(sequence++);
- outfilename += ".xml";
- std::ofstream outfile(outfilename.c_str(), std::ifstream::out);
- xmlString = (*itm)->asXMLString();
- outfile.write(xmlString.c_str(), xmlString.size());
- outfile.close();
- }
+ for (reacting_answers_const_iterator it = a_deques.begin();
+ it != a_deques.end(); it++) {
+ int sequence = 1;
+ for (codec_messages_deque_const_iterator itm = it->second->begin();
+ itm != it->second->end(); itm++) {
+ // programmed_answer.<code>.<sequence>
+ outfilename = "programmed_answer.";
+ outfilename += anna::functions::asString(it->first);
+ outfilename += ".";
+ outfilename += anna::functions::asString(sequence++);
+ outfilename += ".xml";
+ std::ofstream outfile(outfilename.c_str(), std::ifstream::out);
+ xmlString = (*itm)->asXMLString();
+ outfile.write(xmlString.c_str(), xmlString.size());
+ outfile.close();
+ }
}
}
-void ProgrammedAnswers::addMessage(int code, anna::diameter::codec::Message *message) throw() {
- if (!message) return; // just in case
- message->setEngine(a_codecEngine); // just in case
+void ProgrammedAnswers::addMessage(int code, anna::diameter::codec::Message *message) throw () {
+ if (!message)
+ return; // just in case
reacting_answers_const_iterator it = a_deques.find(code);
if (it != a_deques.end()) {
- it->second->push_back(message);
- }
- else {
- codec_messages_deque *deque = new codec_messages_deque;
- a_deques[code] = deque;
- deque->push_back(message);
+ it->second->push_back(message);
+ } else {
+ codec_messages_deque *deque = new codec_messages_deque;
+ a_deques[code] = deque;
+ deque->push_back(message);
}
}
-anna::diameter::codec::Message* ProgrammedAnswers::getMessage(int code) const throw() { //get the front message (begin()), returns NULL if deque is empty
+anna::diameter::codec::Message* ProgrammedAnswers::getMessage(int code) const throw () { //get the front message (begin()), returns NULL if deque is empty
anna::diameter::codec::Message *result = NULL;
reacting_answers_const_iterator it = a_deques.find(code);
if (it != a_deques.end()) {
- if (!it->second->empty()) result = *(it->second->begin());
+ if (!it->second->empty())
+ result = *(it->second->begin());
}
return result;
}
-void ProgrammedAnswers::nextMessage(int code) throw() { //pops the deque and release the message (when deque is not empty: deque::empty)
- reacting_answers_const_iterator it = a_deques.find(code);
- if (it != a_deques.end()) {
- if (!it->second->empty()) {
- if (a_rotate) {
- addMessage(code, *(it->second->begin()));
- }
- else {
- a_codecEngine->releaseMessage(*(it->second->begin()));
- }
- it->second->pop_front();
- }
+void ProgrammedAnswers::nextMessage(int code) throw () { //pops the deque and release the message (when deque is not empty: deque::empty)
+ try {
+ reacting_answers_const_iterator it = a_deques.find(code);
+ if (it != a_deques.end()) {
+ if (!it->second->empty()) {
+ if (a_rotate) {
+ addMessage(code, *(it->second->begin()));
+ } else {
+ anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+ anna::diameter::codec::Message *message = *(it->second->begin());
+ em.getCodecEngine(message->getApplicationId())->releaseMessage(message);
+ }
+ it->second->pop_front();
+ }
+ }
+ }
+ catch (anna::RuntimeException &ex) {
+ ex.trace();
}
}
-std::string ProgrammedAnswers::asString(const char *queueName) const throw() {
+std::string ProgrammedAnswers::asString(const char *queueName) const throw () {
std::string result = "";
std::string aux = "FIFO QUEUE '";
aux += queueName;
aux += "', Rotation ";
- aux += a_rotate ? "enabled":"disabled";
+ aux += a_rotate ? "enabled" : "disabled";
result += anna::functions::highlightJustify(aux);
- result += "Codec engine: ";
- result += a_codecEngine->getClassName();
- if(a_deques.size() != 0) {
- for(reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) {
- if (it->second->size() != 0) {
- aux = "Answer code ";
- aux += anna::functions::asString(it->first);
- result += anna::functions::highlightJustify(aux, anna::functions::TextHighlightMode::OverAndUnderline,
- anna::functions::TextJustifyMode::Left, '-');
- for(codec_messages_deque_const_iterator itm = it->second->begin(); itm != it->second->end(); itm++) {
- result += (*itm)->asXMLString();
- result += "\n";
- }
- result += "\n";
- }
- }
- }
- else {
- result = "No ocurrences found\n\n";
+ if (a_deques.size() != 0) {
+ for (reacting_answers_const_iterator it = a_deques.begin();
+ it != a_deques.end(); it++) {
+ if (it->second->size() != 0) {
+ aux = "Answer code ";
+ aux += anna::functions::asString(it->first);
+ result += anna::functions::highlightJustify(aux,
+ anna::functions::TextHighlightMode::OverAndUnderline,
+ anna::functions::TextJustifyMode::Left, '-');
+ for (codec_messages_deque_const_iterator itm = it->second->begin();
+ itm != it->second->end(); itm++) {
+ result += (*itm)->asXMLString();
+ result += "\n";
+ }
+ result += "\n";
+ }
+ }
+ } else {
+ result = "No ocurrences found\n\n";
}
return result;
}
namespace diameter {
namespace codec {
class Message;
- class Engine;
}
}
}
reacting_answers_container a_deques;
bool a_rotate;
- anna::diameter::codec::Engine *a_codecEngine;
public:
ProgrammedAnswers() { a_rotate = false; }
~ProgrammedAnswers() { clear(); }
- void setCodecEngine(anna::diameter::codec::Engine *codecEngine) throw() { a_codecEngine = codecEngine; }
bool rotate() const throw() { return a_rotate; }
void rotate(bool r) throw() { a_rotate = r; }
// Project
#include <anna/diameter.comm/Message.hpp>
+#include <anna/diameter/stack/Dictionary.hpp>
+#include <anna/diameter/codec/EngineManager.hpp>
#include <anna/core/core.hpp>
#include <anna/time/Date.hpp>
#include <anna/xml/Compiler.hpp>
#include <RealmNode.hpp>
#include <MyDiameterEngine.hpp>
+
namespace anna {
namespace diameter {
namespace stack {
}
}
-RealmNode::RealmNode(const std::string &originRealm, anna::diameter::codec::Engine *codecEngine, const anna::diameter::stack::Dictionary *baseProtocolDictionary) :
- a_originRealm(originRealm), a_codecEngine(codecEngine) {
+RealmNode::RealmNode(const std::string &originRealm, unsigned int applicationId, const anna::diameter::stack::Dictionary *baseProtocolDictionary) :
+ a_originRealm(originRealm), a_applicationId(applicationId) {
std::string commEngineName = a_originRealm + "_DiameterCommEngine";
a_commEngine = new MyDiameterEngine(commEngineName.c_str(), baseProtocolDictionary);
a_commEngine->setAutoBind(false); // allow to create client-sessions without binding them, in order to set timeouts.
+ a_codecEngine = anna::diameter::codec::EngineManager::instantiate().getCodecEngine(applicationId);
a_logFile = "";
a_burstLogFile = "";
a_entity = (MyDiameterEntity*)(a_commEngine->createEntity(servers, entityDescription));
a_entity->setClassCodeTimeout(anna::diameter::comm::ClassCode::Bind, bindTimeout);
a_entity->setClassCodeTimeout(anna::diameter::comm::ClassCode::ApplicationMessage, applicationTimeout);
-
- // Codec engine for reacting answers (failed-avp):
- a_entity->setCodecEngine(getCodecEngine());
}
void RealmNode::startDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout) throw(anna::RuntimeException) {
a_diameterServer->setDescription(serverDescription);
a_diameterServer->setAllowedInactivityTime(inactivityTimeout);
-
- // Codec engine for reacting answers (failed-avp):
- a_diameterServer->setCodecEngine(getCodecEngine());
}
anna::diameter::comm::Message *RealmNode::createCommMessage() throw(anna::RuntimeException) {
void RealmNode::writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw() {
-// if (!logEnabled()) return;
- anna::diameter::codec::Message codecMsg(getCodecEngine());
+ anna::diameter::codec::Message codecMsg;
try { codecMsg.decode(db); } catch(anna::RuntimeException &ex) { ex.trace(); }
writeLogFile(codecMsg, logExtension, detail);
-
}
-// Si ya lo tengo decodificado:
-void RealmNode::writeLogFile(const anna::diameter::codec::Message & decodedMessage, const std::string &logExtension, const std::string &detail) const throw() {
-// if (!logEnabled()) return;
+// Already decoded:
+void RealmNode::writeLogFile(const anna::diameter::codec::Message &decodedMessage, const std::string &logExtension, const std::string &detail) const throw() {
// Open target file:
std::string targetFile = a_logFile;
std::map<int, anna::diameter::comm::Message*>::const_iterator it = a_burstMessages.find(order - 1);
if(it != a_burstMessages.end()) {
- // Decode
- anna::diameter::codec::Message codecMsg(getCodecEngine());
- try { codecMsg.decode((*it).second->getBody()); } catch(anna::RuntimeException &ex) { ex.trace(); }
- result = codecMsg.asXMLString();
+ anna::diameter::codec::Message codecMsg;
+ try { codecMsg.decode((*it).second->getBody()); result = codecMsg.asXMLString(); } catch(anna::RuntimeException &ex) { ex.trace(); }
}
return result;
anna::xml::Node* result = parent->createChild("RealmNode");
result->createAttribute("OriginRealm", a_originRealm);
+ result->createAttribute("ApplicationId", a_applicationId);
result->createAttribute("LogFile", a_logFile);
result->createAttribute("SplitLog", a_splitLog ? "yes" : "no");
result->createAttribute("DetailedLog", a_detailedLog ? "yes" : "no");
// main
std::string a_originRealm;
+ unsigned int a_applicationId;
// Timming
anna::Millisecond a_allowedInactivityTime;
int a_burstPopCounter;
public:
- RealmNode(const std::string &originRealm, anna::diameter::codec::Engine *codecEngine, const anna::diameter::stack::Dictionary *baseProtocolDictionary);
+ RealmNode(const std::string &originRealm, unsigned int applicationId, const anna::diameter::stack::Dictionary *baseProtocolDictionary);
~RealmNode() {;}
+ const std::string &getName() const throw() { return a_originRealm; }
+
// Core resources:
MyDiameterEngine* getMyDiameterEngine() const throw() { return a_commEngine; }
+ unsigned int getApplicationId() const throw() { return a_applicationId; }
anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
+
void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException);
MyDiameterEntity *getEntity() const throw() { return a_entity; }
void startDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout) throw(anna::RuntimeException);
commandLine.add("httpServer", anna::CommandLine::Argument::Optional, "HTTP Management interface address (using i.e. curl tool) in '<address>:<port>' format. For example: 10.20.30.40:8080");
commandLine.add("httpServerShared", anna::CommandLine::Argument::Optional, "Enables shared bind for HTTP Management interface address. It would be useful i.e. to allow a great amount of curl operations per second", false);
- // Codec engine
+ // Automatic error answer
commandLine.add("ignoreErrors", anna::CommandLine::Argument::Optional, "Local server skips requests errors analysis which would prepare automatic answers for them when a problem is found. If no answer is programmed and entity is configured, a failed request would be forwarded (delegates at the end point) even if this parameter is missing", false);
- commandLine.add("ignoreFlags", anna::CommandLine::Argument::Optional, "Ignore flags on validation (at the moment only bits M & P from AVPs, because V bit is too important; no operation flags could be checked). Also force compact xml presentation ignoring flags during dictionary elements identification", false);
- commandLine.add("integrationAndDebugging", anna::CommandLine::Argument::Optional, "Sets validation mode to 'Always' (default validates only after decoding), and validation depth to 'Complete' (default validates until 'FirstError')", false);
- commandLine.add("fixMode", anna::CommandLine::Argument::Optional, "Sets message fix mode (unreconized values will assume default 'BeforeEncoding'). Allowed: 'BeforeEncoding', 'AfterDecoding', 'Always', 'Never'");
commandLine.initialize(argv, argc);
commandLine.verify();
[[ "$1" = "" ]] && use
[[ ! -f "$1" ]] && { echo "ERROR: file '$1' not found" ; echo; echo; exit ; }
echo
-operation="sendxml|$1"
+operation="sendxml2e|$1"
[[ "$2" = "2c" ]] && operation="sendxml2c|$1"
curl -m 1 --data "$operation" $TRACE ${SERVER}
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-balancer" entity="192.168.12.11:3868,192.168.12.21:3868" diameterServer="localhost:3868" balance="yes"/>
- <!-- <node originRealm="ADML-proxy" entity="localhost:3868" diameterServer="localhost:3870"/> -->
+ <node originRealm="ADML-balancer" applicationId="0" entity="192.168.12.11:3868,192.168.12.21:3868" diameterServer="localhost:3868" balance="yes"/>
+ <!-- <node originRealm="ADML-proxy" applicationId="0" entity="localhost:3868" diameterServer="localhost:3870"/> -->
</services>
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-client" entity="localhost:3868"/>
+ <node originRealm="ADML-client" applicationId="0" entity="localhost:3868"/>
</services>
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-dummy"/>
+ <node originRealm="ADML-dummy" applicationId="0"/>
</services>
<stack id="0" dictionary="dictionaryBase.xml"/>
<!-- Nodes -->
- <node originRealm="afNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
- <node originRealm="ggsnNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
- <node originRealm="ggsn2Node" entity="192.168.12.11:3868,192.168.12.21:3868"/>
- <node originRealm="ocsNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="afNode" applicationId="16777236" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="ggsnNode" applicationId="16777238" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="ggsn2Node" applicationId="16777238" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="ocsNode" applicationId="16777302" entity="192.168.12.11:3868,192.168.12.21:3868"/>
</services>
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-server" diameterServer="localhost:3868"/>
+ <node originRealm="ADML-server" applicationId="0" diameterServer="localhost:3868"/>
</services>
<!ELEMENT services (stack*, node*)>
<!ELEMENT stack EMPTY>
-<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
+<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED validationMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED validationDepth (Complete | FirstError) #IMPLIED fixMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED ignoreFlagsOnValidation (yes | no) #IMPLIED>
<!--
Stack record
- id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed
- (in multistack applications, it shall be mandatory respect such association to know the stack used
- for processed messages).
- dictionary: Path to the dictionary file
+ id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed
+ (in multistack applications, it shall be mandatory respect such association to know the stack used
+ for processed messages).
+ dictionary: Path to the dictionary file.
+ validationMode: Sets the validation mode. Default is 'AfterDecoding'.
+ validationDepth: Sets the validation depth. Default is 'FirstError'.
+ fixMode: Sets the fix mode. Default is 'BeforeEncoding'.
+ ignoreFlagsOnValidation: Ignore flags during message validation. Default is 'no'.
-->
<!ELEMENT node EMPTY>
-<!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
+<!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
<!--
Node record
originRealm: Node identifier (Origin-Realm name).
+ applicationId: The Application-Id provided must exists as a registered 'stack id'.
originHost: Diameter application host name (system name). If missing, process sets o.s. hostname
Note that if you have two or more realms, the names must be different.
cer: User defined CER path file to be encoded to establish diameter connections.
int steps = a_steps.size();
int stepIndx = stepNumber - 1;
if ((stepIndx < 0) || (stepIndx > (a_steps.size()-1)))
- throw anna::RuntimeException(anna::functions::asString("Step number out of range (test case %llu)", a_id), ANNA_FILE_LOCATION);
+ throw anna::RuntimeException(anna::functions::asString("Step number (%d) out of range (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
TestStep *stepReferred = a_steps[stepIndx];
if (stepReferred->getType() != TestStep::Type::Wait)
- throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'wait' step (test case %llu)", a_id), ANNA_FILE_LOCATION);
+ throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
const TestCondition &tc = (static_cast<TestStepWait*>(stepReferred))->getCondition();
if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors
- throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'wait for request' step (test case %llu)", a_id), ANNA_FILE_LOCATION);
+ throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION);
}
}
}
void TestCase::addWait(bool fromEntity,
- const std::string &code, const std::string &bitR, const std::string &resultCode, const std::string &sessionId,
- const std::string &hopByHop, const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException) {
+ const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
+ const std::string &sessionId, const std::string &resultCode,
+ const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException) {
assertInitialized();
std::string usedHopByHop = hopByHop;
TestStepWait *step = NULL;
}
if (!step) step = new TestStepWait(this);
- step->setCondition(fromEntity, code, bitR, resultCode, sessionId, usedHopByHop, msisdn, imsi, serviceContextId);
+ step->setCondition(fromEntity, code, bitR, usedHopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
LOGWARNING(
if (hasSameCondition(step->getCondition()))
void addSendxml2c(const anna::DataBlock &db, RealmNode *realm, int stepNumber) throw(anna::RuntimeException);
void addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException);
void addWait(bool fromEntity,
- const std::string &code, const std::string &bitR, const std::string &resultCode, const std::string &sessionId,
- const std::string &hopByHop, const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
+ const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
+ const std::string &sessionId, const std::string &resultCode,
+ const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
void addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException);
void addCmd(const std::string &script, const std::string ¶meters) throw(anna::RuntimeException);
#include <anna/xml/Compiler.hpp>
#include <anna/diameter/defines.hpp>
#include <anna/diameter/codec/functions.hpp>
+#include <anna/diameter/codec/Message.hpp>
#include <anna/diameter/helpers/base/functions.hpp>
#include <anna/diameter/helpers/dcca/defines.hpp>
// Process
#include <TestCondition.hpp>
-#include <Launcher.hpp>
const char* TestCondition::asText(const Type::_v type)
if (a_type == Type::Generic)
return (a_regexp != "");
else
- return (a_code != "" || a_bitR != "" || a_resultCode != "" || a_sessionId != "" || a_hopByHop != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != "");
+ return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != "");
}
bool operator==(const TestCondition &c1, const TestCondition &c2) throw() {
else {
if (c1.a_code != c2.a_code) return false;
if (c1.a_bitR != c2.a_bitR) return false;
- if (c1.a_resultCode != c2.a_resultCode) return false;
- if (c1.a_sessionId != c2.a_sessionId) return false;
if (c1.a_hopByHop != c2.a_hopByHop) return false;
+ if (c1.a_applicationId != c2.a_applicationId) return false;
+ if (c1.a_sessionId != c2.a_sessionId) return false;
+ if (c1.a_resultCode != c2.a_resultCode) return false;
if (c1.a_msisdn != c2.a_msisdn) return false;
if (c1.a_imsi != c2.a_imsi) return false;
if (c1.a_serviceContextId != c2.a_serviceContextId) return false;
bool TestCondition::comply(const anna::DataBlock &message/*, bool matchSessionId*/) const throw() {
if (a_type == Type::Generic) {
- Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
- static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine());
- try {
- codecMsg.decode(message);
- }
- catch (anna::RuntimeException &ex) {
- ex.trace();
- }
-
+ anna::diameter::codec::Message codecMsg;
+ try { codecMsg.decode(message); } catch (anna::RuntimeException &ex) { ex.trace(); }
return codecMsg.isLike(a_regexp);
}
if (a_bitR != compare) return false;
}
- if (a_resultCode != "") {
+ if (a_hopByHop != "") {
try {
- anna::U32 rc = anna::diameter::helpers::base::functions::getResultCode(message);
- compare = anna::functions::asString(rc);
+ anna::diameter::HopByHop h = anna::diameter::codec::functions::getHopByHop(message);
+ compare = anna::functions::asString(h);
}
catch (anna::RuntimeException &) { return false; }
- if (a_resultCode != compare) return false;
+ if (a_hopByHop != compare) return false;
+ }
+
+ if (a_applicationId != "") {
+ try {
+ anna::diameter::ApplicationId a = anna::diameter::codec::functions::getApplicationId(message);
+ compare = anna::functions::asString(a);
+ }
+ catch (anna::RuntimeException &) { return false; }
+ if (a_applicationId != compare) return false;
}
//if (matchSessionId) {
- if (a_sessionId != "") {
- try {
- compare = anna::diameter::helpers::base::functions::getSessionId(message);
- }
- catch (anna::RuntimeException &) { return false; }
- if (a_sessionId != compare) return false;
+ if (a_sessionId != "") {
+ try {
+ compare = anna::diameter::helpers::base::functions::getSessionId(message);
}
+ catch (anna::RuntimeException &) { return false; }
+ if (a_sessionId != compare) return false;
+ }
//}
- if (a_hopByHop != "") {
+ if (a_resultCode != "") {
try {
- anna::diameter::HopByHop h = anna::diameter::codec::functions::getHopByHop(message);
- compare = anna::functions::asString(h);
+ anna::U32 rc = anna::diameter::helpers::base::functions::getResultCode(message);
+ compare = anna::functions::asString(rc);
}
catch (anna::RuntimeException &) { return false; }
- if (a_hopByHop != compare) return false;
+ if (a_resultCode != compare) return false;
}
if (a_msisdn != "") {
else {
if (a_code != "") result->createAttribute("Code", atoi(a_code.c_str()));
if (a_bitR != "") result->createAttribute("BitR", ((a_bitR == "1") ? "yes":"no"));
+ if (a_hopByHop != "") result->createAttribute("HopByHop", atoll(a_hopByHop.c_str()));
+ if (a_applicationId != "") result->createAttribute("ApplicationId", atoll(a_applicationId.c_str()));
if (a_sessionId != "") result->createAttribute("SessionId", a_sessionId);
if (a_resultCode != "") result->createAttribute("ResultCode", atoi(a_resultCode.c_str()));
- if (a_hopByHop != "") result->createAttribute("HopByHop", atoll(a_hopByHop.c_str()));
if (a_msisdn != "") result->createAttribute("Msisdn", a_msisdn);
if (a_imsi != "") result->createAttribute("Imsi", a_imsi);
if (a_serviceContextId != "") result->createAttribute("ServiceContextId", a_serviceContextId);
TestCondition() : a_rcvFromEntity(true),
a_regexp(""),
- a_code(""), a_bitR(""), a_resultCode(""), a_sessionId(""),
- a_hopByHop(""), a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Basic; }
+ a_code(""), a_bitR(""), a_hopByHop(""), a_applicationId(""),
+ a_sessionId(""), a_resultCode(""),
+ a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Basic; }
// Source of the received message
// Basic
void setCode(const std::string &value) throw() { a_code = value; }
void setBitR(const std::string &value) throw() { a_bitR = value; }
- void setResultCode(const std::string &value) throw() { a_resultCode = value; }
- void setSessionId(const std::string &value) throw() { a_sessionId = value; }
void setHopByHop(const std::string &value) throw() { a_hopByHop = value; }
+ void setApplicationId(const std::string &value) throw() { a_applicationId = value; }
+ void setSessionId(const std::string &value) throw() { a_sessionId = value; }
+ void setResultCode(const std::string &value) throw() { a_resultCode = value; }
void setMsisdn(const std::string &value) throw() { a_msisdn = value; }
void setImsi(const std::string &value) throw() { a_imsi = value; }
void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; }
// Basic
const std::string & getCode() const throw() { return a_code; }
const std::string & getBitR() const throw() { return a_bitR; }
- const std::string & getResultCode() const throw() { return a_resultCode; }
- const std::string & getSessionId() const throw() { return a_sessionId; }
const std::string & getHopByHop() const throw() { return a_hopByHop; }
+ const std::string & getApplicationId() const throw() { return a_applicationId; }
+ const std::string & getSessionId() const throw() { return a_sessionId; }
+ const std::string & getResultCode() const throw() { return a_resultCode; }
const std::string & getMsisdn() const throw() { return a_msisdn; }
const std::string & getImsi() const throw() { return a_imsi; }
const std::string & getServiceContextId() const throw() { return a_serviceContextId; }
// Basic:
std::string a_code;
std::string a_bitR;
- std::string a_resultCode;
- std::string a_sessionId;
std::string a_hopByHop;
+ std::string a_applicationId;
+ std::string a_sessionId;
+ std::string a_resultCode;
std::string a_msisdn;
std::string a_imsi;
std::string a_serviceContextId;
#include <TestCase.hpp>
#include <TestClock.hpp>
#include <Launcher.hpp>
+#include <RealmNode.hpp>
class TestTimer;
return NULL;
}
-void TestManager::receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) {
+void TestManager::receiveMessage(const anna::DataBlock &message, RealmNode *realm, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) {
// Testing disabled:
if (!tests()) return;
std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "':";
try {
- Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
- static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine());
+ static anna::diameter::codec::Message codecMsg;
codecMsg.decode(message);
hint += "\n"; hint += codecMsg.asXMLString();
+
+ // // Realm checking:
+ // std::string messageOR = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
+ // if (messageOR != realm->getName()) {
+ // LOGWARNING(anna::Logger::warning(anna::functions::asString("Received message from realm '%s', with different Origin-Realm: %s", realm->getName().c_str(), messageOR.c_str()), ANNA_FILE_LOCATION));
+ // }
}
catch (anna::RuntimeException &ex) {
ex.trace();
}
}
-void TestManager::receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) {
+void TestManager::receiveMessage(const anna::DataBlock &message, RealmNode *realm, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) {
// Testing disabled:
if (!tests()) return;
std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "':";
try {
- Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
- static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine());
+ static anna::diameter::codec::Message codecMsg;
codecMsg.decode(message);
hint += "\n"; hint += codecMsg.asXMLString();
+
+ // // Realm checking:
+ // std::string messageOR = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
+ // if (messageOR != realm->getName()) {
+ // LOGWARNING(anna::Logger::warning(anna::functions::asString("Received message from realm '%s', with different Origin-Realm: %s", realm->getName().c_str(), messageOR.c_str()), ANNA_FILE_LOCATION));
+ // }
}
catch (anna::RuntimeException &ex) {
ex.trace();
class TestClock;
class TestCase;
class TestCaseStep;
+class RealmNode;
+
typedef std::map<unsigned int /* test case id */, TestCase*> test_pool_t;
typedef std::map<unsigned int /* test case id */, TestCase*>::const_iterator test_pool_it;
// Main logic
TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw();
- void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
- void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException);
+ void receiveMessage(const anna::DataBlock &message, RealmNode *realm, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
+ void receiveMessage(const anna::DataBlock &message, RealmNode *realm, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException);
anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
std::string asXMLString() const throw();
#include <anna/diameter.comm/ServerSession.hpp>
#include <anna/diameter.comm/Server.hpp>
#include <anna/core/tracing/Logger.hpp>
-#include <anna/diameter/codec/Message.hpp>
#include <anna/diameter/codec/functions.hpp>
#include <anna/diameter/helpers/base/functions.hpp>
#include <MyDiameterEntity.hpp>
#include <MyLocalServer.hpp>
#include <TestStep.hpp>
-#include <Launcher.hpp>
#include <TestCase.hpp>
#include <TestManager.hpp>
#include <TestTimer.hpp>
+#include <Launcher.hpp>
namespace {
// TODO: mutex the step while setting data here !!
}
- bool decodeMessage(const anna::DataBlock &message, anna::diameter::codec::Message *messageCodec) throw() {
+ bool decodeMessage(const anna::DataBlock &message, anna::diameter::codec::Message &messageCodec) throw() {
if (message.isEmpty())
return false;
bool result = true;
try {
- if (!messageCodec) {
- Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
- messageCodec = new anna::diameter::codec::Message(my_app.getCodecEngine());
- }
-
- messageCodec->decode(message);
+ messageCodec.decode(message);
}
catch (anna::RuntimeException &ex) {
ex.trace();
return result;
}
-
}
a_number = testCase->steps() + 1; // testCase is not NULL
}
+bool TestStep::decodeMessage() throw() {
+ if (a_messageCodec) return true;
+ a_messageCodec = new anna::diameter::codec::Message;
+ if (::decodeMessage(a_message, *a_messageCodec)) return true;
+
+ delete a_messageCodec;
+ a_messageCodec = NULL;
+ return false;
+}
+
const char* TestStep::asText(const Type::_v type)
throw() {
static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait", "Cmd" };
return text [type];
}
-anna::xml::Node* TestStep::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestStep::asXML(anna::xml::Node* parent)
throw() {
anna::xml::Node* result = parent->createChild("TestStep");
return result;
}
-std::string TestStep::asXMLString() const throw() {
+std::string TestStep::asXMLString() throw() {
anna::xml::Node root("root");
return anna::xml::Compiler().apply(asXML(&root));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// TestStepTimeout
////////////////////////////////////////////////////////////////////////////////////////////////////////
-anna::xml::Node* TestStepTimeout::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestStepTimeout::asXML(anna::xml::Node* parent)
throw() {
anna::xml::Node* result = TestStep::asXML(parent); // end timestamp will be 0 if test finished OK
//parent->createChild("TestStepTimeout");
////////////////////////////////////////////////////////////////////////////////////////////////////////
// TestStepSendxml
////////////////////////////////////////////////////////////////////////////////////////////////////////
-TestStepSendxml::~TestStepSendxml() {
- delete a_messageCodec;
- a_messageCodec = NULL;
-}
-
-anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent)
throw() {
anna::xml::Node* result = TestStep::asXML(parent);
//parent->createChild("TestStepSendxml");
}
}
- if (decodeMessage(a_message, a_messageCodec)) {
+ if (decodeMessage()) {
xmlmsg = "\n";
xmlmsg += a_messageCodec->asXMLString();
xmlmsg += "\n";
// Detailed log:
if(a_realmNode->logEnabled()) {
- if (!a_messageCodec)
- decodeMessage(a_message, a_messageCodec);
-
- if (a_messageCodec) {
+ if (decodeMessage()) {
std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
a_realmNode->writeLogFile(*a_messageCodec, (success ? "sent2e" : "send2eError"), detail);
}
// Detailed log:
if(a_realmNode->logEnabled()) {
- if (!a_messageCodec)
- decodeMessage(a_message, a_messageCodec);
-
- if (a_messageCodec) {
+ if (decodeMessage()) {
std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
a_realmNode->writeLogFile(*a_messageCodec, (success ? "sent2c" : "send2cError"), detail);
}
void TestStepSendxml::do_reset() throw() {
a_expired = false;
//a_message.clear();
+ //a_messageAlreadyDecoded = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// TestStepDelay
////////////////////////////////////////////////////////////////////////////////////////////////////////
-anna::xml::Node* TestStepDelay::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestStepDelay::asXML(anna::xml::Node* parent)
throw() {
anna::xml::Node* result = TestStep::asXML(parent);
//parent->createChild("TestStepDelay");
////////////////////////////////////////////////////////////////////////////////////////////////////////
// TestStepWait
////////////////////////////////////////////////////////////////////////////////////////////////////////
-TestStepWait::~TestStepWait() {
- delete a_messageCodec;
- a_messageCodec = NULL;
-}
-
void TestStepWait::setCondition(bool fromEntity,
- const std::string &code, const std::string &bitR, const std::string &resultCode, const std::string &sessionId,
- const std::string &hopByHop, const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() {
+ const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
+ const std::string &sessionId, const std::string &resultCode,
+ const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() {
a_condition.setReceivedFromEntity(fromEntity);
a_condition.setCode(code);
a_condition.setRegexp(regexp);
}
-anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent)
throw() {
anna::xml::Node* result = TestStep::asXML(parent);
//parent->createChild("TestStepWait");
}
}
- if (decodeMessage(a_message, a_messageCodec)) {
+ if (decodeMessage()) {
xmlmsg = "\n";
xmlmsg += a_messageCodec->asXMLString();
xmlmsg += "\n";
////////////////////////////////////////////////////////////////////////////////////////////////////////
// TestStepCmd
////////////////////////////////////////////////////////////////////////////////////////////////////////
-anna::xml::Node* TestStepCmd::asXML(anna::xml::Node* parent) const
+anna::xml::Node* TestStepCmd::asXML(anna::xml::Node* parent)
throw() {
anna::xml::Node* result = TestStep::asXML(parent);
//parent->createChild("TestStepCmd");
// Project
#include <anna/core/DataBlock.hpp>
#include <anna/xml/Node.hpp>
+#include <anna/diameter/codec/Message.hpp>
// Process
#include <TestCondition.hpp>
struct Type { enum _v { Unconfigured, Timeout, Sendxml2e, Sendxml2c, Delay, Wait, Cmd }; };
static const char* asText(const Type::_v type) throw();
- TestStep(TestCase *testCase) { initialize(testCase); }
+ TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL) { initialize(testCase); }
virtual ~TestStep() {;}
// setter & getters
void complete() throw();
void reset() throw();
void next() throw();
- virtual anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
- std::string asXMLString() const throw();
+ virtual anna::xml::Node* asXML(anna::xml::Node* parent) throw();
+ std::string asXMLString() throw();
protected:
TestCase *a_testCase;
bool a_completed;
Type::_v a_type;
+ // Message (not for all step types)
+ anna::DataBlock a_message;
+ anna::diameter::codec::Message *a_messageCodec; // used as helper and for traffic logs
+ bool decodeMessage() throw();
+
virtual bool do_execute() throw() = 0; // returns true if next step must be executed
virtual void do_complete() throw() = 0; // end of transaction (delay/timeout expired, wait condition fulfilled, sending done)
// In all cases, the next step will be executed except 'timeout' which is asynchronous
bool do_execute() throw();
void do_complete() throw(); // timeout reached, test case failed
void do_reset() throw();
- anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+ anna::xml::Node* asXML(anna::xml::Node* parent) throw();
};
// Step number reference ('wait for request' step)
int a_waitForRequestStepNumber;
- // Message
- anna::DataBlock a_message;
- anna::diameter::codec::Message *a_messageCodec; // used as helper and for traffic logs
-
// Expired ?
bool a_expired; // a_endTimestamp will be the expiration reception timestamp
public:
- TestStepSendxml(TestCase *testCase) : TestStep(testCase), a_message(true), a_messageCodec(NULL), a_expired(false), a_realmNode(NULL), a_waitForRequestStepNumber(-1) {;}
- ~TestStepSendxml();
+ TestStepSendxml(TestCase *testCase) : TestStep(testCase),
+ a_expired(false),
+ a_realmNode(NULL),
+ a_waitForRequestStepNumber(-1) {;}
+ ~TestStepSendxml() {;}
// setter & getters
void setRealmNode(RealmNode *realm) throw() { a_realmNode = realm; }
bool do_execute() throw();
void do_complete() throw() {;}
void do_reset() throw();
- anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+ anna::xml::Node* asXML(anna::xml::Node* parent) throw();
};
class TestStepSendxml2e : public TestStepSendxml {
bool do_execute() throw();
void do_complete() throw(); // delay reached
void do_reset() throw();
- anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+ anna::xml::Node* asXML(anna::xml::Node* parent) throw();
};
class TestStepWait : public TestStep {
TestCondition a_condition;
- anna::DataBlock a_message; // message which complies with condition
- anna::diameter::codec::Message *a_messageCodec; // used as helper and for traffic logs
anna::diameter::comm::ClientSession *a_clientSession;
anna::diameter::comm::ServerSession *a_serverSession;
public:
- TestStepWait(TestCase *testCase) : TestStep(testCase), a_message(true), a_messageCodec(NULL) { a_type = Type::Wait; a_clientSession = NULL; a_serverSession = NULL; }
- ~TestStepWait();
+ TestStepWait(TestCase *testCase) : TestStep(testCase) {
+ a_type = Type::Wait;
+ a_clientSession = NULL;
+ a_serverSession = NULL;
+ }
+ ~TestStepWait() {;}
// setter & getters
void setCondition(bool fromEntity,
- const std::string &code, const std::string &bitR, const std::string &resultCode, const std::string &sessionId,
- const std::string &hopByHop, const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw();
+ const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
+ const std::string &sessionId, const std::string &resultCode,
+ const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw();
void setCondition(bool fromEntity, const std::string ®exp) throw();
void setClientSession(anna::diameter::comm::ClientSession *cs) throw() { a_clientSession = cs; }
bool do_execute() throw(); // this will be executed when test case starts (at least we could measure the time until condition is fulfilled)
void do_complete() throw(); // condition fulfilled
void do_reset() throw();
- anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+ anna::xml::Node* asXML(anna::xml::Node* parent) throw();
};
bool do_execute() throw();
void do_complete() throw();
void do_reset() throw();
- anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+ anna::xml::Node* asXML(anna::xml::Node* parent) throw();
};
#include <anna/time/functions.hpp>
#include <anna/time/Date.hpp>
-#include <anna/diameter/codec/tme/Engine.hpp>
+#include <anna/diameter/codec/Engine.hpp>
#include <anna/diameter/codec/tme/Message.hpp>
anna::time::functions::setControlPoint(); // start control point (application lifetime)
anna::diameter::stack::Engine & stackEngine = anna::diameter::stack::Engine::instantiate();
anna::diameter::stack::Dictionary *dictionary;
- anna::diameter::codec::tme::Engine *codecEngine = new anna::diameter::codec::tme::Engine();
std::string exec = argv[0];
std::string param = argv[1] ? argv[1] : "";
dictionary->load(param + "/avps_tgpp.xml");
dictionary->load(param + "/avps_tme.xml");
dictionary->load(param + "/commands_dccaOCS-CS_HuaweiNGIN_de-es.xml");
- codecEngine->setDictionary(dictionary);
// Trace:
LOGINFORMATION(Logger::information(stackEngine.asString(), ANNA_FILE_LOCATION));
LOGDEBUG(Logger::debug(dictionary->asString(), ANNA_FILE_LOCATION));
- LOGDEBUG(Logger::debug(codecEngine->asString(), ANNA_FILE_LOCATION));
} catch(anna::RuntimeException &ex) {
ex.trace();
std::cout << ex.getText() << std::endl;
}
// Build CCA
- tmeMessage cca;
+ anna::diameter::codec::Engine *codecEngine = new anna::diameter::codec::Engine("TME Engine", dictionary);
+ LOGDEBUG(Logger::debug(codecEngine->asString(), ANNA_FILE_LOCATION));
+ tmeMessage cca(codecEngine);
cca.setId("Credit-Control-Answer");
tmeAvp *sid = (tmeAvp*)cca.addAvp("Session-Id");
tmeAvp *ohst = (tmeAvp*)cca.addAvp("Origin-Host");
/**
* Default constructor
- @param engine Codec engine used
+ * @param engine Codec engine used
*/
Avp(Engine *engine = NULL);
/**
* Identified constructor
* @param id Avp identifier as pair (code,vendor-id).
- @param engine Codec engine used
+ * @param engine Codec engine used
*/
Avp(AvpId id, Engine *engine = NULL);
- /** Sets the codec engine */
- void setEngine(Engine *engine) throw() { a_engine = engine; }
+ /**
+ * Sets the codec engine
+ *
+ * Once assigned (here or at constructor), this method SHALL NOT be used anymore.
+ * Also, the associated dictionary SHOULD NOT BE CHANGED through the engine,
+ * unless you know what are you doing. If you want to reconfigure the engine,
+ * first #clear the avp and then you could reuse the same object with
+ * different configurations (execution contexts).
+ *
+ * Setting a new different engine with different stack, even same engine where the
+ * stack has been dynamically changed, could cause a bad behaviour depending on the
+ * changes: in general, if the dictionary grows, nothing bad will happen, but if
+ * you remove or modified some elements which were processed with a certain format,
+ * will be interpreted as 'unknown' with the new dictionary, and then some problems
+ * may occur. If you add elements (vendors, avps, messages) is not a problem.
+ */
+ void setEngine(Engine *engine) throw();
// Length references
/**
Adds an avp child providing its identifier and reserve internal memory it.
- An exception is launched is the Avp is not a grouped avp.
+ An exception is launched is the Avp over which we add the new avp, is not a grouped avp.
@param id Avp identifier as pair (code,vendor-id).
*/
Avp * addAvp(const char *name) throw(anna::RuntimeException);
-
/**
Adds an avp child providing a persistent pointer (must be maintained by application).
- An exception is launched is the Avp is not a grouped avp.
+ An exception is launched is the Avp over which we add the new avp, is not a grouped avp.
+ It is not allowed to add an avp with no codec engine configured, neither if the engine
+ is not the same.
@param avp Avp external pointer. If NULL provided, nothing is done and NULL returned.
+ Also NULL returned for bad engine configuration.
@return Pointer to the added avp (again).
*/
- Avp * addAvp(Avp * avp) throw(anna::RuntimeException) { if(!avp) return NULL; addChild(avp); return avp; }
+ Avp * addAvp(Avp * avp) throw(anna::RuntimeException);
+
// Data part access
/** Access content for OctetString Avp in order to set data part */
Constructor
@param className Logical name for the class.
*/
- Engine(const char *className) : EngineImpl(className) {;}
+ Engine(const char *className, const stack::Dictionary * dictionary) : EngineImpl(className, dictionary) {;}
void releaseAvp(Avp* avp) throw() {
if(avp == NULL) return;
namespace codec {
-extern const char *MessageDTD;
-
class Message;
class Avp;
* A child implementation could manage complex Avp classes with new data-part formats. Grouped ones could
* allocate new complex Avps through such engine which knows how to allocate this special Avp's (also for
* complex Message classes with application-specific setters and getters as credit-control related avps,
- * or some another context items). For example helpers for TME scope stands for a new engine component
- * called tme::Engine, allocating tme::Avp and tme::Message classes which support three new Avp formats:
+ * or some another context items). For example tme::Avp and tme::Message classes support three new Avp formats:
* ISDNNumber, ISDNAddress and Unsigned16. Anyway, main Message/Avp and Engine classes stand for all contexts
* included in anna::diameter, that is to say, whole contexts (at the time only TME) will be included in future
* when needed apart from the independent namespace version. Thank to this, single threaded applications could
* use whole engine in a easy way.
*
- * Usually an engine component is associated to a single diameter stack, although single threaded processes could
- * use a common engine alternating different stack dictionaries by mean #setDictionary, depending on which kind of
- * messages are being analyzed. Although the application must ensure that a single dictionary is activated during
- * the same context operations an Avp could be considered as Unknown if was created with another, and we could
- * have validation problems (i.e. if mandatory Avp bit is enabled). In general, managing Unknown data-part format
- * don't have to be a problem because it is interpreted as OctetString format. Depending on what setters/getters
- * we use, it could reach a RuntimeException at our application.
- *
- * At multithread processes we must use one heir engine per stack and never switching stacks within same component.
- * We will use each engine for each context.
+ * An engine component is associated to a single diameter stack. It is application responsability to use message
+ * container initialized with the correct codec engine depending on the context. There are helpers to do this at
+ * anna::diameter::codec::functions::getApplicationId (from xml document or hexadecimal buffer).
*
* It is recommended to use Message class to create Avps (adding them through pair identification <code + vendor-id>
* prototype), but we could create Avps separately (other program section, i.e) and join them after:
* 1. Recommended way:
*
* // Message creation:
- * Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer);
+ * Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer, codecEngine);
* // Adding + creation:
* Avp * avp_sid = msg->addAvp(helpers::base::AVPID__Session_Id);
* Avp * avp_oh = msg->addAvp(helpers::base::AVPID__Origin_Host);
* 2. External Avp creation:
*
* // Message creation:
- * Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer);
+ * Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer, codecEngine);
* // Creation:
- * Avp * avp_sid = new Avp(helpers::base::AVPID__Session_Id);
- * Avp * avp_oh = new Avp(helpers::base::AVPID__Origin_Host);
- * Avp * avp_or = new Avp(helpers::base::AVPID__Origin_Realm);
- * Avp * avp_rc = new Avp(helpers::base::AVPID__Result_Code);
+ * Avp * avp_sid = new Avp(helpers::base::AVPID__Session_Id, codecEngine);
+ * Avp * avp_oh = new Avp(helpers::base::AVPID__Origin_Host, codecEngine);
+ * Avp * avp_or = new Avp(helpers::base::AVPID__Origin_Realm, codecEngine);
+ * Avp * avp_rc = new Avp(helpers::base::AVPID__Result_Code, codecEngine);
* // Adding:
* msg->addAvp(avp_sid);
* msg->addAvp(avp_oh);
private:
- anna::xml::DTDMemory a_dtd;
ValidationDepth::_v a_validationDepth;
ValidationMode::_v a_validationMode;
bool a_singleFailedAVP;
bool a_ignoreFlags;
FixMode::_v a_fixMode;
- bool a_selectStackWithApplicationId; // default behaviour: let the user switch the stack (false for this boolean)
-
// Auxiliary
const stack::Dictionary * a_dictionary;
/** Constructor
@param className Logical name for the class.
+ @param dictionary Diameter dictionary. At single threaded processes, the same codec engine could be used with
+ different diameter dictionaries (multi-stack applications). In that case the process must switch the stack for
+ the whole decoding or enconding operation over a Message depending on the context (normally the message header
+ Application-Id is used as stack identifier). But the smart way implies inherit from this engine creating a
+ component for each diameter stack managed in the application. Inheritance is mandatory in multi-threaded processes:
+ one engine, a unique stack.
*/
- EngineImpl(const char* className);
+ EngineImpl(const char* className, const stack::Dictionary * dictionary);
/**
* Destructor
virtual ~EngineImpl() {;}
- // setters
- /**
- Sets diameter dictionary loaded at stack engine. It's recommended to configure a valid dictionary
- (if not, or NULL provided at #setDictionary, all avps will be managed as 'Unknown' format and all
- items will need to be manually updated, i.e. message and avp flags).
-
- @param dictionary Diameter dictionary. At single threaded processes, the same codec engine could be used with
- different diameter dictionaries (multi-stack applications). In that case the process must switch the stack for
- the whole decoding or enconding operation over a Message depending on the context (normally the message header
- Application-Id is used as stack identifier). But the smart way implies inherit from this engine creating a
- component for each diameter stack managed in the application. Inheritance is mandatory in multi-threaded processes:
- one engine, a unique stack.
- */
- void setDictionary(const stack::Dictionary * dictionary) throw() { a_dictionary = dictionary; }
-
- /**
- * Sets diameter dictionary loaded at stack engine with the provided identifier.
- *
- * @param stackId Stack identifier. When missing, NULL will be returned
- * @return Returns configured dictionary (NULL if stack id was not found)
- */
- const stack::Dictionary *setDictionary(unsigned int stackId) throw();
-
-
- /**
- * By default, the user will select the appropiate stack id depending on the context (see #setDictionary), but
- * some applications could consider interesting automatic stack selection based on managed messages (incoming
- * decoded ones, or built messages to be encoded). By default, on engine construction, no changes are done.
- * Multithreaded processes should have a unique codec engine for each managed stack (then you don't have to
- * worry about), but mono processes with multistack implementation over the same-unique engine, should activate
- * this to have the commonly recommended way to choose the stack: using the Application-Id value.
- *
- * @warning do not activate in case of multithreaded applications.
- * @warning must register the base protocol stack (with id = 0 = application-id) to manage base protocol messages.
- * @param enable Activates/deactivates the stack selection from the Application-Id value within the message header.
- * False by default on engine construction.
- */
- void selectStackWithApplicationId (bool enable) throw() { a_selectStackWithApplicationId = enable; }
-
- // getters
-
- /**
- Gets the currently configured behaviour regarding stack selection for multistack codec engines in mono thread
- applications.
-
- @return True if selection is done with the Application-Id. False (default) if no selection is performed (user responsibility).
- */
- bool hasSelectStackWithApplicationId (void) throw() { return a_selectStackWithApplicationId; }
-
-
/**
Gets currently configured dictionary. NULL if not configured (manual encode/decode operations).
*/
bool getSingleFailedAVP() const throw() { return a_singleFailedAVP; }
- /**
- DTD document for xml message parsing
- */
- const anna::xml::DTDMemory & getDTD() const throw() { return a_dtd; }
-
/**
* Creates a new diameter avp assigning its identifier, using engine resources to allocate memory (recommended
* recycler allocation at engine component re-implementation of allocator methods). Obviously, normal objects
--- /dev/null
+// 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_codec_EngineManager_hpp
+#define anna_diameter_codec_EngineManager_hpp
+
+
+// Project
+#include <anna/core/Singleton.hpp>
+#include <anna/diameter/defines.hpp>
+
+// Standard
+#include <map>
+
+
+namespace anna {
+
+namespace diameter {
+
+namespace codec {
+
+class Engine;
+
+typedef std::map<anna::diameter::ApplicationId, Engine*> appid_codec_engines_t;
+typedef std::map<anna::diameter::ApplicationId, Engine*>::const_iterator appid_codec_engines_it;
+typedef std::map<anna::diameter::ApplicationId, Engine*>::iterator appid_codec_engines_nc_it;
+
+/**
+ * Helper class to centralize application codec engines and ease the automatic codec engine selection from
+ * codec resources (mainly Message class, but also Avp).
+ *
+ * This is useful if you enable the auto selection from messages application id, avoiding to pre-initialize
+ * such codec messages with a specific engine before interpreting the data sources or building.
+ * At multithread application, it is recommended to specialize the message codec for each stack without using
+ * this engine manager.
+ */
+class EngineManager : public anna::Singleton <EngineManager> {
+
+private:
+
+ bool a_autoSelectFromApplicationId;
+ appid_codec_engines_t a_appid_codec_engines;
+
+ // private constructor
+ EngineManager() : a_autoSelectFromApplicationId(true) {};
+
+public:
+
+ /**
+ * First element iterator
+ */
+ appid_codec_engines_it begin() const throw() { return a_appid_codec_engines.begin(); }
+
+ /**
+ * Last element iterator
+ */
+ appid_codec_engines_it end() const throw() { return a_appid_codec_engines.end(); }
+
+ /**
+ * Number of registered engines
+ */
+ int size() const throw() { return a_appid_codec_engines.size(); }
+
+ /**
+ * Registers a new codec engine (externally allocated) associated to an application id.
+ * If the application id exists, the new engine pointer will replace the existing one.
+ *
+ * @param appid Application-Id
+ * @param engine Associated codec engine
+ */
+ void registerCodecEngine(const ApplicationId &appid, Engine* engine) throw();
+
+ /**
+ * Get the associated codec engine for a provided Application-Id.
+ *
+ * @param appid Application-Id
+ *
+ * @return Found codec engine, NULL if not found
+ */
+ Engine *getCodecEngine(const ApplicationId &appid) const throw();
+
+ /**
+ * If only one codec engine is registered (mono-stack application), it will be returned
+ * (NULL in other case). This is because mono-stack applications could merge compatible
+ * stacks into one unique dictionary, using a global value for stack id with no relation
+ * regarding application-id concept.
+ *
+ * @return Unique codec engine, NULL if not unique (empty manager or more than one)
+ */
+ Engine *getMonoStackCodecEngine() const throw() {
+ return ((size() != 1) ? NULL : begin()->second);
+ }
+
+ /**
+ * The user could select the appropriate codec engine depending on the context, but some applications could
+ * consider interesting automatic codec engine selection from managed messages (decoded from any source,
+ * or built from scratch). Then, this engine manager will be used registering all the supported stacks
+ * with their corresponding codec engines, and automatic selection will be done during decoding of any
+ * supported source of data (hexadecimal buffers, xml documents) or build operations.
+ *
+ * If the user pre-initializes the codec engine for a #Message or #Avp element, this selection is ignored
+ * even if enabled: the codec engine only can be established one time, for security reasons.
+ *
+ * @param enable Activates/deactivates the codec engine selection from the Application-Id value. True by default
+ * and applicable when this manager store is not empty.
+ */
+ void selectFromApplicationId (bool enable) throw() { a_autoSelectFromApplicationId = enable; }
+
+ /**
+ Gets the currently configured behaviour regarding codec engine selection.
+
+ @return True if selection is done with the Application-Id (default behaviour). False to disable (the manager
+ could be used for some other things which could be also interesting).
+ */
+ bool selectFromApplicationId (void) throw() { return a_autoSelectFromApplicationId; }
+
+
+ friend class anna::Singleton <EngineManager>;
+};
+
+}
+}
+}
+
+#endif
+
Message(CommandId id, Engine *engine = NULL);
- /** Sets the codec engine */
- void setEngine(Engine *engine) throw() { a_engine = engine; }
+ /**
+ * Sets the codec engine.
+ *
+ * Once assigned (here or at constructor), this method SHALL NOT be used anymore.
+ * Also, the associated dictionary SHOULD NOT BE CHANGED through the engine,
+ * unless you know what are you doing. If you want to reconfigure the engine,
+ * first #clear the message and then you could reuse the same object with
+ * different configurations (execution contexts).
+ *
+ * Setting a new different engine with different stack, even same engine where the
+ * stack has been dynamically changed, could cause a bad behaviour depending on the
+ * changes: in general, if the dictionary grows, nothing bad will happen, but if
+ * you remove or modified some elements which were processed with a certain format,
+ * will be interpreted as 'unknown' with the new dictionary, and then some problems
+ * may occur. If you add elements (vendors, avps, messages) is not a problem.
+ */
+ void setEngine(Engine *engine) throw();
// Length references
@warning Request provided must be a request, in other case method do nothing.
*/
- void setHeaderToAnswer(const Message &request) throw() {
+ void setHeaderToAnswer(const Message &request) throw(anna::RuntimeException) {
if(!request.getId().second) return;
+ a_engine = request.getEngine(); // we know this will be
+
setId(CommandId(request.getId().first, !request.getId().second), false /* don't clear */);
setVersion(request.getVersion());
setApplicationId(request.getApplicationId());
@warning Request provided must be a request, in other case method do nothing.
*/
- void setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw();
+ void setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw(anna::RuntimeException);
/**
/**
Adds an avp child providing a persistent pointer (must be maintained by application).
+ It is not allowed to add an avp with no codec engine configured, neither if the engine
+ is not the same.
@param avp Avp external pointer. If NULL provided, nothing is done and NULL returned.
+ Also NULL returned for bad engine configuration.
@return Pointer to the added avp (again).
*/
- Avp * addAvp(Avp * avp) throw() { if(!avp) return NULL; addChild(avp); return avp; }
+ Avp * addAvp(Avp * avp) throw();
/**
/**
* Clears and initializes Message class information.
- * Application must clear auxiliary message objects before adding Avps in a new context.
+ * Application must clear auxiliary message objects before adding Avps in a new context if the same object is reused.
* Application don't need to clear a message object before decode operation (decode invokes #clear before any other task).
* Any reimplementation must first invoke base class method.
*/
*/
bool valid(Message *ptrAnswer = NULL) const throw(anna::RuntimeException);
-
/**
Interpret xml data in order to dump over the class content.
- \param messageNode Message root node
+ \param messageNode Message root node obtained from @functions::xmlFileTo
*/
void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
/**
- Interpret xml string representation in order to dump over the class content.
- DTD validation is used in the same way that #loadXML does.
- \param xmlString XML string representation with relevant information for this instance
- */
- void fromXMLString(const std::string &xmlString) throw(anna::RuntimeException);
-
- /**
- Loads an xml file based on this message DTD (could be accumulative, no initialization will be performed by this method).
-
- <pre>
- <!ELEMENT message (avp*)>
- <!ELEMENT avp (avp*)>
-
- <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>
- <!--
- version: Diameter version. Sets '1' by default
- name: Command name within working stack (dictionary identifier)
-
- In order to get more coding capabilities, command code and flags could be established instead of former command name,
- but neither of them are allowed if 'name' is provided (and vice versa):
-
- code: Command code
- flags: Command flags byte value (0-255) where standard bit set for flags is 'RPET rrrr': (R)equest, (P)roxiable, (E)rror, Potentially re-(T)ransmitted message and (r)eserved
-
-
- application-id: Message application id
- hop-by-hop-id: Message hop by hop id. Sets '0' by default
- end-by-end-id: Message end by end id. Sets '0' by default
- -->
-
- <!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED>
- <!--
- name: Avp name within working stack (dictionary identifier)
-
- In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,
- but neither of them are allowed if 'name' is provided (and vice versa):
-
- code: Avp code
- vendor-code: Avp vendor code
- flags: Avp flags byte value (0-255) where standard bit set for flags is 'VMPr rrrr': (V)endor-specific, (M)andatory, end to end encry(P)tion and r(eserved)
-
-
- data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also
- useful for certain formats which could be easily understandable in such friendly/smart representation. We will
- achieve different human-readable strings depending on data format:
-
- [ OctetString ] (if printable, but not recommended)
- [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)
- [ Time ] (NTP timestamp, normal number representation)
- [ Address ] (auto detects IPv4 or IPv6 address version, then only ip address is specified: IPv4 with dots, IPv6 with colons)
- [ UTF8String, DiameterIdentity, DiameterURI ] (printable)
- [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)
-
- New application formats must define specific natural representation for internal raw data
-
- hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind
- of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable
- data and should better be encoded within this field although being printable. Unknown avps (which fails identifying
- provided name or code/vendor-code) must always use this representation.
-
- Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,
- OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport
- readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).
- Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,
- because xml is read by humans with testing and monitoring purposes.
- -->
- </pre>
-
- @param xmlPathFile Complete path file to the xml document which represents the diameter message
- @see fromXMLString
- */
- void loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException);
-
+ * Interpret a xml file in order to create a diameter message
+ *
+ * @see functions::messageXmlDocumentFromXmlFile
+ * @see fromXML
+ *
+ * @param xmlPathFile Complete path file to the xml document which represents the diameter message
+ */
+ void loadXML(const std::string &xmlPathFile) throw(anna::RuntimeException);
// getters
#define anna_diameter_codec_functions_hpp
-// Local
+// Project
#include <anna/diameter/defines.hpp>
-
#include <anna/core/RuntimeException.hpp>
+#include <anna/xml/xml.hpp>
// STL
#include <string>
namespace codec {
+static const char *MessageDTD = "\
+<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
+<!-- Diameter message DTD -->\n\
+\n\
+<!ELEMENT message (avp*)>\n\
+<!ELEMENT avp (avp*)>\n\
+\n\
+<!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED p-bit (yes | no) #IMPLIED e-bit (yes | no) #IMPLIED t-bit (yes | no) #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>\n\
+<!--\n\
+ version: Diameter version. Sets '1' by default\n\
+ name: Command name within working stack (dictionary identifier)\n\
+ p-bit: (P)roxiable bit flag (yes, no). By default is 'no'\n\
+ e-bit: (E)rror bit flag (yes, no). By default is 'no'\n\
+ t-bit: Potentially re-(T)ransmitted bit flag (yes, no). By default is 'no'\n\
+\n\
+ In order to get more coding capabilities, command code and flags could be established instead of former fields,\n\
+ but neither of them are allowed if the other are present (and vice versa):\n\
+\n\
+ code: Command code\n\
+ flags: Command flags byte value (0-255) where standard bit set for flags is 'RPET rrrr': (R)equest, (P)roxiable, (E)rror, Potentially re-(T)ransmitted message and (r)eserved\n\
+\n\
+\n\
+ application-id: Message application id\n\
+ hop-by-hop-id: Message hop by hop id. Sets '0' by default\n\
+ end-by-end-id: Message end by end id. Sets '0' by default\n\
+-->\n\
+\n\
+<!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED alias CDATA #IMPLIED>\n\
+<!--\n\
+ name: Avp name within working stack (dictionary identifier)\n\
+\n\
+ In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,\n\
+ but neither of them are allowed if 'name' is provided (and vice versa):\n\
+\n\
+ code: Avp code\n\
+ vendor-code: Avp vendor code\n\
+ flags: Avp flags byte value (0-255) where standard bit set for flags is 'VMPr rrrr': (V)endor-specific, (M)andatory, end to end encry(P)tion and r(eserved)\n\
+ alias: Descriptive/helper field for certain numeric data values. Aliases are defined at diameter dictionary, but are ignored (not checked) at xml message parsing\n\
+ The reason to include it in dtd definition, is because xml messages traced by the diameter codec could add alias field for some Avps\n\
+\n\
+\n\
+ data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also\n\
+ useful for certain formats which could be easily understandable in such friendly/smart representation. We will\n\
+ achieve different human-readable strings depending on data format:\n\
+\n\
+ [ OctetString ] (if printable, but not recommended)\n\
+ [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)\n\
+ [ Time ] (NTP timestamp, normal number representation)\n\
+ [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.\n\
+ Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for\n\
+ IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.\n\
+ Currently, only IPv4, IPv6 and E164 address types have a known printable presentation, anyway using printable format\n\
+ for another types will encode the address value directly as E164 does)\n\
+ [ UTF8String, DiameterIdentity, DiameterURI ] (printable)\n\
+ [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)\n\
+\n\
+ New application formats must define specific natural representation for internal raw data\n\
+\n\
+ hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind\n\
+ of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable\n\
+ data and should better be encoded within this field although being printable. Unknown avps (which fails identifying\n\
+ provided name or code/vendor-code) must always use this representation.\n\
+\n\
+ Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,\n\
+ OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport\n\
+ readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).\n\
+ Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,\n\
+ because xml is read by humans with testing and monitoring purposes.\n\
+-->\n\
+\n\
+";
+
+
+
+
// Used for alarms, tracing and Failed-AVP construction:
typedef struct parent {
/**
- * Decodes a Command Header. This helper cannot check boundaries. start pointer must be a valid command context.
- *
- * @param start Must be a valid command start (point to the command version byte).
- * @param version Diameter version.
- * @param length Message length.
- * @param flags Command flags.
- * @param id Command identification (code, request<true,false>).
- * @param appId Application-ID.
- * @param hbh Hop-by-Hop Identifier.
- * @param ete End-to-End Identifier.
- */
+ * Decodes a Command Header. This helper cannot check boundaries. start pointer must be a valid command context.
+ *
+ * @param start Must be a valid command start (point to the command version byte).
+ * @param version Diameter version.
+ * @param length Message length.
+ * @param flags Command flags.
+ * @param id Command identification (code, request<true,false>).
+ * @param appId Application-ID.
+ * @param hbh Hop-by-Hop Identifier.
+ * @param ete End-to-End Identifier.
+ */
static void decodeCommandHeader(const char *start, char & version, U24 & length, char & flags, CommandId & id, int & appId, int & hbh, int & ete) throw(anna::RuntimeException);
/**
- * Decodes an AVP. This helper cannot check boundaries. start pointer must be a valid avp context.
- *
- * @param start Must be a valid avp start (point to the 32-bits avp code word).
- * @param id Avp identification (code, vendorId).
- * @param flags Avp flags byte.
- * @param length Avp length (includes code, flags, length itself, vendorId if exists and data length).
- * @param data Avp data part.
- */
+ * Decodes an AVP. This helper cannot check boundaries. start pointer must be a valid avp context.
+ *
+ * @param start Must be a valid avp start (point to the 32-bits avp code word).
+ * @param id Avp identification (code, vendorId).
+ * @param flags Avp flags byte.
+ * @param length Avp length (includes code, flags, length itself, vendorId if exists and data length).
+ * @param data Avp data part.
+ */
static void decodeAVP(const char *start, AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException);
/**
- * Gets the next AVP pointer reference starting from a first-avp data block. It could be the first avp within
- * a command, or within an grouped avp.
- *
- * @param avpsDB AVP data block buffer pointer
- * @param avpsLen AVP data block buffer length
- * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
- *
- * @return Pointer to the next AVP found. NULL if no more.
- */
+ * Gets the next AVP pointer reference starting from a first-avp data block. It could be the first avp within
+ * a command, or within an grouped avp.
+ *
+ * @param avpsDB AVP data block buffer pointer
+ * @param avpsLen AVP data block buffer length
+ * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
+ *
+ * @return Pointer to the next AVP found. NULL if no more.
+ */
static const char * nextAVP(const char *avpsDB, int avpsLen, const char *start) throw(anna::RuntimeException);
-// /**
-// * Gets the next AVP pointer reference starting from a first-avp datablock. It could be the first avp within
-// * a command, or within an grouped avp.
-// *
-// * @param avpsDB AVPs set as datablock
-// * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
-// *
-// * @return Pointer to the next AVP found. NULL if no more.
-// */
-// static const char * nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException);
+ // /**
+ // * Gets the next AVP pointer reference starting from a first-avp datablock. It could be the first avp within
+ // * a command, or within an grouped avp.
+ // *
+ // * @param avpsDB AVPs set as datablock
+ // * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word).
+ // *
+ // * @return Pointer to the next AVP found. NULL if no more.
+ // */
+ // static const char * nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException);
/**
- * Gets the next AVP pointer reference within an AVPs set data block with a certain AVP identification.
- *
- * @param avpsDB AVP data block buffer pointer
- * @param avpsLen AVP data block buffer length
- * @param id Avp identification (code, vendorId).
- * @param n Ocurrence number (first avp, second avp, etc.). 1 by default.
- *
- * @return Pointer to first AVP found with identification provided. NULL if not found.
- */
+ * Gets the next AVP pointer reference within an AVPs set data block with a certain AVP identification.
+ *
+ * @param avpsDB AVP data block buffer pointer
+ * @param avpsLen AVP data block buffer length
+ * @param id Avp identification (code, vendorId).
+ * @param n Ocurrence number (first avp, second avp, etc.). 1 by default.
+ *
+ * @return Pointer to first AVP found with identification provided. NULL if not found.
+ */
static const char *findAVP(const char *avpsDB, int avpsLen, const diameter::AvpId & id, int n = 1) throw(anna::RuntimeException);
-// /**
-// * Gets the next AVP pointer reference within an AVPs set datablock with a certain AVP identification.
-// *
-// * @param avpsDB AVPs set as datablock
-// * @param id Avp identification (code, vendorId).
-// * @param n Ocurrence number (first avp, second avp, etc.). 1 by default.
-// *
-// * @return Pointer to first AVP found with identification provided. NULL if not found.
-// */
-// static const char * findAVP(const anna::DataBlock & avpsDB, const AvpId & id, int n = 1) throw(anna::RuntimeException);
+ // /**
+ // * Gets the next AVP pointer reference within an AVPs set datablock with a certain AVP identification.
+ // *
+ // * @param avpsDB AVPs set as datablock
+ // * @param id Avp identification (code, vendorId).
+ // * @param n Ocurrence number (first avp, second avp, etc.). 1 by default.
+ // *
+ // * @return Pointer to first AVP found with identification provided. NULL if not found.
+ // */
+ // static const char * findAVP(const anna::DataBlock & avpsDB, const AvpId & id, int n = 1) throw(anna::RuntimeException);
static void setHopByHop(anna::DataBlock &, HopByHop) throw(anna::RuntimeException);
static void setEndToEnd(anna::DataBlock &, EndToEnd) throw(anna::RuntimeException);
static void setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate = true) throw(anna::RuntimeException);
+
+
+ /**
+ Interpret a xml file in order to create a memory xml document.
+ The xml file is based on this message DTD:
+
+ <pre>
+ <!ELEMENT message (avp*)>
+ <!ELEMENT avp (avp*)>
+
+ <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>
+ <!--
+ version: Diameter version. Sets '1' by default
+ name: Command name within working stack (dictionary identifier)
+
+ In order to get more coding capabilities, command code and flags could be established instead of former command name,
+ but neither of them are allowed if 'name' is provided (and vice versa):
+
+ code: Command code
+ flags: Command flags byte value (0-255) where standard bit set for flags is 'RPET rrrr': (R)equest, (P)roxiable, (E)rror, Potentially re-(T)ransmitted message and (r)eserved
+
+
+ application-id: Message application id
+ hop-by-hop-id: Message hop by hop id. Sets '0' by default
+ end-by-end-id: Message end by end id. Sets '0' by default
+ -->
+
+ <!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED>
+ <!--
+ name: Avp name within working stack (dictionary identifier)
+
+ In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,
+ but neither of them are allowed if 'name' is provided (and vice versa):
+
+ code: Avp code
+ vendor-code: Avp vendor code
+ flags: Avp flags byte value (0-255) where standard bit set for flags is 'VMPr rrrr': (V)endor-specific, (M)andatory, end to end encry(P)tion and r(eserved)
+
+
+ data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also
+ useful for certain formats which could be easily understandable in such friendly/smart representation. We will
+ achieve different human-readable strings depending on data format:
+
+ [ OctetString ] (if printable, but not recommended)
+ [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)
+ [ Time ] (NTP timestamp, normal number representation)
+ [ Address ] (auto detects IPv4 or IPv6 address version, then only ip address is specified: IPv4 with dots, IPv6 with colons)
+ [ UTF8String, DiameterIdentity, DiameterURI ] (printable)
+ [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)
+
+ New application formats must define specific natural representation for internal raw data
+
+ hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind
+ of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable
+ data and should better be encoded within this field although being printable. Unknown avps (which fails identifying
+ provided name or code/vendor-code) must always use this representation.
+
+ Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,
+ OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport
+ readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).
+ Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,
+ because xml is read by humans with testing and monitoring purposes.
+ -->
+ </pre>
+
+ @param xmlDocument XML document allocated by the user of the function (anna::xml::DocumentMemory xmlDocument)
+ @param xmlPathFile Complete path file to the xml document which represents the diameter message
+ @see messageXmlDocumentFromXmlString
+
+ @warning Whatever you will do with the xml document, will be only valid inside the scope of such xml document.
+ For example, you could load the document to be decoded over a codec Message by mean #Message::fromXML (using
+ the xml document #getRootNode) during document lifetime. After that, it could be destroyed.
+ */
+ static void messageXmlDocumentFromXmlFile(anna::xml::DocumentFile &xmlDocument, const std::string & xmlPathFile) throw(anna::RuntimeException);
+
+ /**
+ Interpret xml string representation in order to create a memory xml document.
+ DTD validation is used in the same way that #messageXmlDocumentFromXmlFile does.
+
+ @param xmlDocument XML document allocated by the user of the function (anna::xml::DocumentMemory xmlDocument)
+ @param xmlString XML string representation of the diameter message
+ @see messageXmlDocumentFromXmlFile
+
+ @warning Whatever you will do with the xml document, will be only valid inside the scope of such xml document.
+ For example, you could load the document to be decoded over a codec Message by mean #Message::fromXML (using
+ the xml document #getRootNode) during document lifetime. After that, it could be destroyed.
+ */
+ static void messageXmlDocumentFromXmlString(anna::xml::DocumentFile &xmlDocument, const std::string &xmlString) throw(anna::RuntimeException);
};
namespace codec {
+class Engine;
+
namespace tme {
class Avp;
class Message;
-class Engine;
+//class Engine;
using namespace helpers::tme::codectypes;
virtual void allocationByFormat(const anna::diameter::stack::Format *stackFormat) throw();
virtual void clearByFormat() throw();
-protected:
+//protected:
- /** Codec Engine getter: avoids have to create base engine when using its child */
- virtual anna::diameter::codec::Engine * getEngine() const throw(anna::RuntimeException);
+// /** Codec Engine getter: avoids have to create base engine when using its child */
+// virtual anna::diameter::codec::Engine * getEngine() const throw(anna::RuntimeException);
public:
/**
* Default constructor
+ * @param engine Codec engine used
*/
- Avp();
+ Avp(Engine *engine = NULL) : anna::diameter::codec::Avp(engine) {;}
/**
* Identified constructor
- * @param id Avp identifier as pair (code,vendorID).
+ * @param id Avp identifier as pair (code,vendor-id).
+ * @param engine Codec engine used
*/
- Avp(AvpId id);
+ Avp(AvpId id, Engine *engine = NULL) : anna::diameter::codec::Avp(id, engine) {;}
+
/**
* Destructor
friend class Message;
- friend class Engine;
+ friend class anna::diameter::codec::Engine;
};
}
+++ /dev/null
-// 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_codec_tme_Engine_hpp
-#define anna_diameter_codec_tme_Engine_hpp
-
-
-// STL
-#include <string>
-
-#include <anna/core/util/Recycler.hpp>
-
-#include <anna/diameter/codec/tme/Message.hpp>
-#include <anna/diameter/codec/tme/Avp.hpp>
-#include <anna/diameter/codec/EngineImpl.hpp>
-
-
-using namespace anna::diameter::codec::tme;
-
-
-//------------------------------------------------------------------------------
-//---------------------------------------------------------------------- #define
-//------------------------------------------------------------------------------
-
-
-namespace anna {
-
-namespace diameter {
-
-namespace codec {
-
-namespace tme {
-
-
-
-/**
- * Standard inheritance for engine component implementation, allocating basic Avp and Message classes.
- */
-class Engine : public EngineImpl {
-
-public:
-
- /**
- Constructor
- @param className Logical name for the class.
- */
- Engine(const char *className = "anna::diameter::codec::tme::Engine") : EngineImpl(className) {;}
-
- void releaseAvp(anna::diameter::codec::Avp* avp) throw() {
- if(avp == NULL) return;
-
- Avp* aux = static_cast <Avp*>(avp);
- aux->clear(); // free internal data-part storage specially for grouped avps which will release its childrens
- a_avps.release(aux);
- }
-
- void releaseMessage(anna::diameter::codec::Message* message) throw() {
- if(message == NULL) return;
-
- Message* aux = static_cast <Message*>(message);
- aux->clear(); // free internal data-part storage specially for childrens releasing
- a_messages.release(aux);
- }
-
-protected:
-
- anna::Recycler<Avp> a_avps;
- anna::Recycler<Message> a_messages;
-
- anna::diameter::codec::Avp* allocateAvp() throw() { return a_avps.create(); }
- anna::diameter::codec::Message* allocateMessage() throw() { return a_messages.create(); }
-
- friend class Message;
- friend class Avp;
-};
-
-}
-}
-}
-}
-
-#endif
-
--- /dev/null
+// 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_codec_tme_Engine_hpp
+#define anna_diameter_codec_tme_Engine_hpp
+
+
+// STL
+#include <string>
+
+#include <anna/core/util/Recycler.hpp>
+
+#include <anna/diameter/codec/tme/Message.hpp>
+#include <anna/diameter/codec/tme/Avp.hpp>
+#include <anna/diameter/codec/EngineImpl.hpp>
+
+
+using namespace anna::diameter::codec::tme;
+
+
+//------------------------------------------------------------------------------
+//---------------------------------------------------------------------- #define
+//------------------------------------------------------------------------------
+
+
+namespace anna {
+
+namespace diameter {
+
+namespace codec {
+
+namespace tme {
+
+
+
+/**
+ * Standard inheritance for engine component implementation, allocating basic Avp and Message classes.
+ */
+class Engine : public EngineImpl {
+
+public:
+
+ /**
+ Constructor
+ @param className Logical name for the class.
+ */
+ Engine(const char *className = "anna::diameter::codec::tme::Engine") : EngineImpl(className) {;}
+
+ void releaseAvp(anna::diameter::codec::Avp* avp) throw() {
+ if(avp == NULL) return;
+
+ Avp* aux = static_cast <Avp*>(avp);
+ aux->clear(); // free internal data-part storage specially for grouped avps which will release its childrens
+ a_avps.release(aux);
+ }
+
+ void releaseMessage(anna::diameter::codec::Message* message) throw() {
+ if(message == NULL) return;
+
+ Message* aux = static_cast <Message*>(message);
+ aux->clear(); // free internal data-part storage specially for childrens releasing
+ a_messages.release(aux);
+ }
+
+protected:
+
+ anna::Recycler<Avp> a_avps;
+ anna::Recycler<Message> a_messages;
+
+ anna::diameter::codec::Avp* allocateAvp() throw() { return a_avps.create(); }
+ anna::diameter::codec::Message* allocateMessage() throw() { return a_messages.create(); }
+
+ friend class Message;
+ friend class Avp;
+};
+
+}
+}
+}
+}
+
+#endif
+
namespace tme {
class Avp;
-class Engine;
+//class Engine;
/**
* Diameter TME message generic container
protected:
- /** Codec Engine getter: avoids have to create base engine when using its child */
- virtual anna::diameter::codec::Engine * getEngine() const throw(anna::RuntimeException);
+// /** Codec Engine getter: avoids have to create base engine when using its child */
+// virtual anna::diameter::codec::Engine * getEngine() const throw(anna::RuntimeException);
/**
* Initializes Message class information.
* Any reimplementation must first invoke base class method.
*/
virtual void initialize() throw();
+
+public:
+
+ /**
+ * Default constructor
+ * @param engine Codec engine used
+ */
+ Message(Engine *engine = NULL) : anna::diameter::codec::Message(engine) {;}
+
+ /**
+ * Identified constructor
+ * @param id Command identifier as pair (code,request-indicator).
+ * @param engine Codec engine used
+ */
+ Message(CommandId id, Engine *engine = NULL) : anna::diameter::codec::Message(id, engine) {;}
+
+
+
};
}
*/
class DTDFile : public DTD {
public:
+
/**
- Constructor.
+ Constructor
+ \param file File name
*/
- DTDFile() {;}
+ DTDFile(const char *file = NULL) { if (file) initialize(file); }
private:
_xmlDtd* parse(const char* fileName) const throw(RuntimeException);
*/
class DTDMemory : public DTD {
public:
+
/**
- Constructor.
+ Constructor
+ \param dtd Dtd representation
*/
- DTDMemory();
+ DTDMemory(const char *dtd = NULL);
private:
std::string a_filename;
a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
a_numberOfClientSessionsPerServer(1),
- a_baseProtocolCodecEngine((std::string("baseProtocolCodecEngine_for_") + std::string(className)).c_str())
+ a_baseProtocolCodecEngine((std::string("baseProtocolCodecEngine_for_") + std::string(className)).c_str(), baseProtocolDictionary)
{
anna::diameter::sccs::activate();
a_realm = anna::functions::getDomainname();
a_host = anna::functions::getHostname();
// Internal base protocol codec engine:
- a_baseProtocolCodecEngine.setDictionary(baseProtocolDictionary);
a_baseProtocolCodecEngine.setValidationMode(anna::diameter::codec::Engine::ValidationMode::Always); // default was: after decoding
}
initialize();
}
-
//------------------------------------------------------------------------------
//------------------------------------------------------------------- Avp::Avp()
//------------------------------------------------------------------------------
setId(id);
}
-
//------------------------------------------------------------------------------
//------------------------------------------------------------------ Avp::~Avp()
//------------------------------------------------------------------------------
clear();
}
+//------------------------------------------------------------------------------
+//------------------------------------------------------------- Avp::setEngine()
+//------------------------------------------------------------------------------
+void Avp::setEngine(Engine *engine) throw() {
+ if (a_engine && engine != a_engine) {
+ LOGWARNING(anna::Logger::warning("Ignored: it is not a good practice to change the codec engine once assigned. Clear the avp first to set the engine again.", ANNA_FILE_LOCATION));
+ return;
+ }
+
+ a_engine = engine;
+}
//------------------------------------------------------------------------------
//------------------------------------------------------------- Avp::getEngine()
//------------------------------------------------------------------------------
Engine * Avp::getEngine() const throw(anna::RuntimeException) {
if(!a_engine)
- throw anna::RuntimeException("Invalid codec engine reference (NULL)", ANNA_FILE_LOCATION);
+ throw anna::RuntimeException("Invalid codec engine reference (NULL). Use setEngine() to set the corresponding codec engine", ANNA_FILE_LOCATION);
return a_engine;
}
}
+//------------------------------------------------------------------------------
+//---------------------------------------------------------------- Avp::addAvp()
+//------------------------------------------------------------------------------
+Avp * Avp::addAvp(Avp * avp) throw(anna::RuntimeException) {
+ if(!avp) return NULL;
+ if (avp->getEngine() != getEngine()) return NULL;
+ addChild(avp);
+ return avp;
+}
+
+
//------------------------------------------------------------------------------
//------------------------------------------------------------- Avp::removeAvp()
//------------------------------------------------------------------------------
if(!stackFormat) {
isHex = true;
- return a_Unknown->asHexString(); // el asHexString del OctetString no puede lanzar una excepcion en realidad
+ // Tricky situation: if you change the dictionary dynamically, and a previous formatted avp
+ // becomes unknown (the change consists in remove Avps basically), then this would get a core
+ // dump: a_Unknown = NULL. We are not going to protect that situation because it represents a
+ // implementation fault, and there are many points which could have similar bad behaviour
+ // (those where we access directly the a_Unknown pointer).
+ // The best way to afford this is ... TODO:
+ // Freeze dictionary after use from any resource (avp, message), setting a flag which deny
+ // any modification in such dictionary. The best way to do this is on engine configuration
+ // for Avp o Message, where we could invoke something like getEngine()->getDictionary()->freeze()
+
+ return a_Unknown->asHexString(); // asHexString for OctetString cannot launch exception
}
// Special case for Address: could launch exception if not printable
#include <anna/core/mt/Guard.hpp>
-
-namespace anna {
-namespace diameter {
-namespace codec {
-
-const char *MessageDTD = "\
-<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
-<!-- Diameter message DTD -->\n\
-\n\
-<!ELEMENT message (avp*)>\n\
-<!ELEMENT avp (avp*)>\n\
-\n\
-<!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED p-bit (yes | no) #IMPLIED e-bit (yes | no) #IMPLIED t-bit (yes | no) #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>\n\
-<!--\n\
- version: Diameter version. Sets '1' by default\n\
- name: Command name within working stack (dictionary identifier)\n\
- p-bit: (P)roxiable bit flag (yes, no). By default is 'no'\n\
- e-bit: (E)rror bit flag (yes, no). By default is 'no'\n\
- t-bit: Potentially re-(T)ransmitted bit flag (yes, no). By default is 'no'\n\
-\n\
- In order to get more coding capabilities, command code and flags could be established instead of former fields,\n\
- but neither of them are allowed if the other are present (and vice versa):\n\
-\n\
- code: Command code\n\
- flags: Command flags byte value (0-255) where standard bit set for flags is 'RPET rrrr': (R)equest, (P)roxiable, (E)rror, Potentially re-(T)ransmitted message and (r)eserved\n\
-\n\
-\n\
- application-id: Message application id\n\
- hop-by-hop-id: Message hop by hop id. Sets '0' by default\n\
- end-by-end-id: Message end by end id. Sets '0' by default\n\
--->\n\
-\n\
-<!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED alias CDATA #IMPLIED>\n\
-<!--\n\
- name: Avp name within working stack (dictionary identifier)\n\
-\n\
- In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,\n\
- but neither of them are allowed if 'name' is provided (and vice versa):\n\
-\n\
- code: Avp code\n\
- vendor-code: Avp vendor code\n\
- flags: Avp flags byte value (0-255) where standard bit set for flags is 'VMPr rrrr': (V)endor-specific, (M)andatory, end to end encry(P)tion and r(eserved)\n\
- alias: Descriptive/helper field for certain numeric data values. Aliases are defined at diameter dictionary, but are ignored (not checked) at xml message parsing\n\
- The reason to include it in dtd definition, is because xml messages traced by the diameter codec could add alias field for some Avps\n\
-\n\
-\n\
- data: Natural string representation for avp data. Specially applicable with numbers and printable strings, but also\n\
- useful for certain formats which could be easily understandable in such friendly/smart representation. We will\n\
- achieve different human-readable strings depending on data format:\n\
-\n\
- [ OctetString ] (if printable, but not recommended)\n\
- [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)\n\
- [ Time ] (NTP timestamp, normal number representation)\n\
- [ Address ] ('<type (IANA Address Family Number)>|<value>' representation; i.e. '1|192.168.0.1'(IPv4), '8|34616279266'(E164), etc.\n\
- Type (and pipe) field could be avoided for IPv4 and IPv6 address types (a light parse checking is done: one colon for\n\
- IPv6, one dot for IPv4). Internal engine always includes type on data field, which is also recommended for inputs.\n\
- Currently, only IPv4, IPv6 and E164 address types have a known printable presentation, anyway using printable format\n\
- for another types will encode the address value directly as E164 does)\n\
- [ UTF8String, DiameterIdentity, DiameterURI ] (printable)\n\
- [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)\n\
-\n\
- New application formats must define specific natural representation for internal raw data\n\
-\n\
- hex-data: Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind\n\
- of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable\n\
- data and should better be encoded within this field although being printable. Unknown avps (which fails identifying\n\
- provided name or code/vendor-code) must always use this representation.\n\
-\n\
- Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,\n\
- OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport\n\
- readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).\n\
- Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,\n\
- because xml is read by humans with testing and monitoring purposes.\n\
--->\n\
-\n\
-";
-
-
-}
-}
-}
-
using namespace anna::diameter::codec;
//------------------------------------------------------------------------------
//----------------------------------------------------- EngineImpl::EngineImpl()
//------------------------------------------------------------------------------
-EngineImpl::EngineImpl(const char* className) :
+EngineImpl::EngineImpl(const char* className, const stack::Dictionary * dictionary) :
anna::Component(className),
- a_dictionary(NULL),
+ a_dictionary(dictionary),
a_validationDepth(ValidationDepth::FirstError),
a_validationMode(ValidationMode::AfterDecoding),
a_singleFailedAVP(true),
a_ignoreFlags(false),
- a_selectStackWithApplicationId(false),
a_fixMode(FixMode::BeforeEncoding) {
anna::diameter::sccs::activate();
anna::xml::functions::initialize();
- a_dtd.initialize(MessageDTD);
-}
-
-
-//------------------------------------------------------------------------------
-//-------------------------------------------------- EngineImpl::setDictionary()
-//------------------------------------------------------------------------------
-const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(unsigned int stackId) throw() {
- a_dictionary = (stack::Engine::instantiate()).getDictionary(stackId);
- return a_dictionary;
}
--- /dev/null
+// 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 //
+
+
+// Project
+#include <anna/diameter/codec/EngineManager.hpp>
+#include <anna/diameter/stack/Engine.hpp>
+#include <anna/diameter/stack/Dictionary.hpp>
+#include <anna/core/tracing/Logger.hpp>
+
+using namespace anna::diameter::codec;
+
+Engine *EngineManager::getCodecEngine(const ApplicationId &appid) const throw() {
+ appid_codec_engines_it it = a_appid_codec_engines.find(appid);
+ if (it != a_appid_codec_engines.end())
+ return it->second;
+
+ return NULL;
+}
+
+void EngineManager::registerCodecEngine(const ApplicationId &appid, Engine* engine) throw() {
+ if (!engine) return; // nothing done
+ a_appid_codec_engines[appid] = engine;
+}
#include <anna/diameter/codec/functions.hpp> // REQUIRED_WORDS
#include <anna/diameter/codec/OamModule.hpp>
#include <anna/diameter/codec/Engine.hpp>
+#include <anna/diameter/codec/EngineManager.hpp>
#include <anna/diameter/stack/Avp.hpp>
#include <anna/diameter/stack/Format.hpp>
#include <anna/diameter/stack/Dictionary.hpp>
}
+//------------------------------------------------------------------------------
+//--------------------------------------------------------- Message::setEngine()
+//------------------------------------------------------------------------------
+void Message::setEngine(Engine *engine) throw() {
+
+ if (a_engine && engine != a_engine) {
+ LOGWARNING(anna::Logger::warning("Ignored: it is not a good practice to change the codec engine once assigned. Clear the message first to set the engine again.", ANNA_FILE_LOCATION));
+ return;
+ }
+
+ a_engine = engine;
+}
+
+
//------------------------------------------------------------------------------
//--------------------------------------------------------- Message::getEngine()
//------------------------------------------------------------------------------
Engine * Message::getEngine() const throw(anna::RuntimeException) {
if(!a_engine)
- throw anna::RuntimeException("Invalid codec engine reference (NULL)", ANNA_FILE_LOCATION);
+ throw anna::RuntimeException("Invalid codec engine reference (NULL). Use setEngine() to set the corresponding codec engine", ANNA_FILE_LOCATION);
return a_engine;
void Message::setApplicationId(U32 aid) throw(anna::RuntimeException) {
a_applicationId = aid;
- // Default behaviour:
- if (!getEngine()->hasSelectStackWithApplicationId()) return;
+ // Automatic engine configuration:
+ if (a_engine) return;
- // Adapts for Application-ID stack identifier:
- getEngine()->setDictionary(aid);
+ // Codec engine manager (a multithreaded application, normally does not achieve this point, because
+ // messages are prepared for each interface with the corresponding codec engine)
+ anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+ if (em.selectFromApplicationId()) {
+ Engine *monostackEngine = em.getMonoStackCodecEngine();
+ if (monostackEngine) { a_engine = monostackEngine; return; }
+ a_engine = em.getCodecEngine(aid);
+ }
}
}
+//------------------------------------------------------------------------------
+//------------------------------------------------------------ Message::addAvp()
+//------------------------------------------------------------------------------
+Avp * Message::addAvp(Avp * avp) throw() {
+ if(!avp) return NULL;
+ if (avp->getEngine() != getEngine()) return NULL;
+ addChild(avp);
+ return avp;
+}
+
+
//------------------------------------------------------------------------------
//--------------------------------------------------------- Message::removeAvp()
//------------------------------------------------------------------------------
void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::RuntimeException) {
// Trace
LOGDEBUG(
- anna::xml::Node root("Message::decode");
- std::string trace = "DataBlock to decode:\n";
- trace += db.asString();
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ anna::xml::Node root("Message::decode");
+ std::string trace = "DataBlock to decode:\n";
+ trace += db.asString();
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
);
clear();
// EXCEPTION MANAGEMENT IN THIS METHOD
} catch(anna::RuntimeException &ex) {
getEngine()->releaseAvp(avp);
LOGWARNING(
- anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
- anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
+ anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
+ anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
);
break;
}
// Trace
LOGDEBUG(
- std::string trace = "Message decoded:\n";
- trace += asXMLString();
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ std::string trace = "Message decoded:\n";
+ trace += asXMLString();
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
);
// Post-Validation
Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
if(isRequest()) return;
-// RFC 6733:
-//
-// 7.5. Failed-AVP AVP
-//
-// The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
-// debugging information in cases where a request is rejected or not
-// fully processed due to erroneous information in a specific AVP. The
-// value of the Result-Code AVP will provide information on the reason
-// for the Failed-AVP AVP. A Diameter answer message SHOULD contain an
-// instance of the Failed-AVP AVP that corresponds to the error
-// indicated by the Result-Code AVP. For practical purposes, this
-// Failed-AVP would typically refer to the first AVP processing error
-// that a Diameter node encounters.
+ // RFC 6733:
+ //
+ // 7.5. Failed-AVP AVP
+ //
+ // The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+ // debugging information in cases where a request is rejected or not
+ // fully processed due to erroneous information in a specific AVP. The
+ // value of the Result-Code AVP will provide information on the reason
+ // for the Failed-AVP AVP. A Diameter answer message SHOULD contain an
+ // instance of the Failed-AVP AVP that corresponds to the error
+ // indicated by the Result-Code AVP. For practical purposes, this
+ // Failed-AVP would typically refer to the first AVP processing error
+ // that a Diameter node encounters.
// Although the Failed-AVP definition has cardinality 1* and Failed-AVP itself is defined in
// most of the command codes as *[Failed-AVP], i think this is not a deliberate ambiguity.
Avp *leaf = theFailedAvp;
LOGDEBUG(
- std::string msg = "Adding to Failed-AVP, the wrong avp ";
- msg += wrongName ? wrongName : (anna::diameter::functions::avpIdAsPairString(wrong));
- msg += " found inside ";
- msg += parent.asString();
+ std::string msg = "Adding to Failed-AVP, the wrong avp ";
+ msg += wrongName ? wrongName : (anna::diameter::functions::avpIdAsPairString(wrong));
+ msg += " found inside ";
+ msg += parent.asString();
- anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+ anna::Logger::debug(msg, ANNA_FILE_LOCATION);
);
std::vector<AvpId>::const_iterator it;
for(it = parent.AvpsId.begin(); it != parent.AvpsId.end(); it++)
- leaf = leaf->addAvp(*it);
+ leaf = leaf->addAvp(*it);
leaf->addAvp(wrong);
}
//------------------------------------------------------------------------------
//----------------------------------------------- Message::setStandardToAnswer()
//------------------------------------------------------------------------------
-void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw() {
+void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw(anna::RuntimeException) {
if(!request.getId().second) return;
// Message header:
const Avp *reqSessionId = request.getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore);
if(reqSessionId)
- if(!getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore))
+ if(!getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore))
addAvp(helpers::base::AVPID__Session_Id)->getUTF8String()->setValue(reqSessionId->getUTF8String()->getValue());
// Origin-Host & Realm
// Fix:
fix();
LOGDEBUG(
- std::string msg = "Completed answer:\n";
- msg += asXMLString();
- anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+ std::string msg = "Completed answer:\n";
+ msg += asXMLString();
+ anna::Logger::debug(msg, ANNA_FILE_LOCATION);
);
}
// Trace
LOGDEBUG(
- std::string trace = "Message to code:\n";
- trace += asXMLString();
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ std::string trace = "Message to code:\n";
+ trace += asXMLString();
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
);
// Memory allocation
U24 length = getLength();
// Trace
LOGDEBUG(
- std::string trace = "DataBlock encoded:\n";
- trace += a_forCode.asString();
-// trace += "\nAs continuous hexadecimal string:\n";
-// trace += anna::functions::asHexString(a_forCode);
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ std::string trace = "DataBlock encoded:\n";
+ trace += a_forCode.asString();
+ // trace += "\nAs continuous hexadecimal string:\n";
+ // trace += anna::functions::asHexString(a_forCode);
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
);
return a_forCode;
}
-
//------------------------------------------------------------------------------
-//----------------------------------------------------- Message::fromXMLString()
+//----------------------------------------------------------- Message::loadXML()
//------------------------------------------------------------------------------
-void Message::fromXMLString(const std::string &xmlString) throw(anna::RuntimeException) {
- LOGDEBUG(anna::Logger::debug("Reading diameter message from xml string representation", ANNA_FILE_LOCATION));
- anna::xml::DocumentMemory xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
- const anna::xml::Node *rootNode;
- xmlDocument.initialize(xmlString.c_str());
- rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
- LOGDEBUG(anna::Logger::debug("Read OK from XML string representation", ANNA_FILE_LOCATION));
- fromXML(rootNode);
-}
+void Message::loadXML(const std::string &xmlPathFile) throw(anna::RuntimeException) {
+ anna::xml::DocumentFile xmlDocument;
+ anna::diameter::codec::functions::messageXmlDocumentFromXmlFile(xmlDocument, xmlPathFile);
+ fromXML(xmlDocument.getRootNode());
+}
//------------------------------------------------------------------------------
//----------------------------------------------------------- Message::fromXML()
msg += "': negative values are not allowed";
throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
}
- */
+ */
} else u_aux = 0;
setHopByHop(u_aux);
msg += "': negative values are not allowed";
throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
}
- */
+ */
} else u_aux = 0;
setEndToEnd(u_aux);
}
-//------------------------------------------------------------------------------
-//----------------------------------------------------------- Message::loadXML()
-//------------------------------------------------------------------------------
-void Message::loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException) {
- LOGDEBUG(
- std::string trace = "Loading diameter message from file '";
- trace += xmlPathFile;
- trace += "'";
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
- );
- anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
- const anna::xml::Node *rootNode;
- xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
- rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
- LOGDEBUG(
- std::string trace = "Loaded XML file (";
- trace += xmlPathFile;
- trace += "):\n";
- trace += anna::xml::Compiler().apply(rootNode);
- anna::Logger::debug(trace, ANNA_FILE_LOCATION);
- );
- fromXML(rootNode);
-}
-
-
//------------------------------------------------------------------------------
//------------------------------------------------------------- Message::asXML()
//------------------------------------------------------------------------------
#include <string>
-using namespace anna::diameter::codec;
-
+namespace anna {
+namespace diameter {
+namespace codec {
+// Preloaded MemoryDTD for function helpers:
+anna::xml::DTDMemory MessageDTDMemory(MessageDTD);
+}
+}
+}
// Parent struct helper /////////////////////////////////////////////////////////////////////////////
-void parent::setMessage(const anna::diameter::CommandId & mid, const char *mname) throw() {
+void anna::diameter::codec::parent::setMessage(const anna::diameter::CommandId & mid, const char *mname) throw() {
MessageId = mid;
if (mname) {
MessageName = mname;
}
else {
- MessageName = "Message";
+ MessageName = "Message";
MessageName += anna::diameter::functions::commandIdAsPairString(mid);
}
}
-void parent::addAvp(const anna::diameter::AvpId & aid, const char *aname) throw() {
+void anna::diameter::codec::parent::addAvp(const anna::diameter::AvpId & aid, const char *aname) throw() {
AvpsId.push_back(aid);
std::string name;
if (aname) {
- name = aname;
+ name = aname;
}
else {
name = "Avp";
- name += anna::diameter::functions::avpIdAsPairString(aid);
+ name += anna::diameter::functions::avpIdAsPairString(aid);
}
AvpsName.push_back(name);
}
-std::string parent::asString() const throw() { // "<command><avp 1>-><avp 2>->...-><avp N>"
+std::string anna::diameter::codec::parent::asString() const throw() { // "<command><avp 1>-><avp 2>->...-><avp N>"
std::string result = MessageName;
for (std::vector<std::string>::const_iterator it = AvpsName.begin(); it != AvpsName.end(); it++) {
- result += "->";
- result += (*it);
+ result += "->";
+ result += (*it);
}
return result;
/////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
// getters
-anna::diameter::CommandId functions::getCommandId(const anna::DataBlock & db) throw(anna::RuntimeException) {
+anna::diameter::CommandId anna::diameter::codec::functions::getCommandId(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
const char * data = db.getData();
U8 flags = data[4];
U24 code = DECODE3BYTES_INDX_VALUETYPE(data, 5, U24);
-// U24 code = (((U24)data[5] << 16) & 0xFF0000) +
-// (((U24)data[6] << 8) & 0x00FF00) +
-// (((U24)data[7]) & 0x0000FF);
+ // U24 code = (((U24)data[5] << 16) & 0xFF0000) +
+ // (((U24)data[6] << 8) & 0x00FF00) +
+ // (((U24)data[7]) & 0x0000FF);
return (anna::diameter::CommandId(code, (flags & Message::RBitMask) != 0x00));
}
-bool functions::requestBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+bool anna::diameter::codec::functions::requestBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
return (((db.getData())[4] & Message::RBitMask) != 0x00);
}
-bool functions::proxiableBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+bool anna::diameter::codec::functions::proxiableBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
return (((db.getData())[4] & Message::PBitMask) != 0x00);
}
-bool functions::errorBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+bool anna::diameter::codec::functions::errorBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
return (((db.getData())[4] & Message::EBitMask) != 0x00);
}
-bool functions::potentiallyReTransmittedMessageBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
+bool anna::diameter::codec::functions::potentiallyReTransmittedMessageBit(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
return (((db.getData())[4] & Message::TBitMask) != 0x00);
}
-anna::diameter::ApplicationId functions::getApplicationId(const anna::DataBlock & db) throw(anna::RuntimeException) {
+anna::diameter::ApplicationId anna::diameter::codec::functions::getApplicationId(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
const char * appidPtr = db.getData() + 8;
anna::diameter::ApplicationId result = DECODE4BYTES_INDX_VALUETYPE(appidPtr, 0, U32);
-// anna::diameter::ApplicationId result = (((U32)appidPtr[0] << 24) & 0xFF000000) +
-// (((U32)appidPtr[1] << 16) & 0x00FF0000) +
-// (((U32)appidPtr[2] << 8) & 0x0000FF00) +
-// (((U32)appidPtr[3]) & 0x000000FF);
+ // anna::diameter::ApplicationId result = (((U32)appidPtr[0] << 24) & 0xFF000000) +
+ // (((U32)appidPtr[1] << 16) & 0x00FF0000) +
+ // (((U32)appidPtr[2] << 8) & 0x0000FF00) +
+ // (((U32)appidPtr[3]) & 0x000000FF);
return result;
}
-anna::diameter::HopByHop functions::getHopByHop(const anna::DataBlock & db) throw(anna::RuntimeException) {
+anna::diameter::HopByHop anna::diameter::codec::functions::getHopByHop(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
const char * hbhPtr = db.getData() + 12;
anna::diameter::HopByHop result = DECODE4BYTES_INDX_VALUETYPE(hbhPtr, 0, U32);
-// anna::diameter::HopByHop result = (((U32)hbhPtr[0] << 24) & 0xFF000000) +
-// (((U32)hbhPtr[1] << 16) & 0x00FF0000) +
-// (((U32)hbhPtr[2] << 8) & 0x0000FF00) +
-// (((U32)hbhPtr[3]) & 0x000000FF);
+ // anna::diameter::HopByHop result = (((U32)hbhPtr[0] << 24) & 0xFF000000) +
+ // (((U32)hbhPtr[1] << 16) & 0x00FF0000) +
+ // (((U32)hbhPtr[2] << 8) & 0x0000FF00) +
+ // (((U32)hbhPtr[3]) & 0x000000FF);
return result;
}
-anna::diameter::EndToEnd functions::getEndToEnd(const anna::DataBlock & db) throw(anna::RuntimeException) {
+anna::diameter::EndToEnd anna::diameter::codec::functions::getEndToEnd(const anna::DataBlock & db) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength)
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
const char * etePtr = db.getData() + 16;
anna::diameter::EndToEnd result = DECODE4BYTES_INDX_VALUETYPE(etePtr, 0, U32);
-// anna::diameter::EndToEnd result = (((U32)etePtr[0] << 24) & 0xFF000000) +
-// (((U32)etePtr[1] << 16) & 0x00FF0000) +
-// (((U32)etePtr[2] << 8) & 0x0000FF00) +
-// (((U32)etePtr[3]) & 0x000000FF);
+ // anna::diameter::EndToEnd result = (((U32)etePtr[0] << 24) & 0xFF000000) +
+ // (((U32)etePtr[1] << 16) & 0x00FF0000) +
+ // (((U32)etePtr[2] << 8) & 0x0000FF00) +
+ // (((U32)etePtr[3]) & 0x000000FF);
return result;
}
-void functions::decodeCommandHeader(const char *start, char & version, U24 & length, char & flags, diameter::CommandId & id, int & appId, int & hbh, int & ete) throw(anna::RuntimeException) {
+void anna::diameter::codec::functions::decodeCommandHeader(const char *start, char & version, U24 & length, char & flags, diameter::CommandId & id, int & appId, int & hbh, int & ete) throw(anna::RuntimeException) {
if(start == NULL)
throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
ete = DECODE4BYTES_INDX_VALUETYPE(start, 16, U32);
}
-void functions::decodeAVP(const char *start, diameter::AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException) {
+void anna::diameter::codec::functions::decodeAVP(const char *start, diameter::AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException) {
if(start == NULL)
throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
const char *dataPointer = (vendorSpecific ? (start + 12) : (start + 8)); // pointer to data part
data.assign(dataPointer, dataLength);
LOGLOCAL3(
- std::string msg = anna::functions::asString("decodedAVP id (%d,%d), length %d, data length %d, data part 0x%s",
- id.first, id.second, length, dataLength, anna::functions::asHexString(anna::DataBlock(dataPointer, dataLength)).c_str());
- anna::Logger::write(anna::Logger::Local3, msg, ANNA_FILE_LOCATION);
+ std::string msg = anna::functions::asString("decodedAVP id (%d,%d), length %d, data length %d, data part 0x%s",
+ id.first, id.second, length, dataLength, anna::functions::asHexString(anna::DataBlock(dataPointer, dataLength)).c_str());
+ anna::Logger::write(anna::Logger::Local3, msg, ANNA_FILE_LOCATION);
);
}
-const char * functions::nextAVP(const char *avpsDB, int avpsLen, const char *start) throw(anna::RuntimeException) {
+const char * anna::diameter::codec::functions::nextAVP(const char *avpsDB, int avpsLen, const char *start) throw(anna::RuntimeException) {
if(start == NULL)
throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION);
if(avpsDB == NULL)
throw anna::RuntimeException("NULL provided avpsDB pointer", ANNA_FILE_LOCATION);
const char *result;
-// LOGDEBUG(
-// std::string msg("DataBlock provided to 'nextAVP'");
-// msg += avpsDB.asString();
-// anna::Logger::debug(msg, ANNA_FILE_LOCATION);
-// );
+ // LOGDEBUG(
+ // std::string msg("DataBlock provided to 'nextAVP'");
+ // msg += avpsDB.asString();
+ // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+ // );
//int avpLength = (start[5] << 16) + (start[6] << 8) + start[7]; // AVP Length
int avpLength = DECODE3BYTES_INDX_VALUETYPE(start, 5, int);
result = start + 4 * REQUIRED_WORDS(avpLength);
// return nextAVP(avpsDB.getData(), avpsDB.getSize(), start);
//}
-const char * functions::findAVP(const char *avpsDB, int avpsLen, const diameter::AvpId & id, int n) throw(anna::RuntimeException) {
+const char * anna::diameter::codec::functions::findAVP(const char *avpsDB, int avpsLen, const diameter::AvpId & id, int n) throw(anna::RuntimeException) {
const char *result = avpsDB; // first avp
int positives = 0;
// Decoded avp information:
if(result == NULL) { // (*)
LOGDEBUG(
- std::string msg = "AVP ";
- msg += anna::diameter::functions::avpIdAsPairString(id);
- msg += " not found at DataBlock";
- anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+ std::string msg = "AVP ";
+ msg += anna::diameter::functions::avpIdAsPairString(id);
+ msg += " not found at DataBlock";
+ anna::Logger::debug(msg, ANNA_FILE_LOCATION);
);
return NULL;
}
//}
// modifiers
-void functions::setHopByHop(anna::DataBlock & db, diameter::HopByHop hbh) throw(anna::RuntimeException) {
+void anna::diameter::codec::functions::setHopByHop(anna::DataBlock & db, diameter::HopByHop hbh) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength) {
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
}
}
-void functions::setEndToEnd(anna::DataBlock & db, diameter::EndToEnd ete) throw(anna::RuntimeException) {
+void anna::diameter::codec::functions::setEndToEnd(anna::DataBlock & db, diameter::EndToEnd ete) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength) {
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
}
memcpy((char *)(db.getData() + 16), source, 4);
}
-void functions::setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate) throw(anna::RuntimeException) {
+void anna::diameter::codec::functions::setPotentiallyReTransmittedMessageBit(const anna::DataBlock & db, bool activate) throw(anna::RuntimeException) {
if(db.getSize() < Message::HeaderLength) {
throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION);
}
memcpy((char *)(db.getData() + 4), flags, 1);
}
+// XML parsers for diameter messages ///////////////////////////////////////////////////////////////////////////
+void anna::diameter::codec::functions::messageXmlDocumentFromXmlFile(anna::xml::DocumentFile &xmlDocument, const std::string & xmlPathFile) throw(anna::RuntimeException) {
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("Parsing diameter message from xml file '%s' into xml document", xmlPathFile.c_str()), ANNA_FILE_LOCATION));
+ xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
+ const anna::xml::Node *rootNode = xmlDocument.parse(MessageDTDMemory); // Parsing: fail here if xml violates dtd
+ LOGDEBUG(
+ std::string trace = "Parsing OK from XML file '";
+ trace += xmlPathFile;
+ trace += "':\n";
+ trace += anna::xml::Compiler().apply(rootNode);
+ anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+ );
+}
+void anna::diameter::codec::functions::messageXmlDocumentFromXmlString(anna::xml::DocumentFile &xmlDocument, const std::string &xmlString) throw(anna::RuntimeException) {
+ LOGDEBUG(anna::Logger::debug("Parsing diameter message from xml string representation into xml document", ANNA_FILE_LOCATION));
+ xmlDocument.initialize(xmlString.c_str());
+ xmlDocument.parse(MessageDTDMemory); // Parsing: fail here if xml violates dtd
+ LOGDEBUG(anna::Logger::debug("Parsing OK from XML string representation", ANNA_FILE_LOCATION));
+}
// Local
#include <anna/diameter/codec/tme/Avp.hpp>
-#include <anna/diameter/codec/tme/Engine.hpp>
+//#include <anna/diameter/codec/tme/Engine.hpp>
#include <anna/diameter/helpers/defines.hpp>
#include <anna/diameter/stack/Format.hpp>
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------- Avp::Avp()
-//------------------------------------------------------------------------------
-Avp::Avp() {
- initialize();
-}
-
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------- Avp::Avp()
-//------------------------------------------------------------------------------
-Avp::Avp(AvpId id) {
- initialize();
- setId(id);
-}
-
+////------------------------------------------------------------------------------
+////------------------------------------------------------------------- Avp::Avp()
+////------------------------------------------------------------------------------
+//Avp::Avp(Engine *engine) : anna::diameter::codec::Avp(engine) {
+// initialize();
+//}
+//
+////------------------------------------------------------------------------------
+////------------------------------------------------------------------- Avp::Avp()
+////------------------------------------------------------------------------------
+//Avp::Avp(AvpId id, Engine *engine)/* : anna::diameter::codec::Avp(id, engine)*/ {
+// initialize();
+// setId(id);
+//}
//------------------------------------------------------------------------------
//------------------------------------------------------------------ Avp::~Avp()
}
-//------------------------------------------------------------------------------
-//------------------------------------------------------------- Avp::getEngine()
-//------------------------------------------------------------------------------
-anna::diameter::codec::Engine * Avp::getEngine() const throw(anna::RuntimeException) {
- if(!a_engine)
- throw anna::RuntimeException("Invalid codec engine reference (NULL)", ANNA_FILE_LOCATION);
-
- return a_engine;
-}
+////------------------------------------------------------------------------------
+////------------------------------------------------------------- Avp::getEngine()
+////------------------------------------------------------------------------------
+//anna::diameter::codec::Engine * Avp::getEngine() const throw(anna::RuntimeException) {
+// if(!a_engine)
+// throw anna::RuntimeException("Invalid codec engine reference (NULL)", ANNA_FILE_LOCATION);
+//
+// return a_engine;
+//}
//------------------------------------------------------------------------------
// Local
#include <anna/diameter/codec/tme/Message.hpp>
-#include <anna/diameter/codec/tme/Engine.hpp>
+//#include <anna/diameter/codec/tme/Engine.hpp>
#include <anna/core/functions.hpp>
using namespace anna::diameter::codec::tme;
-//------------------------------------------------------------------------------
-//--------------------------------------------------------- Message::getEngine()
-//------------------------------------------------------------------------------
-anna::diameter::codec::Engine * Message::getEngine() const throw(anna::RuntimeException) {
- if(!a_engine)
- throw anna::RuntimeException("Invalid codec engine reference (NULL)", ANNA_FILE_LOCATION);
-
- return a_engine;
-}
+////------------------------------------------------------------------------------
+////--------------------------------------------------------- Message::getEngine()
+////------------------------------------------------------------------------------
+//anna::diameter::codec::Engine * Message::getEngine() const throw(anna::RuntimeException) {
+// if(!a_engine)
+// throw anna::RuntimeException("Invalid codec engine reference (NULL)", ANNA_FILE_LOCATION);
+//
+// return a_engine;
+//}
//------------------------------------------------------------------------------
using namespace anna;
using namespace anna::xml;
-_xmlDtd* DTDFile::parse(const char* filename) const
+_xmlDtd* DTDFile::parse(const char *filename) const
throw(RuntimeException) {
LOGMETHOD(TraceMethod tf("anna::xml::DTDFile", "parse", ANNA_FILE_LOCATION));
_xmlDtd* result;
using namespace anna;
using namespace anna::xml;
-DTDMemory::DTDMemory() {
+DTDMemory::DTDMemory(const char *dtd) {
a_filename = "/tmp/anna.xml.";
a_filename += functions::asString((int) getpid());
a_filename += ".dtd";
+ if (dtd) initialize(dtd);
}
//---------------------------------------------------------------------------------------------
// Aunque la libXML ofrece funciones para analizar una DTD a partir de un buffer de memoria
-// hemos sido incapaces de hacerla funcionar correctamente. Así que nos vemos obligados
-// a volcar el buffer en un archivo .. y una vez allí analizarlo con la función que analiza
+// hemos sido incapaces de hacerla funcionar correctamente. As� que nos vemos obligados
+// a volcar el buffer en un archivo .. y una vez all� analizarlo con la funci�n que analiza
// la DTD a partir de un archivo.
//---------------------------------------------------------------------------------------------
_xmlDtd* DTDMemory::parse(const char* content) const