Skip to content

Me Nepomuk, You Nepomuk, too?

Tuesday, 18 March 2008  |  trueg

Now that the Nepomuk project review is done I can get back to promoting Nepomuk features and possibilities. Today I will show how existing Nepomuk and Soprano technologies can be combined to provide very simple "Social" capabilities. In a previous blog entry I presented the Nepomuk search client which allows to search the Nepomuk data store based on installed types and properties. Now how about taking that, wrapping it in the simple Soprano tcp server/client system and announcing it via Avahi? That would allow us to query our buddies' Nepomuk data. I did exactly that and the result are two little tools with very fancy names: The Nepomuk Social Query Daemon and Client.

[image:3333 align=center size=preview]

The nsqd, the Nepomuk Social Query Daemon, is a kded module which provides an Avahi service that is then found by the nsqclient, the Nepomuk Social Query Client, which allows to perform queries on remote data in the exact same way as the normal search client does. Easy. Now as this is just a showcase tool there is close to no security except for read-only access. Thus, once the nsqd is running everyone able to open a connection to the server port is able to read your data. So there is room for improvement. ;)

The nsqd and nsqclient can be found (like all experimental Nepomuk stuff) in the KDE svn playground module. Due to the security issues the nsqd is not started by default but has to be started manually:

qdbus org.kde.kded /kded org.kde.kded.loadModule nsqd

Implementation If you are not interested in hacking details just stop reading now. Let's look at some details. The most simple thing first: the read-only access. I did this by implementing a read only Soprano Model (which I actually moved to Soprano::Utils since it has a clean API and seems usable beyond this example). This is actually pretty simple: I just derived it from Soprano::Model and made all writing methods throw a "permission denied" error. (I did not derive from Soprano::FilterModel since then one could easily access the parent model and write to it anyway.) Exposing the local Nepomuk store through TCP was simple, too. Soprano already comes with a simple binary TCP server/client implementation (which I used before the cleaner D-Bus one). So all I had to do was create a Soprano::Server::ServerCore implementation which forwards all calls to the local Nepomuk server. This is actually pretty simple. In our new ServerCore subclass we create a connection to the Nepomuk server:

SopranoForwardingCore::SopranoForwardingCore( QObject* parent )
    : ServerCore( parent )
{
    m_client = new Soprano::Client::DBusClient( "org.kde.NepomukServer", this );
[...]

Then we have a cache for the models which store the wrapping ReadOnlyModel instances and simply forward:

Soprano::Model* SopranoForwardingCore::model( const QString& name )
{
    if ( m_models.contains( name ) ) {
        return m_models[name];
    }
    else {
        if ( Soprano::Model* model = m_client->createModel( name ) ) {
            Soprano::Util::ReadOnlyModel* roModel = new Soprano::Util::ReadOnlyModel( model );
            model->setParent( roModel ); // memory management
            m_models.insert( name, roModel );
            return roModel;
        }
    }
    return 0;
}

Last but not least we start the server core, i.e. make it listen on some port and promote the service through Avahi:

m_serverCore = new SopranoForwardingCore( this );
m_serverCore->listen( 0 );
m_dnssdService = new DNSSD::PublicService( "Nepomuk Social Query Service","_nepomuk._tcp", m_serverCore->serverPort() );
m_dnssdService->publish();

Now the nsqclient can discover our service through Avahi and connect to it using Soprano::Client::TcpClient:

Soprano::Model* createModel( DNSSD::RemoteService::Ptr service, const QString& name )
{
    Soprano::Client::TcpClient* sopranoTcpClient = new Soprano::Client::TcpClient();
    QHostInfo hostInfo = QHostInfo::fromName( service->hostName() );
    sopranoTcpClient->connect( hostInfo.addresses().first(), service->port() );
    return sopranoTcpClient->createModel( name );
}

Again: easy. Ok, that is all for today. I feel I am getting too technical again anyway.