// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
//-----------------------------------------------------------------------------
// includes
//-----------------------------------------------------------------------------
#include "administered_module.h"
#include "module_admin_itf.h"
#include "patchman_tester.h"
#include "patchman_constants.h"
//-------------------------------------------------------------------------------------------------
// namespaces
//-------------------------------------------------------------------------------------------------
using namespace std;
using namespace NLMISC;
using namespace NLNET;
using namespace PATCHMAN;
//-----------------------------------------------------------------------------
// methods CAdministeredModuleBase - ctors / dtors
//-----------------------------------------------------------------------------
CAdministeredModuleBase::CAdministeredModuleBase()
{
_ErrorCount= 0;
_Initialised= false;
}
NLMISC::CSString CAdministeredModuleBase::init(const TParsedCommandLine &initInfo)
{
CAdministeredModuleBaseSkel::init(this);
// prevent double initialialisation (given that we are using virtual inherritance,
// this init can be called more than once for the same module)
if (_Initialised)
return "";
_Initialised= true;
// initialise the module base...
CModuleBase::initModule(initInfo);
// initialise the state variables
setStateVariable("State","Initialising");
broadcastStateInfo();
return "";
}
//-----------------------------------------------------------------------------
// methods CAdministeredModuleBase - hooks for methods that this interface implements and that must be called from the parent class
//-----------------------------------------------------------------------------
void CAdministeredModuleBase::onModuleUp(IModuleProxy *module)
{
// if the module coming up is an SPM module then we call it 'dad'
if (CSString(module->getModuleManifest()).contains(ManifestEntryIsAdministrator))
{
_PatchManagers.insert(module);
registerProgress("ServerPatchManager Connected: "+module->getModuleName()+" ("+module->getModuleManifest()+")");
}
}
void CAdministeredModuleBase::onModuleDown(IModuleProxy *module)
{
// if the module going down is an SPM module then remove it from our lists
if (_PatchManagers.find(module)!=_PatchManagers.end())
{
_PatchManagers.erase(module);
registerProgress("ServerPatchManager Disconnected: "+module->getModuleName()+" ("+module->getModuleManifest()+")");
}
}
void CAdministeredModuleBase::onModuleUpdate()
{
H_AUTO(CAdministeredModuleBase_onModuleUpdate);
// if the state has changed then broadcast the new state info
if (_LastBroadcastState != getStateString())
{
broadcastStateInfo();
}
}
//bool CAdministeredModuleBase::onDispatchMessage(NLNET::IModuleProxy *sender, const NLNET::CMessage &message)
//{
// return CAdministeredModuleBaseSkel::onDispatchMessage(sender,message);
//}
//-----------------------------------------------------------------------------
// methods CAdministeredModuleBase - callbackf for treating module messages
//-----------------------------------------------------------------------------
void CAdministeredModuleBase::executeCommand(NLNET::IModuleProxy *sender, const NLMISC::CSString &cmdline, const NLMISC::CSString &originator)
{
// create a displayer to gather the output of the command
class SStringDisplayer: public IDisplayer
{
public:
CSString Data;
void doDisplay( const CLog::TDisplayInfo& args, const char *message)
{
Data += message;
}
};
CLog log;
SStringDisplayer stringDisplayer;
log.addDisplayer(&stringDisplayer);
// execute the command
registerProgress(NLMISC::toString("exec '%s' (via '%s'): '%s'", originator.c_str(), sender->getModuleName().c_str(), cmdline.c_str()));
ICommand::execute(this->getModuleName()+'.'+cmdline.strip(),log);
// send a reply message to the originating service
CServerPatchManagerProxy manager(sender);
manager.executedCommandResult(this,originator,cmdline,stringDisplayer.Data);
}
//-----------------------------------------------------------------------------
// methods CAdministeredModuleBase - methods for managing state variables
//-----------------------------------------------------------------------------
void CAdministeredModuleBase::registerError(const NLMISC::CSString& value) const
{
++_ErrorCount;
setStateVariable("Error",NLMISC::toString("[%d]",_ErrorCount)+value);
nlwarning("%s: ERROR: %s",CModuleBase::getModuleName().c_str(),value.c_str());
}
void CAdministeredModuleBase::registerProgress(const NLMISC::CSString& value) const
{
setStateVariable("Latest",value);
nlinfo("%s: %s",CModuleBase::getModuleName().c_str(),value.c_str());
}
void CAdministeredModuleBase::setStateVariable(const NLMISC::CSString& variableName, const NLMISC::CSString& value) const
{
// if the state variable is in fact unchanged then just drop out as there is nothing real to do
if (_StateVariables[variableName] == value)
{
return;
}
// set the state variable value
_StateVariables[variableName]= value;
}
void CAdministeredModuleBase::appendStateVariable(const NLMISC::CSString& variableName, const NLMISC::CSString& value) const
{
// if the state variable is in fact unchanged then just drop out as there is nothing real to do
if (value.empty())
{
return;
}
// if the state variable isn't currently empty then add a separating space before the new value
if (!_StateVariables[variableName].empty())
{
_StateVariables[variableName]+= ' ';
}
// append the new value to the state variable
_StateVariables[variableName]+= value;
}
const NLMISC::CSString& CAdministeredModuleBase::getStateVariable(const NLMISC::CSString& variableName) const
{
// setup a static 'emptyString' to return a refference to if requested variables are not found
static NLMISC::CSString emptyString;
return (_StateVariables.find(variableName)==_StateVariables.end())? emptyString: _StateVariables[variableName];
}
NLMISC::CSString CAdministeredModuleBase::getStateString() const
{
// setup a result variable to build the output string in
NLMISC::CSString result;
// add each variable to the result string in turn
for (TStateVariables::iterator it= _StateVariables.begin(); it!=_StateVariables.end(); ++it)
{
if (!result.empty())
{
result+=", ";
}
result+= it->first;
result+= "=";
result+= it->second.quoteIfNotAtomic();
}
// all done so return the constructed string
return result;
}
void CAdministeredModuleBase::clearStateVariable(const NLMISC::CSString& variableName) const
{
_StateVariables.erase(variableName);
}
void CAdministeredModuleBase::clearAllStateVariables() const
{
_StateVariables.clear();
}
void CAdministeredModuleBase::broadcastStateInfo() const
{
// generate the state string that we need to dispatch to any listening modules
CSString stateString= getStateString();
// send an update message to any connected SPM modules
for (TPatchManagers::const_iterator it=_PatchManagers.begin(); it!=_PatchManagers.end(); ++it)
{
CServerPatchManagerProxy spm(*it);
spm.declareState(const_cast(this),stateString);
}
// after broadcasting the state info the 'state has changed' flag can be set to false
_LastBroadcastState= stateString;
}
//-----------------------------------------------------------------------------
// class CAdministeredModuleWrapper
//-----------------------------------------------------------------------------
CAdministeredModuleWrapper::CAdministeredModuleWrapper()
{
_AdministeredModuleBase= NULL;
}
NLMISC::CSString CAdministeredModuleWrapper::init(CAdministeredModuleBase* administeredModuleBase)
{
_AdministeredModuleBase= administeredModuleBase;
return CSString();
}
void CAdministeredModuleWrapper::registerError(const NLMISC::CSString& value) const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->registerError(value);
}
else
{
nlwarning("Error: %s",value.c_str());
}
}
void CAdministeredModuleWrapper::registerProgress(const NLMISC::CSString& value) const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->registerProgress(value);
}
else
{
nlinfo("Latest: %s",value.c_str());
}
}
void CAdministeredModuleWrapper::setStateVariable(const NLMISC::CSString& variableName, const NLMISC::CSString& value) const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->setStateVariable(variableName,value);
}
else
{
nldebug("Setting: %s = %s",variableName.c_str(),value.c_str());
}
}
void CAdministeredModuleWrapper::appendStateVariable(const NLMISC::CSString& variableName, const NLMISC::CSString& value) const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->appendStateVariable(variableName,value);
}
else
{
nldebug("Appending: %s += %s",variableName.c_str(),value.c_str());
}
}
void CAdministeredModuleWrapper::clearStateVariable(const NLMISC::CSString& variableName) const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->clearStateVariable(variableName);
}
else
{
nldebug("Clearing: %s",variableName.c_str());
}
}
void CAdministeredModuleWrapper::clearAllStateVariables() const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->clearAllStateVariables();
}
else
{
nlinfo("Clearing all state variables...");
}
}
void CAdministeredModuleWrapper::broadcastStateInfo() const
{
if (_AdministeredModuleBase!=NULL)
{
_AdministeredModuleBase->broadcastStateInfo();
}
}