aboutsummaryrefslogtreecommitdiff
path: root/libcontextsubscriber
diff options
context:
space:
mode:
authorGergely Risko <gergely+context@risko.hu>2009-09-16 16:46:22 +0300
committerGergely Risko <gergely+context@risko.hu>2009-09-16 16:46:22 +0300
commit7cf44bded328c27c6cbe18300ed379c4d706bc16 (patch)
tree72c7aaeb1d39365c11d9921303bdb8cc8a927fb0 /libcontextsubscriber
parent23f77ad83b75a8a9256c6c72ca855ba1a358efd8 (diff)
parent3b6abe1576cc24652d91179161f0960ffb2c6ff3 (diff)
Merge commit 'origin/master' into cdbqvariant
Diffstat (limited to 'libcontextsubscriber')
-rw-r--r--libcontextsubscriber/cli/commandwatcher.cpp11
-rw-r--r--libcontextsubscriber/customer-tests/Makefile.am5
-rwxr-xr-xlibcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py41
-rw-r--r--libcontextsubscriber/customer-tests/common/cltool.py80
-rw-r--r--libcontextsubscriber/customer-tests/env.sh6
-rwxr-xr-xlibcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py90
-rw-r--r--libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp4
-rw-r--r--libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp4
-rwxr-xr-xlibcontextsubscriber/customer-tests/runTests.sh7
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/Makefile.am2
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp68
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin.h59
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore2
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am30
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore2
-rw-r--r--libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am30
-rw-r--r--libcontextsubscriber/src/Makefile.am6
-rw-r--r--libcontextsubscriber/src/contextpropertyinfo.cpp47
-rw-r--r--libcontextsubscriber/src/contextpropertyinfo.h8
-rw-r--r--libcontextsubscriber/src/iproviderplugin.h7
-rw-r--r--libcontextsubscriber/src/loggingfeatures.h1
-rw-r--r--libcontextsubscriber/src/propertyhandle.cpp13
-rw-r--r--libcontextsubscriber/src/provider.cpp52
-rw-r--r--libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h1
-rw-r--r--libcontextsubscriber/unit-tests/provider/Makefile.am4
25 files changed, 521 insertions, 59 deletions
diff --git a/libcontextsubscriber/cli/commandwatcher.cpp b/libcontextsubscriber/cli/commandwatcher.cpp
index a4841d96..c4b8d11e 100644
--- a/libcontextsubscriber/cli/commandwatcher.cpp
+++ b/libcontextsubscriber/cli/commandwatcher.cpp
@@ -17,7 +17,6 @@
CommandWatcher::CommandWatcher(int commandfd, QMap<QString, ContextProperty*> *properties, QObject *parent) :
QObject(parent), commandfd(commandfd), properties(properties)
{
- fcntl(commandfd, F_SETFL, O_NONBLOCK);
commandNotifier = new QSocketNotifier(commandfd, QSocketNotifier::Read, this);
sconnect(commandNotifier, SIGNAL(activated(int)), this, SLOT(onActivated()));
help();
@@ -29,6 +28,7 @@ void CommandWatcher::onActivated()
static QByteArray commandBuffer = "";
static char buf[1024];
int readSize;
+ fcntl(commandfd, F_SETFL, O_NONBLOCK);
while ((readSize = read(commandfd, &buf, 1024)) > 0)
commandBuffer += QByteArray(buf, readSize);
@@ -61,12 +61,14 @@ void CommandWatcher::help()
qDebug() << " type KEY - get the info()->type for a key";
qDebug() << " plugin KEY - get the info()->plugin for a key";
qDebug() << " constructionstring KEY - get the info()->constructionstring for a key";
+ qDebug() << " flush - write FLUSHED to stderr and stdout";
qDebug() << "Any prefix of a command can be used as an abbreviation";
}
void CommandWatcher::interpret(const QString& command) const
{
QTextStream out(stdout);
+ QTextStream err(stderr);
if (command == "") {
help();
} else {
@@ -74,7 +76,7 @@ void CommandWatcher::interpret(const QString& command) const
QString commandName = args[0];
args.pop_front();
- if (args.size() == 0) {
+ if (args.size() == 0 && !QString("flush").startsWith(commandName)) {
help();
return;
}
@@ -163,6 +165,11 @@ void CommandWatcher::interpret(const QString& command) const
out << "constructionstring: " << properties->value(key)->info()->constructionString() << endl;
else
qDebug() << "no such key:" << key;
+ } else if (QString("flush").startsWith(commandName)) {
+ out << "FLUSHED" << endl;
+ out.flush();
+ err << "FLUSHED" << endl;
+ err.flush();
} else
help();
}
diff --git a/libcontextsubscriber/customer-tests/Makefile.am b/libcontextsubscriber/customer-tests/Makefile.am
index 8eed733d..8ec2e565 100644
--- a/libcontextsubscriber/customer-tests/Makefile.am
+++ b/libcontextsubscriber/customer-tests/Makefile.am
@@ -1,4 +1,5 @@
-SUBDIRS = update-contextkit-providers
+SUBDIRS = update-contextkit-providers testplugins
+CHECKSUBDIRS = update-contextkit-providers
libcontextsubscribertestsdir = $(datadir)/libcontextsubscriber-tests
libcontextsubscribertests_DATA = tests.xml
@@ -6,7 +7,7 @@ libcontextsubscribertests_DATA = tests.xml
check-customer:
$(MAKE) -C $(top_srcdir) all
./runTests.sh
- for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done
+ for i in $(CHECKSUBDIRS); do $(MAKE) -C $$i $@; done
CLEANFILES = *.pyc
diff --git a/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py b/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py
index 11180b58..d1de94af 100755
--- a/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py
+++ b/libcontextsubscriber/customer-tests/asynchronicity/asynchronicity.py
@@ -32,16 +32,9 @@ import os
import signal
import re
import time
-
import unittest
from subprocess import Popen, PIPE
-
-def timeoutHandler(signum, frame):
- raise Exception('tests has been running for too long')
-
-class Callable:
- def __init__(self, anycallable):
- self.__call__ = anycallable
+from cltool import CLTool
class Asynchronous(unittest.TestCase):
def startProvider(busname, args):
@@ -51,15 +44,7 @@ class Asynchronous(unittest.TestCase):
print >>ret.stdin, "info()"
ret.stdout.readline().rstrip()
return ret
- startProvider = Callable(startProvider)
-
- def wanted(name, type, value):
- return "%s = %s:%s" % (name, type, value)
- wanted = Callable(wanted)
-
- def wantedUnknown(name):
- return "%s is Unknown" % (name)
- wantedUnknown = Callable(wantedUnknown)
+ startProvider = staticmethod(startProvider)
#SetUp
def setUp(self):
@@ -97,20 +82,19 @@ class Asynchronous(unittest.TestCase):
"""
# check the fast property
- self.context_client = Popen(["context-listen", "test.fast", "test.slow"],
- stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ self.context_client = CLTool("context-listen", "test.fast", "test.slow")
- got = self.context_client.stdout.readline().rstrip()
- self.assertEqual(got,
- self.wanted("test.fast", "int", "42"),
- "Bad value for the fast property")
+ self.assert_(self.context_client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.fast", "int", "42"),
+ 1), # timeout == 1 second
+ "Bad value for the fast property, wanted 42, communication:")
fast_time = time.time()
# check the slow property
- got = self.context_client.stdout.readline().rstrip()
- self.assertEqual(got,
- self.wanted("test.slow", "int", "42"),
- "Bad value for the slow property")
+ self.assert_(self.context_client.expect(CLTool.STDOUT,
+ CLTool.wanted("test.slow", "int", "42"),
+ 10), # timeout == 10 second max, but 3 is enough usually
+ "Bad value for the slow property, wanted 42, communication:")
slow_time = time.time()
self.assert_(slow_time - fast_time > 2.0,
@@ -129,9 +113,6 @@ def runTests():
result = unittest.TextTestRunner(verbosity=2).run(suiteInstallation)
return len(result.errors + result.failures)
-
if __name__ == "__main__":
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
- signal.signal(signal.SIGALRM, timeoutHandler)
- signal.alarm(10)
sys.exit(runTests())
diff --git a/libcontextsubscriber/customer-tests/common/cltool.py b/libcontextsubscriber/customer-tests/common/cltool.py
new file mode 100644
index 00000000..c80ce355
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/common/cltool.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+##
+## Copyright (C) 2008, 2009 Nokia. All rights reserved.
+##
+## Contact: Marius Vollmer <marius.vollmer@nokia.com>
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## version 2.1 as published by the Free Software Foundation.
+##
+## This library is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+## 02110-1301 USA
+
+import re
+import time
+from subprocess import Popen, PIPE
+
+class CLTool:
+ STDOUT = 1
+ STDERR = 2
+ def __init__(self, *cline):
+ self.__process = Popen(cline, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ self.__io = []
+
+ def send(self, string):
+ self.__io.append((0, string))
+ print >>self.__process.stdin, string
+
+ def expect(self, fileno, exp_str, timeout):
+ stream = 0
+ if fileno == self.STDOUT: stream = self.__process.stdout
+ if fileno == self.STDERR: stream = self.__process.stderr
+ if stream == 0: return False
+
+ print >>self.__process.stdin, "flush"
+ cur_str = ""
+ start_time = time.time()
+ while True:
+ line = stream.readline().rstrip()
+ if line == "FLUSHED":
+ if re.match(exp_str, cur_str):
+ return True
+ else:
+ time.sleep(0.1)
+ if time.time() - start_time > timeout:
+ self.printio()
+ print "Expected:", exp_str
+ print "Received before the timeout:\n", cur_str
+ return False
+ print >>self.__process.stdin, "flush"
+ else:
+ cur_str += line + "\n"
+ self.__io.append((fileno, line))
+
+ def printio(self):
+ print
+ print '----------------------------------------------------'
+ for line in self.__io:
+ if line[0] == 0:
+ print "[IN] <<<", line[1]
+ if line[0] == 1:
+ print "[OU] >>>", line[1]
+ if line[0] == 2:
+ print "[ER] >>>", line[1]
+ print '----------------------------------------------------'
+
+ def wanted(name, type, value):
+ return "%s = %s:%s$" % (name, type, value)
+ wanted = staticmethod(wanted)
+
+ def wantedUnknown(name):
+ return "%s is Unknown" % (name)
+ wantedUnknown = staticmethod(wantedUnknown)
diff --git a/libcontextsubscriber/customer-tests/env.sh b/libcontextsubscriber/customer-tests/env.sh
new file mode 100644
index 00000000..31a65d57
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/env.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+export PYTHONPATH="`pwd`/common/"
+export CONTEXT_PROVIDERS=.
+export LD_LIBRARY_PATH=../../src/.libs:../../../libcontextprovider/src/.libs
+export PATH=$PATH:../../../python:../../cli:../../reg-cli
diff --git a/libcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py b/libcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py
new file mode 100755
index 00000000..70a04149
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/pluginchanging/pluginchanging.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+##
+## This file is part of ContextKit.
+##
+## Copyright (C) 2009 Nokia. All rights reserved.
+##
+## Contact: Marius Vollmer <marius.vollmer@nokia.com>
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public License
+## version 2.1 as published by the Free Software Foundation.
+##
+## This library is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+## 02110-1301 USA
+##
+##
+## Requires python2.5-gobject and python2.5-dbus
+##
+import sys
+import unittest
+import os
+import string
+from subprocess import Popen, PIPE
+import time
+import signal
+
+def proc_kill(pid):
+ os.system('../common/rec-kill.sh %d' % pid)
+
+def timeoutHandler(signum, frame):
+ raise Exception('tests have been running for too long')
+
+def stdoutRead (object,lines):
+ list = []
+ for i in range(lines):
+ list.append(object.stdout.readline().rstrip())
+ return list
+
+class Subscription(unittest.TestCase):
+
+ def setUp(self):
+ os.environ["CONTEXT_PROVIDERS"] = "."
+ # We need 2 plugins which are in separate directories.
+ os.environ["CONTEXT_SUBSCRIBER_PLUGINS"] = "."
+ os.system('cp ../testplugins/timeplugin1/.libs/libcontextsubscribertime1.so* .')
+ os.system('cp ../testplugins/timeplugin2/.libs/libcontextsubscribertime2.so* .')
+
+
+ self.context_client = Popen(["context-listen","Test.Time"],stdin=PIPE,stdout=PIPE,stderr=PIPE)
+
+ def tearDown(self):
+ proc_kill(self.context_client.pid)
+ os.remove('time.context')
+ os.system('rm libcontextsubscribertime*.so*')
+
+ def testChangingPlugin(self):
+
+ # Copy the declaration file, declaring libcontextsubscribertime1 plugin.
+ os.system('cp time1.context.temp time.context.temp')
+ os.system('mv time.context.temp time.context')
+ #print "now reading"
+ actual = self.context_client.stdout.readline().rstrip()
+ #print actual
+
+ # The client got a value provided by the libcontextsubscribertime1
+ self.assertEqual(actual.startswith("Test.Time = QString:Time1: "), True, "Got: %s" % actual)
+
+ # Modify the registry so that the key is now provided by libcontextsubscribertime2
+ os.system('cp time2.context.temp time.context.temp')
+ os.system('mv time.context.temp time.context')
+
+ # Assert that the client starts getting the value from the correct plugin
+ # (not necessarily the first one)
+ actual = self.context_client.stdout.readline().rstrip()
+ actual = self.context_client.stdout.readline().rstrip()
+
+ self.assertEqual(actual.startswith("Test.Time = QString:Time2: "), True, "Got: %s" % actual)
+
+if __name__ == "__main__":
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
+ signal.signal(signal.SIGALRM, timeoutHandler)
+ signal.alarm(30)
+ unittest.main()
diff --git a/libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp b/libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp
new file mode 100644
index 00000000..d29592fe
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/pluginchanging/time1.context.temp
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<provider xmlns="http://contextkit.freedesktop.org/Provider" plugin="/libcontextsubscribertime1" constructionString="time">
+ <key name="Test.Time"><type>STRING</type></key>
+</provider>
diff --git a/libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp b/libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp
new file mode 100644
index 00000000..c8a2757c
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/pluginchanging/time2.context.temp
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<provider xmlns="http://contextkit.freedesktop.org/Provider" plugin="/libcontextsubscribertime2" constructionString="time">
+ <key name="Test.Time"><type>STRING</type></key>
+</provider>
diff --git a/libcontextsubscriber/customer-tests/runTests.sh b/libcontextsubscriber/customer-tests/runTests.sh
index 82efc04f..c723a495 100755
--- a/libcontextsubscriber/customer-tests/runTests.sh
+++ b/libcontextsubscriber/customer-tests/runTests.sh
@@ -1,12 +1,11 @@
#!/bin/bash
-DIRS="commander subscription asynchronicity registry"
+cd $(dirname $0)
+DIRS="commander subscription asynchronicity registry pluginchanging"
+. ./env.sh
if pkg-config contextprovider-1.0 || [ -e ../../libcontextprovider/src/.libs/libcontextprovider.so ]
then
- export CONTEXT_PROVIDERS=.
- export LD_LIBRARY_PATH=../../src/.libs:../../../libcontextprovider/src/.libs
- export PATH=$PATH:../../../python:../../cli:../../reg-cli
for dir in $DIRS; do
cd $dir
diff --git a/libcontextsubscriber/customer-tests/testplugins/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/Makefile.am
new file mode 100644
index 00000000..38b401c6
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = timeplugin1 timeplugin2
+EXTRA_DIST = timeplugin.cpp timeplugin.h
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp b/libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp
new file mode 100644
index 00000000..6e3c4df0
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "timeplugin.h"
+#include "sconnect.h"
+
+#include "logging.h"
+
+#include <QDateTime>
+
+/// The factory method for constructing the IPropertyProvider instance.
+IProviderPlugin* pluginFactory(QString /*constructionString*/)
+{
+ // Note: it's the caller's responsibility to delete the plugin if
+ // needed.
+ return new ContextSubscriberTime::TimePlugin();
+}
+
+namespace ContextSubscriberTime {
+
+TimePlugin::TimePlugin()
+{
+ contextDebug();
+ prefix = TIME_PLUGIN_PREFIX;
+ timer.setInterval(2000);
+ sconnect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
+ QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);
+}
+
+void TimePlugin::subscribe(QSet<QString> keys)
+{
+ contextDebug() << keys;
+ foreach(const QString& key, keys) {
+ emit subscribeFinished(key);
+ }
+ timer.start();
+}
+
+void TimePlugin::unsubscribe(QSet<QString> keys)
+{
+ timer.stop();
+}
+
+void TimePlugin::onTimeout()
+{
+ contextDebug() << "Timeout";
+ emit valueChanged("Test.Time", QDateTime::currentDateTime().toString().prepend(prefix));
+}
+
+} // end namespace
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin.h b/libcontextsubscriber/customer-tests/testplugins/timeplugin.h
new file mode 100644
index 00000000..3a51fb5a
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Contact: Marius Vollmer <marius.vollmer@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+This is a test plugin for customer tests.
+*/
+
+#ifndef TIMEPLUGIN_H
+#define TIMEPLUGIN_H
+
+#include "iproviderplugin.h" // For IProviderPlugin definition
+#include <QTimer>
+
+using ContextSubscriber::IProviderPlugin;
+
+extern "C" {
+ IProviderPlugin* pluginFactory(QString constructionString);
+}
+
+namespace ContextSubscriberTime
+{
+
+class TimePlugin : public IProviderPlugin
+{
+ Q_OBJECT
+
+public:
+ explicit TimePlugin();
+ virtual void subscribe(QSet<QString> keys);
+ virtual void unsubscribe(QSet<QString> keys);
+
+private slots:
+ void onTimeout();
+
+private:
+ QTimer timer;
+ QString prefix;
+};
+}
+
+#endif
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore
new file mode 100644
index 00000000..d79521dc
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/.gitignore
@@ -0,0 +1,2 @@
+timeplugin.cpp
+timeplugin.h
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
new file mode 100644
index 00000000..723dfd06
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin1/Makefile.am
@@ -0,0 +1,30 @@
+lib_LTLIBRARIES = libcontextsubscribertime1.la
+libcontextsubscribertime1_la_SOURCES = timeplugin.cpp timeplugin.h
+
+BUILT_SOURCES = $(libcontextsubscribertime1_la_SOURCES)
+timeplugin.cpp: ../timeplugin.cpp
+ ln -sf $< $@
+timeplugin.h: ../timeplugin.h
+ ln -sf $< $@
+
+clean-local:
+ rm -f timeplugin.cpp timeplugin.h
+
+AM_CXXFLAGS = -I$(top_srcdir)/common \
+ -I$(srcdir)/../../../src $(QtCore_CFLAGS) \
+ $(QtDBus_CFLAGS) \
+ '-DCONTEXT_LOG_MODULE_NAME="time1plugin"' \
+ '-DTIME_PLUGIN_PREFIX="Time1: "'
+
+$(top_builddir)/common/libcommon.a:
+ $(MAKE) -C $(top_builddir)/common libcommon.a
+
+LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtDBus_LIBS)
+libcontextsubscribertime1_la_LIBADD=$(top_builddir)/common/libcommon.la
+
+.PHONY: $(top_builddir)/common/libcommon.la
+
+# moccing
+nodist_libcontextsubscribertime1_la_SOURCES = mocs.cpp
+QT_TOMOC = $(filter %.h, $(libcontextsubscribertime1_la_SOURCES))
+include $(top_srcdir)/am/qt.am
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore
new file mode 100644
index 00000000..d79521dc
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/.gitignore
@@ -0,0 +1,2 @@
+timeplugin.cpp
+timeplugin.h
diff --git a/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
new file mode 100644
index 00000000..6e1f27bc
--- /dev/null
+++ b/libcontextsubscriber/customer-tests/testplugins/timeplugin2/Makefile.am
@@ -0,0 +1,30 @@
+lib_LTLIBRARIES = libcontextsubscribertime2.la
+libcontextsubscribertime2_la_SOURCES = timeplugin.cpp timeplugin.h
+
+BUILT_SOURCES = $(libcontextsubscribertime2_la_SOURCES)
+timeplugin.cpp: ../timeplugin.cpp
+ ln -sf $< $@
+timeplugin.h: ../timeplugin.h
+ ln -sf $< $@
+
+clean-local:
+ rm -f timeplugin.cpp timeplugin.h
+
+AM_CXXFLAGS = -I$(top_srcdir)/common \
+ -I$(srcdir)/../../../src $(QtCore_CFLAGS) \
+ $(QtDBus_CFLAGS) \
+ '-DCONTEXT_LOG_MODULE_NAME="time2plugin"' \
+ '-DTIME_PLUGIN_PREFIX="Time2: "'
+
+$(top_builddir)/common/libcommon.a:
+ $(MAKE) -C $(top_builddir)/common libcommon.a
+
+LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtDBus_LIBS)
+libcontextsubscribertime2_la_LIBADD=$(top_builddir)/common/libcommon.la
+
+.PHONY: $(top_builddir)/common/libcommon.la
+
+# moccing
+nodist_libcontextsubscribertime2_la_SOURCES = mocs.cpp
+QT_TOMOC = $(filter %.h, $(libcontextsubscribertime2_la_SOURCES))
+include $(top_srcdir)/am/qt.am
diff --git a/libcontextsubscriber/src/Makefile.am b/libcontextsubscriber/src/Makefile.am
index 051c7b92..b790c4dd 100644
--- a/libcontextsubscriber/src/Makefile.am
+++ b/libcontextsubscriber/src/Makefile.am
@@ -20,6 +20,7 @@ includecontextsubscriber_HEADERS = contextproperty.h \
AM_CXXFLAGS = -I$(top_srcdir)/common \
$(QtCore_CFLAGS) $(QtXml_CFLAGS) $(QtDBus_CFLAGS) \
+ '-DDEFAULT_CONTEXT_SUBSCRIBER_PLUGINS="@libdir@/contextkit/subscriber-plugins"' \
'-DDEFAULT_CONTEXT_PROVIDERS="@datadir@/contextkit/providers/"' \
'-DDEFAULT_CONTEXT_CORE_DECLARATIONS="@datadir@/contextkit/core.context"' \
'-DCONTEXT_LOG_MODULE_NAME="libcontextsubscriber"'
@@ -27,7 +28,7 @@ AM_CXXFLAGS = -I$(top_srcdir)/common \
$(top_builddir)/common/libcommon.la:
$(MAKE) -C $(top_builddir)/common libcommon.la
-LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtXml_LIBS) $(QtDBus_LIBS)
+LIBS += $(CDB_LIBS) $(QtCore_LIBS) $(QtXml_LIBS) $(QtDBus_LIBS)
libcontextsubscriber_la_LIBADD=$(top_builddir)/common/libcommon.la
.PHONY: $(top_builddir)/common/libcommon.la
@@ -40,3 +41,6 @@ include $(top_srcdir)/am/qt.am
# because if you change configure parameter DEFAULT_CONTEXT_PROVIDERS,
# you should do a recompile
infocdbbackend.lo infoxmlbackend.lo: Makefile
+
+# and the same for DEFAULT_CONTEXT_SUBSCRIBER_PLUGINS
+provider.lo: Makefile
diff --git a/libcontextsubscriber/src/contextpropertyinfo.cpp b/libcontextsubscriber/src/contextpropertyinfo.cpp
index 1e23f2db..30cfd8b7 100644
--- a/libcontextsubscriber/src/contextpropertyinfo.cpp
+++ b/libcontextsubscriber/src/contextpropertyinfo.cpp
@@ -303,29 +303,48 @@ void ContextPropertyInfo::onKeyDataChanged(const QString& key)
if (key != keyName)
return;
+ // Update caches and store old values
+ QString oldType = cachedType;
QString newType = InfoBackend::instance()->typeForKey(keyName);
- if (cachedType != newType) {
+ cachedType = newType;
- if (cachedType == "")
+ cachedDoc = InfoBackend::instance()->docForKey(keyName);
+
+ QString oldPlugin = cachedPlugin;
+ QString oldConstructionString = cachedConstructionString;
+ QString newPlugin = InfoBackend::instance()->pluginForKey(keyName);
+ QString newConstructionString = InfoBackend::instance()->constructionStringForKey(keyName);
+ cachedPlugin = newPlugin;
+ cachedConstructionString = newConstructionString;
+
+ // Release the lock before emitting the signals; otherwise
+ // listeners trying to access cached values would create a
+ // deadlock.
+ lock.unlock();
+
+ // Emit the needed signals
+ if (oldType != newType) {
+
+ if (oldType == "")
emit existsChanged(true);
if (newType == "")
emit existsChanged(false);
- cachedType = newType;
emit typeChanged(cachedType);
}
- cachedDoc = InfoBackend::instance()->docForKey(keyName);
-
- // TBD: obsolete the providerChanged signal and add pluginChanged or sth?
- QString newPlugin = InfoBackend::instance()->pluginForKey(keyName);
- if (cachedPlugin == "contextkit-dbus" || newPlugin == "contextkit-dbus") {
- cachedPlugin = newPlugin;
- cachedConstructionString = InfoBackend::instance()->constructionStringForKey(keyName);
- QString newProvider = "";
- if (newPlugin == "contextkit-dbus") {
- newProvider = cachedConstructionString.split(":").last();
+ // TBD: obsolete the providerChanged & providerDBusTypeChanged signals?
+ if (oldPlugin != newPlugin || oldConstructionString != newConstructionString) {
+ if (oldPlugin == "contextkit-dbus" || newPlugin == "contextkit-dbus") {
+ QString newProvider = "";
+ if (newPlugin == "contextkit-dbus") {
+ newProvider = cachedConstructionString.split(":").last();
+ }
+ emit providerChanged(newProvider);
+ // Note: we don't emit providerDBusTypeChanged any
+ // more. It would be cumbersome, and there's no real use
+ // case for listening to it.
}
- emit providerChanged(newProvider);
+ emit pluginChanged(newPlugin, newConstructionString);
}
}
diff --git a/libcontextsubscriber/src/contextpropertyinfo.h b/libcontextsubscriber/src/contextpropertyinfo.h
index 3ba6b255..e83aebb9 100644
--- a/libcontextsubscriber/src/contextpropertyinfo.h
+++ b/libcontextsubscriber/src/contextpropertyinfo.h
@@ -85,6 +85,14 @@ signals:
/// signal you can wait (watch) for various keys to become available.
/// \param exists The new state of the key.
void existsChanged(bool exists);
+
+ /// Emitted when the libcontextsubscriber plugin providing the key
+ /// changes, or the construction parameter to give to the plugin
+ /// changes.. The \a plugin is the name of the new plugin
+ /// providing the key and the \a constructionString is the new
+ /// construction parameter to give to the plugin.
+ void pluginChanged(QString plugin, QString constructionString);
+
};
#endif // CONTEXTPROPERTYINFO_H
diff --git a/libcontextsubscriber/src/iproviderplugin.h b/libcontextsubscriber/src/iproviderplugin.h
index 2cfef845..dcd4342c 100644
--- a/libcontextsubscriber/src/iproviderplugin.h
+++ b/libcontextsubscriber/src/iproviderplugin.h
@@ -27,6 +27,11 @@
namespace ContextSubscriber {
+/* This is not a public API of ContextKit, please do not write third
+ * party plugins for the ContextKit client library without first
+ * contacting us.
+ */
+
class IProviderPlugin : public QObject
{
Q_OBJECT
@@ -42,6 +47,8 @@ signals:
void valueChanged(QString key, QVariant value);
};
+typedef IProviderPlugin* (*PluginFactoryFunc)(QString constructionString);
+
}
#endif
diff --git a/libcontextsubscriber/src/loggingfeatures.h b/libcontextsubscriber/src/loggingfeatures.h
index 86f71c25..05a0d276 100644
--- a/libcontextsubscriber/src/loggingfeatures.h
+++ b/libcontextsubscriber/src/loggingfeatures.h
@@ -26,5 +26,6 @@
#define F_XML (ContextFeature("xml"))
#define F_CDB (ContextFeature("cdb"))
#define F_DESTROY (ContextFeature("destroy"))
+#define F_PLUGINS (ContextFeature("plugins"))
#endif
diff --git a/libcontextsubscriber/src/propertyhandle.cpp b/libcontextsubscriber/src/propertyhandle.cpp
index f1592659..cef5c406 100644
--- a/libcontextsubscriber/src/propertyhandle.cpp
+++ b/libcontextsubscriber/src/propertyhandle.cpp
@@ -83,6 +83,12 @@ PropertyHandle::PropertyHandle(const QString& key)
// done before calling updateProvider.
myInfo = new ContextPropertyInfo(myKey, this);
+ // Start listening to changes in property introspection (e.g., added to registry, plugin changes)
+ sconnect(myInfo, SIGNAL(existsChanged(bool)),
+ this, SLOT(updateProvider()));
+ sconnect(myInfo, SIGNAL(pluginChanged(QString, QString)),
+ this, SLOT(updateProvider()));
+
// Start listening for the context commander, and also initiate a
// NameHasOwner check.
@@ -103,9 +109,6 @@ PropertyHandle::PropertyHandle(const QString& key)
// Otherwise, delay connecting to the provider until we know
// whether commander is present.
- // Start listening to changes in property registry (e.g., new keys, keys removed)
- sconnect(ContextRegistryInfo::instance(), SIGNAL(keysChanged(const QStringList&)),
- this, SLOT(updateProvider()));
// Move the PropertyHandle (and all children) to main thread.
moveToThread(QCoreApplication::instance()->thread());
@@ -127,6 +130,7 @@ void PropertyHandle::setTypeCheck(bool typeCheck)
void PropertyHandle::updateProvider()
{
Provider *newProvider;
+ contextDebug() << F_PLUGINS;
if (commandingEnabled && commanderListener->isServicePresent() == DBusNameListener::Present) {
// If commander is present it should be able to override the
@@ -139,6 +143,7 @@ void PropertyHandle::updateProvider()
if (myInfo->exists()) {
// If myInfo knows the current provider which should be
// connected to, connect to it.
+ contextDebug() << F_PLUGINS << "Key exists";
newProvider = Provider::instance(myInfo->plugin(),
myInfo->constructionString());
} else {
@@ -146,6 +151,8 @@ void PropertyHandle::updateProvider()
// This way, we can still continue communicating with the
// provider even though the key is no longer in the
// registry.
+ contextDebug() << F_PLUGINS << "Key doesn't exist -> keep old provider info";
+
newProvider = myProvider;
if (newProvider == 0) {
diff --git a/libcontextsubscriber/src/provider.cpp b/libcontextsubscriber/src/provider.cpp
index c5e8b931..dcf745fc 100644
--- a/libcontextsubscriber/src/provider.cpp
+++ b/libcontextsubscriber/src/provider.cpp
@@ -25,10 +25,12 @@
#include "sconnect.h"
#include "contextkitplugin.h"
#include "logging.h"
+#include "loggingfeatures.h"
#include <QTimer>
#include <QMutexLocker>
#include <QCoreApplication>
#include <QThread>
+#include <QLibrary>
namespace ContextSubscriber {
@@ -36,6 +38,11 @@ namespace ContextSubscriber {
\class IProviderPlugin
\brief Interface for provider plugins.
+ Note: this interface is private, currently it is not advised to use
+ it and create ContextKit subscriber plugins on your own, we can and
+ will change this interface anytime in the future even between small
+ bugfix releases.
+
Every Provider instance contains exactly one plugin (pointer) with
this interface which is constructed on initialization time and never
change after that. This way the concrete protocol (dbus, shared
@@ -45,7 +52,14 @@ namespace ContextSubscriber {
unsubscribe calls (on the wire) using the \c subscribe and \c
unsubscribe methods.
- The plugin can fail or became ready anytime because of things
+ When the plugin is constructed, it should emit the signal ready()
+ when it is ready to take in subscriptions. However, the signal
+ ready() should not be emitted in the plugin constructor. If the
+ plugin is able to take in subscriptions immediately, you can use
+ QMetaObject::invokeMethod with QueuedConnection to emit the signal
+ when the main loop is entered the next time.
+
+ The plugin can fail or became ready again anytime because of things
happening on the wire inside the plugin (socket closed, dbus service
appears/disappears). Whenever the plugin has new information about
this it should emit the signal \c ready or \c failed accordingly.
@@ -109,9 +123,43 @@ Provider::Provider(const QString &plugin, const QString &constructionString)
/// up with the name of the function).
void Provider::constructPlugin()
{
+ contextDebug() << F_PLUGINS;
if (pluginName == "contextkit-dbus") {
plugin = contextKitPluginFactory(constructionString);
- } else ; // FIXME: implement plugin system in the else branch
+ }
+ else if (pluginName.startsWith("/")) { // Require the plugin name to start with /
+ // Enable overriding the plugin location with an environment variable
+ const char *pluginPath = getenv("CONTEXT_SUBSCRIBER_PLUGINS");
+ if (! pluginPath)
+ pluginPath = DEFAULT_CONTEXT_SUBSCRIBER_PLUGINS;
+
+ QString pluginFilename(pluginPath);
+ // Allow pluginPath to have a trailing / or not
+ if (pluginFilename.endsWith("/")) {
+ pluginFilename.chop(1);
+ }
+
+ pluginFilename.append(pluginName);
+
+ QLibrary library(pluginFilename);
+ library.load();
+
+ if (library.isLoaded()) {
+ PluginFactoryFunc factory = (PluginFactoryFunc) library.resolve("pluginFactory");
+ if (factory) {
+ contextDebug() << "Resolved factory function";
+ plugin = factory(constructionString);
+ } else {
+ contextCritical() << "Error resolving function pluginFactory from plugin" << pluginFilename;
+ }
+ }
+ else {
+ contextCritical() << "Error loading plugin" << pluginFilename << ":" << library.errorString();
+ }
+ }
+ else {
+ contextCritical() << "Illegal plugin name" << pluginName << ", doesn't start with /";
+ }
if (plugin == 0) {
pluginState = FAILED;
diff --git a/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h b/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h
index a53f6d46..61cdcb45 100644
--- a/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h
+++ b/libcontextsubscriber/unit-tests/propertyhandle/contextpropertyinfo.h
@@ -50,6 +50,7 @@ signals:
void providerDBusTypeChanged(QDBusConnection::BusType newBusType);
void typeChanged(QString newType);
void existsChanged(bool exists);
+ void pluginChanged(QString, QString);
public:
// For the test program
diff --git a/libcontextsubscriber/unit-tests/provider/Makefile.am b/libcontextsubscriber/unit-tests/provider/Makefile.am
index 8d28ea53..936b9a92 100644
--- a/libcontextsubscriber/unit-tests/provider/Makefile.am
+++ b/libcontextsubscriber/unit-tests/provider/Makefile.am
@@ -9,7 +9,9 @@ COVERAGE_FILES = provider.cpp
# do the testing, coverage, etc. stuff
# tests.am is using +=, so we have to set a value here for these four always
-AM_CXXFLAGS = $(QtDBus_CFLAGS)
+AM_CXXFLAGS = $(QtDBus_CFLAGS) \
+ '-DDEFAULT_CONTEXT_SUBSCRIBER_PLUGINS="@libdir@/contextkit/subscriber-plugins"'
+
AM_LDFLAGS = $(QtDBus_LIBS)
# copy these files from the real source
FROM_SOURCE = provider.cpp provider.h iproviderplugin.h \