D-Bus API: added missing Session.Attach() (BMC #7761)

It was always part of the design that multiple clients can use any session.
Somehow we forgot to implement Session.Attach(), the call that allows a client
which hasn't started a session to register it's interest in the session. In
particular, that session won't go away unless the client exits or detaches.

This patch adds that missing call, a corresponding capability
("SessionAttach") and a unit test. The unit test covers recursive
attach/detach, it does not actually verify that the session remains
around.
This commit is contained in:
Patrick Ohly 2010-09-30 10:32:13 +02:00
parent cb67e29e0a
commit 320e23e31d
4 changed files with 86 additions and 17 deletions

View File

@ -65,6 +65,12 @@
</doc:definition>
</doc:item>
<doc:item><doc:term>SessionAttach</doc:term>
<doc:definition>Session.Attach()
implemented
</doc:definition>
</doc:item>
<doc:item><doc:term>Notifications</doc:term>
<doc:definition>Server.DisableNotifications()
and Server.EnableNotifications() implemented

View File

@ -2,8 +2,18 @@
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="org.syncevolution.Session">
<doc:doc><doc:description>
<doc:para>A Session object is used to do do syncs and to modify the server configurations. Clients can create a Session with Server.StartSession() and detach from it with Session.Detach().</doc:para>
<doc:para>Commands (other than Detach()) cannot be used before the status changes to "idle" (see GetStatus() and StatusChanged).</doc:para>
<doc:para>
A Session object is used to do do syncs and to modify
the server configurations. Clients can create a Session with
Server.StartSession(), attach to a session started by some other
client with Session.Attach(), and detach from it with
Session.Detach().
</doc:para>
<doc:para>
Commands (other than Attach() and Detach()) cannot be used
before the status changes to "idle" (see GetStatus() and
StatusChanged).
</doc:para>
</doc:description></doc:doc>
<method name="GetFlags">

View File

@ -1822,6 +1822,9 @@ class Session : public DBusObjectHelper,
/** Cmdline to execute command line args */
boost::shared_ptr<CmdlineWrapper> m_cmdline;
/** Session.Attach() */
void attach(const Caller_t &caller);
/** Session.Detach() */
void detach(const Caller_t &caller);
@ -1868,13 +1871,30 @@ class Session : public DBusObjectHelper,
static string syncStatusToString(SyncStatus state);
public:
/**
* Sessions must always be held in a shared pointer
* because some operations depend on that. This
* constructor function here ensures that and
* also adds a weak pointer to the instance itself,
* so that it can create more shared pointers as
* needed.
*/
static boost::shared_ptr<Session> createSession(DBusServer &server,
const std::string &peerDeviceID,
const std::string &config_name,
const std::string &session,
const std::vector<std::string> &flags = std::vector<std::string>());
~Session();
private:
Session(DBusServer &server,
const std::string &peerDeviceID,
const std::string &config_name,
const std::string &session,
const std::vector<std::string> &flags = std::vector<std::string>());
~Session();
boost::weak_ptr<Session> m_me;
public:
enum {
PRI_CMDLINE = -10,
PRI_DEFAULT = 0,
@ -2890,6 +2910,19 @@ string DBusSync::askPassword(const string &passwordName,
/***************** Session implementation ***********************/
void Session::attach(const Caller_t &caller)
{
boost::shared_ptr<Client> client(m_server.findClient(caller));
if (!client) {
throw runtime_error("unknown client");
}
boost::shared_ptr<Session> me = m_me.lock();
if (!me) {
throw runtime_error("session already deleted?!");
}
client->attach(me);
}
void Session::detach(const Caller_t &caller)
{
boost::shared_ptr<Client> client(m_server.findClient(caller));
@ -3163,6 +3196,17 @@ string Session::syncStatusToString(SyncStatus state)
};
}
boost::shared_ptr<Session> Session::createSession(DBusServer &server,
const std::string &peerDeviceID,
const std::string &config_name,
const std::string &session,
const std::vector<std::string> &flags)
{
boost::shared_ptr<Session> me(new Session(server, peerDeviceID, config_name, session, flags));
me->m_me = me;
return me;
}
Session::Session(DBusServer &server,
const std::string &peerDeviceID,
const std::string &config_name,
@ -3199,6 +3243,7 @@ Session::Session(DBusServer &server,
emitStatus(*this, "StatusChanged"),
emitProgress(*this, "ProgressChanged")
{
add(this, &Session::attach, "Attach");
add(this, &Session::detach, "Detach");
add(this, &Session::getFlags, "GetFlags");
add(this, &Session::getNormalConfigName, "GetConfigName");
@ -4053,10 +4098,10 @@ void Connection::process(const Caller_t &caller,
// run session as client or server
m_state = PROCESSING;
m_session.reset(new Session(m_server,
peerDeviceID,
config,
m_sessionID));
m_session = Session::createSession(m_server,
peerDeviceID,
config,
m_sessionID);
if (serverMode) {
m_session->initServer(SharedBuffer(reinterpret_cast<const char *>(message.second),
message.first),
@ -4691,6 +4736,7 @@ vector<string> DBusServer::getCapabilities()
capabilities.push_back("Notifications");
capabilities.push_back("Version");
capabilities.push_back("SessionFlags");
capabilities.push_back("SessionAttach");
return capabilities;
}
@ -4789,11 +4835,11 @@ void DBusServer::startSessionWithFlags(const Caller_t &caller,
caller,
watch);
std::string new_session = getNextSession();
boost::shared_ptr<Session> session(new Session(*this,
"is this a client or server session?",
server,
new_session,
flags));
boost::shared_ptr<Session> session = Session::createSession(*this,
"is this a client or server session?",
server,
new_session,
flags);
client->attach(session);
session->activate();
enqueue(session);
@ -5737,10 +5783,10 @@ void AutoSyncManager::startTask()
m_activeTask.reset(new AutoSyncTask(m_workQueue.front()));
m_workQueue.pop_front();
string newSession = m_server.getNextSession();
m_session.reset(new Session(m_server,
"",
m_activeTask->m_peer,
newSession));
m_session = Session::createSession(m_server,
"",
m_activeTask->m_peer,
newSession);
m_session->setPriority(Session::PRI_AUTOSYNC);
m_session->addListener(this);
m_server.enqueue(m_session);

View File

@ -554,7 +554,7 @@ class TestDBusServer(unittest.TestCase, DBusUtil):
"""check the Server.GetCapabilities() call"""
capabilities = self.server.GetCapabilities()
capabilities.sort()
self.failUnlessEqual(capabilities, ['ConfigChanged', 'GetConfigName', 'Notifications', 'SessionFlags', 'Version'])
self.failUnlessEqual(capabilities, ['ConfigChanged', 'GetConfigName', 'Notifications', 'SessionAttach', 'SessionFlags', 'Version'])
def testVersions(self):
"""check the Server.GetVersions() call"""
@ -910,6 +910,13 @@ class TestDBusSession(unittest.TestCase, DBusUtil):
self.failUnlessEqual(self.session.GetFlags(), [])
self.failUnlessEqual(self.session.GetConfigName(), "@default");
def testAttachSession(self):
"""attach to running session"""
self.session.Attach()
self.session.Detach()
self.failUnlessEqual(self.session.GetFlags(), [])
self.failUnlessEqual(self.session.GetConfigName(), "@default");
def testCreateSessionWithFlags(self):
"""ask for session with some specific flags and config"""
self.session.Detach()