Thursday, June 3, 2010

Verktyg - or how to tame signals and slots

Using the meta-object functionality of Qt gives every C++ developer a mighty tool at hand that makes many daily tasks solvable in an easy and elegant way. You can decouple components from each other via signals and slots, communicate over thread border in a safe way, add custom properties to any QObject based object and invoke methods via QMetaObject::invokeMethod() delayed by the event loop. However all these neat features come at a certain price, we basically lost type checking by the C++ compiler. This friendly companion tells us whenever we use the wrong type for a variable, pass the wrong number of arguments to a method or try to assign variables of incompatible type. When it comes to the QObject::connect() or QObject::disconnect() methods, our beloved C++ compiler reaches its limits, though. The following statement

QObject::connect( timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) );

is processed by the preprocessor and converted to

QObject::connect( timer, "timeout()", this, "slotTimeout()" );

So while the meta object compiler (moc) can parse the first representation and extracts all information it needs, the C++ compiler only gets two 'const char*' parameters where it doesn't know how to interpret them further. But who will make sure that the object 'timer' really provides a signal with the signature 'timeout()' and the 'this' object provides a slot with signature 'slotTimeout()'? Currently these information are only checked by QMetaObject on runtime and if an error is detected, a message similar to the following is printed to the standard output:

QObject::connect: No such signal MyClass::foobar()

The problem with runtime checking is, that errors can only be detected if the code is really executed during testing. However today's software is so complex that it is mostly impossible to execute all possible code paths during the test phase, we have to find another way...

So when do broken signal/slot connections occur? A quick search over the KDE code base shows that broken connections exists mostly in outdated or seldom run code (e.g. unit tests !!!) and in code that is under heavy development and refactoring. During refactoring signals and slots are moved around, will be renamed or removed completely from the code base. While all direct calls to the missing/renamed methods will trigger a compile error, the outdated signatures inside the QObject::[dis]connect() calls won't be noticed and lead to difficult to find bugs (yes, I mean the really ugly ones...).

After 8 years of studying computer science (Rome wasn't built in a day either ;)) it is now the time to write my diploma thesis, and since until now there is no application available that solves the problem described above, I asked KDAB Germany whether they would be interested to act as supervisor for a thesis that will address this issue. KDAB does a lot of software porting from Motif, MFC and Qt3 towards Qt4 and faces the problems of broken signal/slot connections in a larger scale, so they agreed and now Till Adam (aka KDE-PIM/KMail dude) is my official supervisor :)

Since the work started already on April 1th (no joke ;)), we have a working application now. Its name is 'Verktyg' and it is based on the KDevPlatform and KDevelop C++ parser. At this point I have to say a big Thank You! to my colleagues at KDAB Milian Wolff and Bertjan Broeksema who helped me to get into the KDevelop code quite fast and motivated me to keep on using this framework :) So what can Verktyg do for you? Verktyg can analyze CMake/Qt based projects statically for broken signal/slot connections, wrong usage of QMetaObject::invokeMethod() and missuses of QObject::setProperty()/QObject::property(). The code is available under GPL and can be cloned from Everything you need to know to compile and setup Verktyg is described in the README file inside the source directory. I know that deployment is still a bit tricky, you need a current version (aka git master) of KDevPlatform and KDevelop and additionally the kdevcpptools. But once you have Verktyg running, it will reward you with nice error reports like the following from kdepimlibs:

WARN: qgpgme/eventloopinteractor_win.cpp:[35:4] Class QObject has no slot 'slotReadActivity(int)' but its subclass EventLoopInteractor does, is it really used here?
WARN: qgpgme/eventloopinteractor_win.cpp:[41:4] Class QObject has no slot 'slotWriteActivity(int)' but its subclass EventLoopInteractor does, is it really used here?
WARN: akonadi/job.cpp:[280:13] Class KJob has no signal 'aboutToStart(Akonadi::Job*)' but its subclass Job does, is it really used here?
WARN: akonadi/session.cpp:[135:22] Class QIODevice has no signal 'disconnected()' but its subclass QAbstractSocket does, is it really used here?
WARN: akonadi/contact/contactdefaultactions.cpp:[51:13] Class QObject has no signal 'urlClicked(KUrl)' but its subclass ContactViewer does, is it really used here?
WARN: kontactinterface/core.cpp:[92:4] Class QObject has no slot 'slotPartDestroyed(QObject*)' but its subclass Core does, is it really used here?
ERRO: kblog/tests/testlivejournal.cpp:[358:11] Class LiveJournal has no signal 'fetchedUserInfo(QMap)'

