D-Bus API: allow setting different configs in the same session

Forcing D-Bus API users to start a new session for each config that
they want to modify adds considerable complexity to clients, in
particular for local sync.

Relaxing that requirements is easy because the current implementation
already only runs one session at a time. It's also only a minor
restriction for future improvements: sessions which request access to
all configs cannot run in parallel with other sessions.

Thus this patch adds Session.SetNamedConfig() and (for the sake of
completeness) Session.GetNamedConfig().

Temporary config changes via SetNamedConfig() are harder to implement
and not currently supported. They are part of the API though, just in
case.
This commit is contained in:
Patrick Ohly 2011-10-18 16:37:14 +02:00
parent c4e69e2e58
commit bb710674da
8 changed files with 154 additions and 18 deletions

View File

@ -96,6 +96,13 @@
"evolutionuser", "evolutionpassword"; semantic is unchanged
</doc:definition>
</doc:item>
<doc:item><doc:term>NamedConfig</doc:term>
<doc:definition>Session.Get/SetNamedConfig()
are implemented
</doc:definition>
</doc:item>
</doc:list>
</doc:para>
</doc:description>
@ -485,10 +492,13 @@
<doc:item><doc:term>no-sync</doc:term>
<doc:definition>session will not be used for running a synchronization</doc:definition>
</doc:item>
<doc:item><doc:term>all-configs</doc:term>
<doc:definition>session will provide read/write access to all configurations, via Get/SetNamedConfig()</doc:definition>
</doc:item>
</doc:list>
</doc:description></doc:doc>
<arg type="s" name="config" direction="in">
<doc:doc><doc:summary>name of configuration to be created or used in session</doc:summary></doc:doc>
<doc:doc><doc:summary>name of configuration to be created or used in session; typically this will be empty when used in combination with 'all-configs' and Get/SetNamedConfig()</doc:summary></doc:doc>
</arg>
<arg type="as" name="flags" direction="in">
<doc:doc><doc:summary>optional flags</doc:summary></doc:doc>

View File

@ -48,9 +48,9 @@
</method>
<method name="GetConfig">
<doc:doc><doc:description>Get the configuration of the server</doc:description></doc:doc>
<doc:doc><doc:description>Get the configuration identified by the name given to StartSession()</doc:description></doc:doc>
<arg type="b" name="tmplate" direction="in">
<doc:doc><doc:summary>if TRUE, will return a matching template configuration, otherwise will return a matching server configuration</doc:summary></doc:doc>
<doc:doc><doc:summary>if TRUE, will return a matching template configuration, otherwise will return the stored configuration</doc:summary></doc:doc>
</arg>
<arg type="a{sa{ss}}" name="configuration" direction="out">
<doc:doc><doc:summary>server configuration</doc:summary></doc:doc>
@ -107,6 +107,32 @@
<annotation name="com.trolltech.QtDBus.QtTypeName.In2" value="QStringMultiMap"/>
</method>
<method name="GetNamedConfig">
<doc:doc><doc:description>Get the configuration identified by
the name given in the first argument; same as
Server.GetConfig(), provided again in Session for the sake of
completeness</doc:description></doc:doc>
<arg type="s" name="name" direction="in">
<doc:doc><doc:summary>configuration name</doc:summary></doc:doc>
</arg>
<arg type="b" name="tmplate" direction="in"/>
<arg type="a{sa{ss}}" name="configuration" direction="out"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QStringMultiMap"/>
</method>
<method name="SetNamedConfig">
<doc:doc><doc:description>Same as SetConfig() except that the modified configuration is named explicitly</doc:description></doc:doc>
<arg type="s" name="name" direction="in">
<doc:doc><doc:summary>configuration name; if exactly the same as in StartSession() or StartSessionWithFlags(), then SetNamedConfig() behaves exactly like SetConfig() and none of the constraints for SetNamedConfig() apply</doc:summary></doc:doc>
</arg>
<arg type="b" name="update" direction="in"/>
<arg type="b" name="temporary" direction="in">
<doc:doc><doc:summary>temporary changes of the configuration are currently only supported for the configuration chosen when creating the session</doc:summary></doc:doc>
</arg>
<arg type="a{sa{ss}}" name="configuration" direction="in"/>
<annotation name="com.trolltech.QtDBus.QtTypeName.In2" value="QStringMultiMap"/>
</method>
<method name="GetReports">
<doc:doc><doc:description>Get synchronization reports for the server</doc:description></doc:doc>
<arg type="u" name="start" direction="in">

