tags:

views:

561

answers:

1

With a Qt DBus proxy built on QDbusAbstractInterface (via qdbusxml2cpp), what's the best way to handle the service/object you want to interface to not being available when you start? Note: I'm not interested in simply knowing it (you can use BlahService.isValid() to find that out); I want to be able to know if it's valid, and know when it becomes valid so I can change state (and broadcast that state change with a signal), and on that state change do other stuff. Conversely, I want to know when it's no longer valid for similar reasons.

Without tracking the state of the service:

#define CONNECT_DBUS_SIG(x,y) connect(blah,SIGNAL(x),this,SLOT(y))

// FIX - should watch for service, and also handle it going away and
// coming back
blah = new BlahService("com.xyzzy.BlahService", "/com/xyzzy/BlahService",
                           QDBusConnection::sessionBus(), this);
if (!blah)
    return 0;
if (blah.isValid())
{
    CONNECT_DBUS_SIG(foo(),Event_foo());
}
else
{
    // Since we aren't watching for registration, what can we do but exit?
}

Probably we need to watch for NameOwnerChanged on the DBus connection object - unless QT's dbus code does this for us - and then when we get that signal change state, and if needed connect or disconnect the signals from the object.

All the examples I find either ignore the issue or simply exit if the server object doesn't exist, and don't deal with it going away. The Car/Controller Qt example at least notices if the server goes away and prints "Disconnected" if isValid() becomes false during use, but it's polling isValid().

Added:

Note that QtDbusAbtractInterface registers for changes of ownership of the server (NameOwnerChanged), and updates isValid() when changes occur. So I suspect you can connect to that serverOwnerChanged signal directly to find out about changes to ownership and use that as an indicator to try again - though you won't be able to trust isValid since it may be updated before or after you get signaled.

Alternatively (ugly) you can set up a timer and poll for isValid().

+1  A: 

Ok, since no one answered, I've found the answer in the meantime:

You want to watch NameOwnerChanged:

// subscribe to notifications about when a service is registered/unregistered
   connect(QDBusConnection::sessionBus().interface(),
           SIGNAL(serviceOwnerChanged(QString,QString,QString)),
           this,SLOT(serviceOwnerChanged(QString,QString,QString)));

and

void 
VcsApplicationController::serviceOwnerChanged(const QString &name,
                                              const QString &oldOwner,
                                              const QString &newOwner)
{
    Q_UNUSED(oldOwner);
    if (name == "com.foo.bar.FooService")
    {
        qLog(Whatever) << "serviceOwnerChanged" << name << oldOwner << newOwner;
        if (!newOwner.isEmpty())
        {
            // New owner in town
            emit Initialized();
            // or if you control the interface and both sides, you can wait for
            // a "Ready()" signal before declaring FooService ready for business.
        }
        else
        {
            // indicate we've lost connection, etc
            emit Uninitialized();
        }
    }
}

Note that there may be race conditions with doing methods on FooService from within serviceOwnerChanged - I'm not sure yet if they're a side-effect of the binding (dbus-c++ in my test case), or inherent in the design of dbus (possible - no on on the dbus mailing list will answer the question). If there is a real race condition, you can wait on a Ready()/whatever signal, if you control the DBus API. If you don't control the other end, you can add a very short delay or you can also watch AddMatch() to make sure the new owner has added a match on the name as well.

jesup