bool RhsListener::ExecuteRhsCommand(AgentSML* pAgentSML, smlRhsEventId eventID, std::string const& functionName, std::string const& arguments, std::string* pResultStr) { bool result = false ; // Get the list of connections (clients) who have registered to implement this right hand side (RHS) function. ConnectionList* pList = GetRhsListeners(functionName.c_str()) ; // If nobody is listening we're done (not a bug as we register for all rhs functions and only forward specific ones that the client has registered) if (!pList || pList->size() == 0) return result ; ConnectionListIter connectionIter = pList->begin() ; // We need the first connection for when we're building the message. Perhaps this is a sign that // we shouldn't have rolled these methods into Connection. Connection* pConnection = *connectionIter ; // Convert eventID to a string char const* event = m_pKernelSML->ConvertEventToString(eventID) ; // Build the SML message we're doing to send. // Pass the agent in the "name" parameter not the "agent" parameter as this is a kernel // level event, not an agent level one (because you need to register with the kernel to get "agent created"). soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event) ; if (pAgentSML) pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamName, pAgentSML->GetName()) ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamFunction, functionName.c_str()) ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamValue, arguments.c_str()) ; #ifdef _DEBUG // Generate a text form of the XML so we can look at it in the debugger. char* pStr = pMsg->GenerateXMLString(true) ; #endif AnalyzeXML response ; // We want to call embedded connections first, so that we get the best performance // for these functions. I don't want to sort the list or otherwise change it so // instead we'll just use a rather clumsy outer loop to do this. for (int phase = 0 ; phase < 2 && !result ; phase++) { // Only call to embedded connections bool embeddedPhase = (phase == 0) ; // Reset the iterator to the beginning of the list connectionIter = pList->begin(); // Keep looping until we get a result while (connectionIter != pList->end() && !result) { pConnection = *connectionIter ; // We call all embedded connections (same process) first before // trying any remote methods. This ensures that if multiple folks register // for the same function we execute the fastest one (w/o going over a socket for the result). if (pConnection->IsRemoteConnection() && embeddedPhase) { connectionIter++ ; continue ; } // It would be faster to just send a message here without waiting for a response // but that could produce incorrect behavior if the client expects to act *during* // the event that we're notifying them about (e.g. notification that we're in the input phase). bool ok = pConnection->SendMessageGetResponse(&response, pMsg) ; if (ok) { char const* pResult = response.GetResultString() ; if (pResult != NULL) { (*pResultStr) = pResult ; result = true ; } } connectionIter++ ; } } #ifdef _DEBUG // Release the string form we generated for the debugger pMsg->DeleteString(pStr) ; #endif // Clean up delete pMsg ; return result ; }
void PrintListener::FlushOutput(Connection* pSourceConnection, smlPrintEventId eventID) { int buffer = eventID - smlEVENT_FIRST_PRINT_EVENT ; // Nothing waiting to be sent, so we're done. if (!m_BufferedPrintOutput[buffer].str().size()) return ; // Get the first listener for this event (or return if there are none) ConnectionListIter connectionIter ; if (!EventManager<smlPrintEventId>::GetBegin(eventID, &connectionIter)) return ; // We need the first connection for when we're building the message. Perhaps this is a sign that // we shouldn't have rolled these methods into Connection. Connection* pConnection = *connectionIter; // Convert eventID to a string char const* event = m_pKernelSML->ConvertEventToString(eventID) ; // Send the message out AnalyzeXML response ; // For non-echo events, just send it normally if (eventID != smlEVENT_ECHO) { // Build the SML message we're going to send. soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName()); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamMessage, m_BufferedPrintOutput[buffer].str().c_str()); // Send the message out AnalyzeXML response ; SendEvent(0, pConnection, pMsg, &response, connectionIter, GetEnd(eventID)) ; // Clean up delete pMsg ; } else { // For echo events build up the message on a connection by connection basis. // This allows us to tell a sender if they were the cause of a given message so a client // can filter out echoes from commands they originated. while (connectionIter != GetEnd(eventID)) { pConnection = *connectionIter ; connectionIter++ ; // Build the SML message we're going to send. soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName()); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamMessage, m_BufferedPrintOutput[buffer].str().c_str()); pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamSelf, (pSourceConnection == pConnection) ? sml_Names::kTrue : sml_Names::kFalse) ; #ifdef _DEBUG // Generate a text form of the XML so we can look at it in the debugger. char* pStr = pMsg->GenerateXMLString(true) ; #endif // It would be faster to just send a message here without waiting for a response // but that could produce incorrect behavior if the client expects to act *during* // the event that we're notifying them about (e.g. notification that we're in the input phase). pConnection->SendMessageGetResponse(&response, pMsg) ; #ifdef _DEBUG // Clean up the string form of the message. pMsg->DeleteString(pStr) ; #endif // Clean up delete pMsg ; } } // Clear the buffer now that it's been sent m_BufferedPrintOutput[buffer].str( std::string() ); }
bool RhsListener::HandleFilterEvent(smlRhsEventId eventID, AgentSML* pAgent, char const* pArgument, std::string &pReturnValue) { // Currently only supporting one event here, but that could change in time. assert(eventID == smlEVENT_FILTER) ; // Filters are handled as a RHS function call internally, using a special reserved name. char const* pFunctionName = sml_Names::kFilterName ; // Get the list of connections (clients) who have registered to implement this right hand side (RHS) function. ConnectionList* pList = GetRhsListeners(pFunctionName) ; bool result = false ; // If nobody is listening we're done (not a bug as we register for all rhs functions and only forward specific ones that the client has registered) if (!pList || pList->size() == 0) return result ; ConnectionListIter connectionIter = pList->begin() ; // We need the first connection for when we're building the message. Perhaps this is a sign that // we shouldn't have rolled these methods into Connection. Connection* pConnection = *connectionIter ; // Convert eventID to a string char const* event = m_pKernelSML->ConvertEventToString(eventID) ; // Copy the initial command line into the return buffer and send that over. // This will be sequentially replaced by each filter in turn and whatever // is left in here at the end is the result of the filtering. // This allows multiple filters to act on the data, each modifying it as it goes. // A side effect of this is that the order the filters work on the data is significant. // Anyone in the chain can set the string to be the empty string, which brings the // whole process to a halt (they have eaten the command line at that point). pReturnValue.reserve(strlen(pArgument) + 1); //adding 1 because strlen does not count the terminating null character pReturnValue.assign(pArgument); bool stop = false ; while (connectionIter != pList->end() && !stop) { // Build the SML message we're doing to send. // Pass the agent in the "name" parameter not the "agent" parameter as this is a kernel // level event, not an agent level one (because you need to register with the kernel to get "agent created"). soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event) ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamName, pAgent ? pAgent->GetName() : "") ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamFunction, sml_Names::kFilterName) ; pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamValue, pReturnValue.c_str()) ; // We send the current command line over #ifdef _DEBUG // Generate a text form of the XML so we can look at it in the debugger. char* pStr = pMsg->GenerateXMLString(true) ; #endif AnalyzeXML response ; pConnection = *connectionIter ; bool ok = pConnection->SendMessageGetResponse(&response, pMsg) ; if (ok) { char const* pResult = response.GetResultString() ; if (pResult != NULL) { // If the listener returns a result then take that // value and return it in "pReturnValue" to the caller. // If the client returns a longer string than the caller, pReturnValue is resized to fit it. pReturnValue.reserve(strlen(pResult) + 1); //adding 1 because strlen does not count the terminating null character pReturnValue.assign(pResult); result = true ; } else { // If one of the filters returns an empty string, stop the process at that point // because the command has been "eaten". Make the result the empty string. pReturnValue[0] = 0 ; result = true ; stop = true ; } } connectionIter++ ; #ifdef _DEBUG // Release the string form we generated for the debugger pMsg->DeleteString(pStr) ; #endif // Clean up delete pMsg ; } return result ; }