View File

@ -96,6 +96,13 @@ boost::shared_ptr<DBusUserInterface> ReadOperations::getLocalConfig(const string
void ReadOperations::getConfig(bool getTemplate,
Config_t &config)
{
getNamedConfig(m_configName, getTemplate, config);
}
void ReadOperations::getNamedConfig(const std::string &configName,
bool getTemplate,
Config_t &config)
{
map<string, string> localConfigs;
boost::shared_ptr<SyncConfig> dbusConfig;
@ -107,7 +114,7 @@ void ReadOperations::getConfig(bool getTemplate,
string peer, context;
boost::shared_ptr<SyncConfig::TemplateDescription> peerTemplate =
m_server.getPeerTempl(m_configName);
m_server.getPeerTempl(configName);
if(peerTemplate) {
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(peerTemplate->m_templateId),
peer, context);
@ -137,13 +144,13 @@ void ReadOperations::getConfig(bool getTemplate,
syncURL = "obex-bt://";
syncURL += peerTemplate->m_deviceId;
} else {
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
peer, context);
dbusConfig = SyncConfig::createPeerTemplate(peer);
}
if(!dbusConfig.get()) {
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + configName + "' found");
}
// use the shared properties from the right context as filter
@ -166,11 +173,11 @@ void ReadOperations::getConfig(bool getTemplate,
}
syncConfig = dbusConfig.get();
} else {
dbusUI = getLocalConfig(m_configName);
dbusUI = getLocalConfig(configName);
//try to check password and read password from gnome keyring if possible
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties());
prop->checkPassword(*dbusUI, configName, *dbusUI->getProperties());
}
list<string> configuredSources = dbusUI->getSyncSources();
BOOST_FOREACH(const string &sourceName, configuredSources) {
@ -178,7 +185,7 @@ void ReadOperations::getConfig(bool getTemplate,
SyncSourceNodes sourceNodes = dbusUI->getSyncSourceNodes(sourceName);
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties(),
prop->checkPassword(*dbusUI, configName, *dbusUI->getProperties(),
sourceName, sourceNodes.getProperties());
}
}
@ -212,7 +219,7 @@ void ReadOperations::getConfig(bool getTemplate,
localConfigs.insert(make_pair("ConsumerReady", "1"));
}
// insert 'configName' of the chosen config (m_configName is not normalized)
// insert 'configName' of the chosen config (configName is not normalized)
localConfigs.insert(pair<string, string>("configName", syncConfig->getConfigName()));
config.insert(pair<string,map<string, string> >("", localConfigs));

View File

@ -61,6 +61,11 @@ public:
void getConfig(bool getTemplate,
Config_t &config);
/** implementation of D-Bus GetNamedConfig() for configuration named in parameter */
void getNamedConfig(const std::string &configName,
bool getTemplate,
Config_t &config);
/** implementation of D-Bus GetReports() for m_configName as server configuration */
void getReports(uint32_t start, uint32_t count,
Reports_t &reports);

View File

@ -73,6 +73,7 @@ vector<string> Server::getCapabilities()
capabilities.push_back("ConfigChanged");
capabilities.push_back("GetConfigName");
capabilities.push_back("NamedConfig");
capabilities.push_back("Notifications");
capabilities.push_back("Version");
capabilities.push_back("SessionFlags");