As you can see there are some warnings, where Verktyg cannot decide whether the signal or slot is valid or not, because an implicit type (QObject) is passed to the connect statement instead of the explicit type, but at least it gives you some hints which makes the manual review a lot easier (Thanks to Volker Krause for the idea!). But there is also an error, which in fact is a real error that should be fixed... (did I mention outdated unit tests already?!? ;)).

So if you like to play around with Verktyg and need some help to set it up or if you have some nice ideas how to improve it, feel free to contact me via mail or ping me on IRC.

Happy analyzing!

Thursday, February 4, 2010

CalDAV/CardDAV/GroupDAV Support for Akonadi

Another month, another new resource for Akonadi ;) Well, actually the resource has been started by Grégory Oestreicher 3 month ago and just recently I came across the code in playground and gave it a try. After some code cleanup (to simplify the later move to kdepim/runtime/resources) I started to refactor some parts of the resource to better match the design of Akonadi. Some of the features have been removed during that work (e.g. only reload data that have been changed on the server), but they will come back in the future.
Initially the resource was designed to support the CalDAV protocol and the calendaring part of the GroupDAV protocol, because these two protocols are basically the same, with some small differences, most of the code for listing, loading and saving events and todos can be shared.
However the GroupDAV protocol supports handling of contacts as well and there is a new standard, called CardDAV, which is the contact handling equivalent to CalDAV. The logical consequence was to add support for CardDAV and adapt the GroupDAV implementation to handle contacts as well. While we are still working on iron out some bugs and test the resource against all free available groupware servers, we plan for the future...

At the moment you'll see a configuration dialog like this:

Here you can configure which protocol to use, what the URLs for the protocol handlers are and what login credentials shall be used. Unfortunately that's much to technically. In the ideal world one would have a configuration dialog that provides a list of supported groupware servers and input fields for the login credentials, everything else should be setup automatically by the resource. And that's indeed the next big task on our TODO list, so be excited :)

Now follows another screenshot of akonadiconsole with two resources loaded (one for CardDav and one for CalDav) that a configured to access the SOGo demo system:

Sunday, January 24, 2010

Open-Xchange Support for Akonadi

My colleague at credativ GmbH, Roland Wolters, has already blogged about it, KDE 4.5 will have decent support for the Open-Xchange groupware server again. Today I moved the Akonadi resource from trunk/playground/pim to trunk/kdepim/runtime/resources, so it should be a bit easier to test it for the brave ones of you. KDE 3 already had a resource (based on the old KResource framework) that supported Open-Xchange, however it also contained code for accessing the SLOX (SuSE Linux Open Xchange) groupware server, which is discontinued in favor of Open-Xchange. So the new resource dropped support for SLOX, which makes the code a lot cleaner and easier to understand ;) Furthermore the Akonadi framework provides most of the useful stuff like handling offline mode or providing a cache, so these features we got for free in the new resource.

So if you want to test the Open-Xchange support, at first you need a Open-Xchange server to connect to... in my case I was allowed to use the company server for testing and development, so the boring work of setting up a server was already done ;) However there are several HOWTOs available on the Internet, so if you have no working server yet, it's only a question of diligence. The next step is to add the Open-Xchange resource to Akonadi. As early tester you can start the akonadiconsole executable from console and choose the 'Open-Xchange Groupware Server' resource. Now the configuration dialog appears where you can enter the URL to the server and the login credentials:

The dialog also provides a button for testing whether the connection can be established. In my setup, the server is running on the company host and port 433 is forwarded via SSH to my local port 1443, therefor the strange URL ;)

After the configuration dialog is closed, the initial loading of the data is started and when this is done, you should see all available folders of the Open-Xchange server in the collection tree:

Every user has her own private address book, calendar and task list under 'Private Folder'. You can add additional folders there from within KAddressBook and KOrganizer. Below 'Public Folders' the folders are listed where all users have read/write permissions for. 'Shared Folder' contains sub folders which have been published by co-workers with restricted permissions. The 'System Folder' contains the global, LDAP based, address book. If you start KAddressBook now, all address book folders will be listed there:

As you can see, all contacts from the web interface show up in KAddressBook as well. In opposite to the old resource, the new one has support for contact groups, so all distribution lists from the server are accessible in KAddressBook now. Starting the Akonadi based KOrganizer from trunk will show you the following:

Both, the private calendar and task list show up and can be included into the current view. Again you can see all events from the web interface in KOrganizer as well.

So porting the old resource to Akonadi was really fun, because this time we have a stable base (read Akonadi) we can rely on and most of the difficult to implement stuff you get for free. The rough port took me around 10 hours (including a lot of code cleanup), so I want to encourage everybody to give it a try to implement new resources or port existing ones to Akonadi, because without the resources the new shiny applications are quite useless ;)