View File

@ -28,6 +28,8 @@
#include "client.h"
#include "dbus-sync.h"
#include <boost/foreach.hpp>
using namespace GDBusCXX;
SE_BEGIN_CXX
@ -114,8 +116,16 @@ static void setSyncFilters(const ReadOperations::Config_t &config,FilterConfigNo
}
}
}
void Session::setConfig(bool update, bool temporary,
const ReadOperations::Config_t &config)
{
setNamedConfig(m_configName, update, temporary, config);
}
void Session::setNamedConfig(const std::string &configName,
bool update, bool temporary,
const ReadOperations::Config_t &config)
{
if (!m_active) {
SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
@ -124,11 +134,30 @@ void Session::setConfig(bool update, bool temporary,
string msg = StringPrintf("%s started, cannot change configuration at this time", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
}
// avoid the check if effect is the same as setConfig()
if (m_configName != configName) {
bool found = false;
BOOST_FOREACH(const std::string &flag, m_flags) {
if (boost::iequals(flag, "all-configs")) {
found = true;
break;
}
}
if (!found) {
SE_THROW_EXCEPTION(InvalidCall,
"SetNameConfig() only allowed in 'all-configs' sessions");
}
m_server.getPresenceStatus().updateConfigPeers (m_configName, config);
if (temporary) {
SE_THROW_EXCEPTION(InvalidCall,
"SetNameConfig() with temporary config change only supported for config named when starting the session");
}
}
m_server.getPresenceStatus().updateConfigPeers (configName, config);
/** check whether we need remove the entire configuration */
if(!update && !temporary && config.empty()) {
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(getConfigName()));
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(configName));
if(syncConfig.get()) {
syncConfig->remove();
m_setConfig = true;
@ -166,10 +195,10 @@ void Session::setConfig(bool update, bool temporary,
m_tempConfig = true;
} else {
/* need to save configurations */
boost::shared_ptr<SyncConfig> from(new SyncConfig(getConfigName()));
boost::shared_ptr<SyncConfig> from(new SyncConfig(configName));
/* if it is not clear mode and config does not exist, an error throws */
if(update && !from->exists()) {
SE_THROW_EXCEPTION(NoSuchConfig, "The configuration '" + getConfigName() + "' doesn't exist" );
SE_THROW_EXCEPTION(NoSuchConfig, "The configuration '" + configName + "' doesn't exist" );
}
if(!update) {
list<string> sources = from->getSyncSources();
@ -202,7 +231,7 @@ void Session::setConfig(bool update, bool temporary,
for ( it = sourceFilters.begin(); it != sourceFilters.end(); it++ ) {
from->setConfigFilter(false, it->first, it->second);
}
boost::shared_ptr<DBusSync> syncConfig(new DBusSync(getConfigName(), *this));
boost::shared_ptr<DBusSync> syncConfig(new DBusSync(configName, *this));
syncConfig->prepareConfigForWrite();
syncConfig->copy(*from, NULL);
@ -432,7 +461,9 @@ Session::Session(Server &server,
add(this, &Session::getNormalConfigName, "GetConfigName");
add(static_cast<ReadOperations *>(this), &ReadOperations::getConfigs, "GetConfigs");
add(static_cast<ReadOperations *>(this), &ReadOperations::getConfig, "GetConfig");
add(static_cast<ReadOperations *>(this), &ReadOperations::getNamedConfig, "GetNamedConfig");
add(this, &Session::setConfig, "SetConfig");
add(this, &Session::setNamedConfig, "SetNamedConfig");
add(static_cast<ReadOperations *>(this), &ReadOperations::getReports, "GetReports");
add(static_cast<ReadOperations *>(this), &ReadOperations::checkSource, "CheckSource");
add(static_cast<ReadOperations *>(this), &ReadOperations::getDatabases, "GetDatabases");

View File

@ -367,6 +367,11 @@ public:
void setConfig(bool update, bool temporary,
const ReadOperations::Config_t &config);
/** Session.SetNamedConfig() */
void setNamedConfig(const std::string &configName,
bool update, bool temporary,
const ReadOperations::Config_t &config);
typedef StringMap SourceModes_t;
/** Session.Sync() */
void sync(const std::string &mode, const SourceModes_t &source_modes);

View File

@ -593,9 +593,9 @@ class DBusUtil(Timeout):
DBusUtil.quit_events = []
return (sessionpath, session)
def setUpSession(self, config):
def setUpSession(self, config, flags=[]):
"""stores ready session in self.sessionpath and self.session"""
self.sessionpath, self.session = self.createSession(config, True)
self.sessionpath, self.session = self.createSession(config, True, flags)
def progressChanged(self, *args):
'''subclasses override this method to write specified callbacks for ProgressChanged signals
@ -796,7 +796,7 @@ class TestDBusServer(unittest.TestCase, DBusUtil):
"""TestDBusServer.testCapabilities - Server.Capabilities()"""
capabilities = self.server.GetCapabilities()
capabilities.sort()
self.assertEqual(capabilities, ['ConfigChanged', 'DatabaseProperties', 'GetConfigName', 'Notifications', 'SessionAttach', 'SessionFlags', 'Version'])
self.assertEqual(capabilities, ['ConfigChanged', 'DatabaseProperties', 'GetConfigName', 'NamedConfig', 'Notifications', 'SessionAttach', 'SessionFlags', 'Version'])
def testVersions(self):
"""TestDBusServer.testVersions - Server.GetVersions()"""
@ -1008,6 +1008,57 @@ class TestDBusServerTerm(unittest.TestCase, DBusUtil):
else:
self.fail("no exception thrown")
class TestNamedConfig(unittest.TestCase, DBusUtil):
"""Tests for Set/GetNamedConfig"""
def setUp(self):
self.setUpServer()
def run(self, result):
self.runTest(result)
def testSetNamedConfigError(self):
"""TestDBusSession.testSetNamedConfigError - SetNamedConfig() only allowed in 'all-configs' sessions"""
self.setUpSession("")
try:
self.session.SetNamedConfig("foobar", False, False, {})
except dbus.DBusException, ex:
self.assertEqual(str(ex),
"org.syncevolution.InvalidCall: SetNameConfig() only allowed in 'all-configs' sessions")
else:
self.fail("no exception thrown")
def testSetNamedConfigErrorTemporary(self):
"""TestDBusSession.testSetNamedConfigErrorTemporary - SetNamedConfig() only implemented for session config"""
self.setUpSession("foo", [ "all-configs" ])
try:
self.session.SetNamedConfig("foobar", False, True, {})
except dbus.DBusException, ex:
self.assertEqual(str(ex),
"org.syncevolution.InvalidCall: SetNameConfig() with temporary config change only supported for config named when starting the session")
else:
self.fail("no exception thrown")
self.session.Detach()
self.setUpSession("")
self.session.SetNamedConfig("", False, True, {})
def testSetNamedConfig(self):
"""TestDBusSession.testSetNamedConfig - create two different configs in one session"""
self.setUpSession("", [ "all-configs" ])
fooConfig = {"": {"username": "foo", "configName": "foo"}}
barConfig = {"": {"username": "bar", "configName": "bar"}}
self.session.SetNamedConfig("foo", False, False, fooConfig)
self.session.SetNamedConfig("bar", False, False, barConfig)
self.assertEqual(fooConfig, self.server.GetConfig("foo", False))
self.assertEqual(barConfig, self.server.GetConfig("bar", False))
self.assertEqual(fooConfig, self.session.GetNamedConfig("foo", False))
self.assertEqual(barConfig, self.session.GetNamedConfig("bar", False))
class Connman (dbus.service.Object):
count = 0