Difference between revisions of "TDE DBus Tutorial"

From Trinity Desktop Project Wiki
Jump to navigation Jump to search
imported>Deloptes
(TDE use of dbus-1-tqt and dbusxml2qt3)
 
imported>Deloptes
Line 5: Line 5:
 
I hope it will save many time for playing, copy/pasting "strange" code or just head ache.
 
I hope it will save many time for playing, copy/pasting "strange" code or just head ache.
   
There are few examples at the end to show the use of DBus proxy and DBus interface.
+
Here are two examples to show the use of DBus proxy and DBus interface.
   
Additional information can be found in the documentation of package dbus-1-tqt.
+
Additional information can be found in the documentation of package dbus-1-tqt, but I spent almost 2 days until magic combination of following steps came out.
   
 
=Create DBus Interface=
 
=Create DBus Interface=
Line 13: Line 13:
 
To create a DBus interface we need first a definition of the interface. More information on the interface type and signatures can be found in [https://dbus.freedesktop.org/doc/dbus-tutorial.html|the official dbus tutorial]
 
To create a DBus interface we need first a definition of the interface. More information on the interface type and signatures can be found in [https://dbus.freedesktop.org/doc/dbus-tutorial.html|the official dbus tutorial]
   
The definition of the interface is a xml file. We take as example a file describing "org.freedesktop.DBus.ObjectManager".
+
The definition of the interface is a xml file. We take as example a file describing "org.example.Service" that has one method "ListSorter".
  +
The generated code will automatically include the Introspection interface and we will see how we can create a service to handle both interfaces.
   
 
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 
<node name="/org/example/Service">
 
<node name="/org/example/Service">
<interface name="org.freedesktop.DBus.ObjectManager">
+
<interface name="org.example.Service">
<method name="GetManagedObjects">
+
<method name="ListSorter">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out" />
+
<arg name="input" type="as" direction="in" />
  +
<arg name="output" type="as" direction="out" />
</method>
 
<signal name="InterfacesAdded">
+
</method>
<arg name="object" type="o" />
+
</interface>
<arg name="interfaces" type="a{sa{sv}}" />
 
</signal>
 
<signal name="InterfacesRemoved">
 
<arg name="object" type="o" />
 
<arg name="interfaces" type="as" />
 
</signal>
 
</interface>
 
<node name="org" />
 
 
</node>
 
</node>
  +
  +
=Generate the code=
   
 
To generate the TQt C++ classes use dbusxml2qt3 (Note: [https://bugs.trinitydesktop.org/show_bug.cgi?id=2925|bug-2925])
 
To generate the TQt C++ classes use dbusxml2qt3 (Note: [https://bugs.trinitydesktop.org/show_bug.cgi?id=2925|bug-2925])
   
$ /usr/bin/dbusxml2qt3 objectmanager.xml
+
$ dbusxml2qt3 sortexample.xml
ClassGenerator: processing interface 'org.freedesktop.DBus.ObjectManager'
+
ClassGenerator: processing interface 'org.example.Service'
 
Generating org.freedesktop.DBus.Introspectable on demand
 
Generating org.freedesktop.DBus.Introspectable on demand
   
 
in the second step generate the proxy with unique namespace to fit your application
 
in the second step generate the proxy with unique namespace to fit your application
   
$/usr/bin/dbusxml2qt3 objectmanager.xml -p -N MyNameSpace::Proxy
+
$/usr/bin/dbusxml2qt3 sortexample.xml -p -N MyNameSpace::Proxy
   
  +
the result is
$ ls -1 | grep -v xml
 
  +
  +
$ ls -1
 
introspectableinterface.cpp
 
introspectableinterface.cpp
 
introspectableinterface.h
 
introspectableinterface.h
objectmanagerinterface.cpp
 
objectmanagerinterface.h
 
objectmanagerproxy.cpp
 
objectmanagerproxy.h
 
 
org_example_servicenode.cpp
 
org_example_servicenode.cpp
 
org_example_servicenode.h
 
org_example_servicenode.h
  +
serviceinterface.cpp
  +
serviceinterface.h
  +
serviceproxy.cpp
  +
serviceproxy.h
  +
sortexample.xml
  +
  +
We have to correct few errors introduced by the generator
  +
  +
* <*>node.cpp file missing "interface" in the include
  +
  +
// interface classes includes
  +
#include "service''interface''.h"
  +
  +
* <*>proxy.cpp you need to add whatever your proxy is called .moc at the end
  +
  +
''#include "serviceproxy.moc"''
  +
// End of File
  +
  +
=Implement the Service=
  +
  +
Now we can create our own service using the interface from step one, or create our own proxy from step two
  +
  +
==testservice.h==
  +
  +
#include <tqdbusconnection.h>
  +
#include <tqdbusobject.h>
  +
#include <tqmap.h>
  +
#include "serviceinterface.h"
  +
#include "org_example_servicenode.h"
  +
  +
class Interface1 : public org::example::Service
  +
{
  +
public:
  +
Interface1(TQT_DBusConnection&);
  +
virtual ~Interface1();
  +
protected: // implement methods
  +
virtual bool ListSorter(const TQStringList& input, TQStringList& output, TQT_DBusError& error);
  +
protected: // implement sending replies
  +
virtual void handleMethodReply(const TQT_DBusMessage& reply);
  +
private:
  +
TQT_DBusConnection *m_connection;
  +
};
  +
  +
class MultiInterfaceService : public org_example_Service
  +
{
  +
public:
  +
MultiInterfaceService(TQT_DBusConnection&);
  +
~MultiInterfaceService();
  +
protected:
  +
virtual TQT_DBusObjectBase* createInterface(const TQString&);
  +
private:
  +
TQMap<TQString, TQT_DBusObjectBase*> m_interfaces;
  +
TQT_DBusConnection m_connection;
  +
};
  +
  +
==testservice.cpp==
  +
  +
#include "testservice.h"
  +
#include <kdebug.h>
  +
  +
// TQt includes
  +
#include <tqdom.h>
  +
#include <tqstring.h>
  +
#include <tqstringlist.h>
  +
#include <tqdbuserror.h>
  +
#include <tqdbusmessage.h>
  +
#include <tqdbusdatalist.h>
  +
  +
Interface1::Interface1(TQT_DBusConnection &conn)
  +
: m_connection(&conn)
  +
{
  +
kdDebug() << k_funcinfo << endl;
  +
}
  +
Interface1::~Interface1(){
  +
kdDebug() << k_funcinfo << endl;
  +
}
  +
  +
void Interface1::handleMethodReply(const TQT_DBusMessage& reply) {
  +
kdDebug() << k_funcinfo << endl;
  +
// do something
  +
m_connection->send(reply);
  +
}
  +
  +
bool Interface1::ListSorter(const TQStringList& input, TQStringList& output, TQT_DBusError& error) {
  +
kdDebug() << k_funcinfo << endl;
  +
output = input;
  +
output.sort();
  +
return true;
  +
}
  +
  +
MultiInterfaceService::MultiInterfaceService(TQT_DBusConnection &connection )
  +
: org_example_Service(), m_connection(connection)
  +
{
  +
kdDebug() << k_funcinfo << endl;
  +
m_interfaces.insert("org.freedesktop.DBus.Introspectable", this);
  +
m_interfaces.insert("org.example.Service", new Interface1(m_connection));
  +
registerObject(m_connection,"/");
  +
}
  +
  +
MultiInterfaceService::~MultiInterfaceService(){
  +
kdDebug() << k_funcinfo << endl;
  +
}
  +
  +
TQT_DBusObjectBase* MultiInterfaceService::createInterface(const TQString& interfaceName)
  +
{
  +
kdDebug() << k_funcinfo << endl;
  +
kdDebug() << "Interface Name: " << interfaceName << endl;
  +
return (TQT_DBusObjectBase*) m_interfaces[interfaceName];
  +
}
  +
  +
==tqdbusexample.cpp==
  +
  +
#include "testservice.h"
  +
#include <tqapplication.h>
  +
#include <tqdbusconnection.h>
  +
  +
int main(int argc, char** argv)
  +
{
  +
TQApplication app(argc, argv, false);
  +
TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
  +
// TQT_DBusConnection connection = TQT_DBusConnection::systemBus();
  +
if (!connection.isConnected())
  +
tqFatal("Cannot connect to session bus");
  +
// try to get a specific service name
  +
if (!connection.requestName("org.example.Service"))
  +
{
  +
tqWarning("Requesting name 'org.example.Service' failed. "
  +
"Will only be addressable through unique name '%s'",
  +
connection.uniqueName().local8Bit().data());
  +
}
  +
else
  +
{
  +
tqDebug("Requesting name 'org.example.Service' successfull");
  +
}
  +
MultiInterfaceService service(connection);
  +
  +
return app.exec();
  +
}
  +
  +
==buildtqdbusexample.sh==
  +
  +
/usr/bin/g++ -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time \
  +
-D_FORTIFY_SOURCE=2 -DQT_NO_ASCII_CAST -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT \
  +
-DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT -D_REENTRANT -include tqt.h -I/usr/include/tqt3 \
  +
-I/opt/trinity/include/ -I/usr/include/dbus-1-tqt -I/usr/include/tqt -I -DQT_NO_ASCII_CAST \
  +
-DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT \
  +
-D_REENTRANT -include tqt.h -g -Wl,-z,relro tqdbusexample.cpp testservice.cpp \
  +
org_example_servicenode.cpp introspectableinterface.cpp serviceinterface.cpp \
  +
-o tqdbusexample -ldbus-1-tqt \
  +
/opt/trinity/lib/libtdeparts.so.2.1.0 /opt/trinity/lib/libtdeio.so.14.0.0 \
  +
/opt/trinity/lib/libtdecore.so.14.0.0 -ltqt -ltqt-mt -lXrender -lX11 -lc \
  +
/usr/lib/x86_64-linux-gnu/libz.so -lidn -lXcomposite -lICE -lSM -lutil -lr -lacl -lattr -ltqui
  +
  +
  +
  +
=Using a Proxy=
  +
  +
We will use the service from our first example and implement a proxy to utilize the service.
  +
  +
==tqdbusproxy.cpp==
  +
  +
#include <tqstringlist.h>
  +
#include <tqdbusconnection.h>
  +
#include "serviceproxy.h"
  +
  +
int main(int argc, char** argv)
  +
{
  +
  +
TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
  +
// TQT_DBusConnection connection = TQT_DBusConnection::systemBus();
  +
if (!connection.isConnected())
  +
tqFatal("Cannot connect to session bus");
  +
  +
MyNameSpace::Proxy::Service serviceProxy("org.example.Service", "/");
  +
  +
serviceProxy.setConnection(connection);
  +
  +
TQStringList list;
  +
list << "D" << "C" << "B" << "A";
  +
  +
tqWarning("OUTPUT BEFORE SORT");
  +
for (TQStringList::iterator it= list.begin(); it != list.end(); ++it) {
  +
tqWarning("\t%s",(*it).utf8().data());
  +
}
  +
  +
TQStringList data;
  +
TQT_DBusError error;
  +
if (!serviceProxy.ListSorter(list,data,error)) {
  +
tqFatal("'org.example.Service.ListSorter' failed. ");
  +
}
  +
else {
  +
tqWarning("OUTPUT AFTER SORT");
  +
for (TQStringList::iterator it = data.begin(); it != data.end(); ++it) {
  +
tqWarning("\t%s",(*it).utf8().data());
  +
}
  +
}
  +
  +
return 0;
  +
}
  +
  +
==buildtqdbusproxy.sh==
  +
  +
/usr/bin/g++ -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time \
  +
-D_FORTIFY_SOURCE=2 -DQT_NO_ASCII_CAST -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT \
  +
-DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT -D_REENTRANT -include tqt.h -I/usr/include/tqt3 \
  +
-I/opt/trinity/include/ -I/usr/include/dbus-1-tqt -I/usr/include/tqt -I -DQT_NO_ASCII_CAST \
  +
-DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT \
  +
-D_REENTRANT -include tqt.h -g -Wl,-z,relro tqdbusproxy.cpp serviceproxy.cpp \
  +
-o tqdbusproxy -ldbus-1-tqt \
  +
/opt/trinity/lib/libtdeparts.so.2.1.0 /opt/trinity/lib/libtdeio.so.14.0.0 \
  +
/opt/trinity/lib/libtdecore.so.14.0.0 -ltqt -ltqt-mt -lXrender -lX11 -lc \
  +
/usr/lib/x86_64-linux-gnu/libz.so -lidn -lXcomposite -lICE -lSM -lutil -lr -lacl -lattr -ltqui
  +
  +
=Try it=
  +
  +
== Start the service==
  +
  +
./tqdbusexample
  +
  +
==Test the service from command line==
  +
  +
$ dbus-send --print-reply --session --dest=org.example.Service / org.example.Service.ListSorter array:string:"B","A","C"
  +
method return time=1533488709.968425 sender=:1.352 -> destination=:1.355 serial=8 reply_serial=2
  +
array [
  +
string "A"
  +
string "B"
  +
string "C"
  +
]
  +
   
  +
==Test the service with proxy client==
   
  +
./tqdbusproxy
Now we can create our own object manager using the interface from step one, or create our own proxy from step two
 
   
  +
OUTPUT BEFORE SORT
[to be completed...]
 
  +
D
  +
C
  +
B
  +
A
  +
OUTPUT AFTER SORT
  +
A
  +
B
  +
C
  +
D

Revision as of 17:57, 5 August 2018

Introduction

I want to put down a summary of my experience with auto-generated interfaces and their use in TDE.

I hope it will save many time for playing, copy/pasting "strange" code or just head ache.

Here are two examples to show the use of DBus proxy and DBus interface.

Additional information can be found in the documentation of package dbus-1-tqt, but I spent almost 2 days until magic combination of following steps came out.

Create DBus Interface

To create a DBus interface we need first a definition of the interface. More information on the interface type and signatures can be found in official dbus tutorial

The definition of the interface is a xml file. We take as example a file describing "org.example.Service" that has one method "ListSorter". The generated code will automatically include the Introspection interface and we will see how we can create a service to handle both interfaces.

     <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
     "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
     <node name="/org/example/Service">
       <interface name="org.example.Service">
               <method name="ListSorter">
                       <arg name="input" type="as" direction="in" />
                       <arg name="output" type="as" direction="out" />
               </method>
       </interface>
     </node>

Generate the code

To generate the TQt C++ classes use dbusxml2qt3 (Note: [1])

     $ dbusxml2qt3 sortexample.xml
     ClassGenerator: processing interface 'org.example.Service'
     Generating org.freedesktop.DBus.Introspectable on demand

in the second step generate the proxy with unique namespace to fit your application

     $/usr/bin/dbusxml2qt3 sortexample.xml -p -N MyNameSpace::Proxy

the result is

     $ ls -1 
     introspectableinterface.cpp
     introspectableinterface.h
     org_example_servicenode.cpp
     org_example_servicenode.h
     serviceinterface.cpp
     serviceinterface.h
     serviceproxy.cpp
     serviceproxy.h
     sortexample.xml

We have to correct few errors introduced by the generator

  • <*>node.cpp file missing "interface" in the include
    // interface classes includes
    #include "serviceinterface.h"
  • <*>proxy.cpp you need to add whatever your proxy is called .moc at the end
    #include "serviceproxy.moc"
    // End of File

Implement the Service

Now we can create our own service using the interface from step one, or create our own proxy from step two

testservice.h

     #include <tqdbusconnection.h>
     #include <tqdbusobject.h>
     #include <tqmap.h>
     #include "serviceinterface.h"
     #include "org_example_servicenode.h"
     
     class Interface1 : public org::example::Service
     {
     public:
         Interface1(TQT_DBusConnection&);
         virtual ~Interface1();
     protected: // implement methods
         virtual bool ListSorter(const TQStringList& input, TQStringList& output, TQT_DBusError& error);
     protected: // implement sending replies
         virtual void handleMethodReply(const TQT_DBusMessage& reply);
     private:
         TQT_DBusConnection *m_connection;
     };
     
     class MultiInterfaceService : public org_example_Service
     {
     public:
         MultiInterfaceService(TQT_DBusConnection&);
       ~MultiInterfaceService();
     protected:
       virtual TQT_DBusObjectBase* createInterface(const TQString&);
     private:
         TQMap<TQString, TQT_DBusObjectBase*> m_interfaces;
         TQT_DBusConnection m_connection;
     };

testservice.cpp

     #include "testservice.h"
     #include <kdebug.h>
     
     // TQt includes
     #include <tqdom.h>
     #include <tqstring.h>
     #include <tqstringlist.h>
     #include <tqdbuserror.h>
     #include <tqdbusmessage.h>
     #include <tqdbusdatalist.h>
     
     Interface1::Interface1(TQT_DBusConnection &conn)
       : m_connection(&conn)
     {
       kdDebug() << k_funcinfo << endl;
     }
     Interface1::~Interface1(){
       kdDebug() << k_funcinfo << endl;
     }
     
     void Interface1::handleMethodReply(const TQT_DBusMessage& reply) {
       kdDebug() << k_funcinfo << endl;
       // do something
       m_connection->send(reply);
     }
     
     bool Interface1::ListSorter(const TQStringList& input, TQStringList& output, TQT_DBusError& error) {
       kdDebug() << k_funcinfo << endl;
       output = input;
       output.sort();
       return true;
     }
     
     MultiInterfaceService::MultiInterfaceService(TQT_DBusConnection &connection )
       : org_example_Service(), m_connection(connection)
     {
       kdDebug() << k_funcinfo << endl;
       m_interfaces.insert("org.freedesktop.DBus.Introspectable", this);
       m_interfaces.insert("org.example.Service", new Interface1(m_connection));
       registerObject(m_connection,"/");
     }
     
     MultiInterfaceService::~MultiInterfaceService(){
       kdDebug() << k_funcinfo << endl;
     }
     
     TQT_DBusObjectBase* MultiInterfaceService::createInterface(const TQString& interfaceName)
     {
       kdDebug() << k_funcinfo << endl;
       kdDebug() << "Interface Name: " << interfaceName << endl;
         return (TQT_DBusObjectBase*) m_interfaces[interfaceName];
     }

tqdbusexample.cpp

     #include "testservice.h"
     #include <tqapplication.h>
     #include <tqdbusconnection.h>
     
     int main(int argc, char** argv)
     {
       TQApplication app(argc, argv, false);
       TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
     //        TQT_DBusConnection connection = TQT_DBusConnection::systemBus();
       if (!connection.isConnected())
                       tqFatal("Cannot connect to session bus");
       // try to get a specific service name
       if (!connection.requestName("org.example.Service"))
       {
               tqWarning("Requesting name 'org.example.Service' failed. "
                               "Will only be addressable through unique name '%s'",
               connection.uniqueName().local8Bit().data());
       }
       else
       {
               tqDebug("Requesting name 'org.example.Service' successfull");
       }
       MultiInterfaceService service(connection);
     
       return app.exec();
     }

buildtqdbusexample.sh

     /usr/bin/g++   -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time \
     -D_FORTIFY_SOURCE=2  -DQT_NO_ASCII_CAST -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT \
     -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT -D_REENTRANT -include tqt.h -I/usr/include/tqt3 \
     -I/opt/trinity/include/ -I/usr/include/dbus-1-tqt -I/usr/include/tqt -I -DQT_NO_ASCII_CAST \
     -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT \
     -D_REENTRANT -include tqt.h -g  -Wl,-z,relro tqdbusexample.cpp  testservice.cpp \
      org_example_servicenode.cpp introspectableinterface.cpp serviceinterface.cpp \
      -o tqdbusexample -ldbus-1-tqt \
     /opt/trinity/lib/libtdeparts.so.2.1.0 /opt/trinity/lib/libtdeio.so.14.0.0 \
     /opt/trinity/lib/libtdecore.so.14.0.0 -ltqt -ltqt-mt -lXrender -lX11 -lc \
     /usr/lib/x86_64-linux-gnu/libz.so -lidn -lXcomposite -lICE -lSM -lutil -lr -lacl -lattr -ltqui


Using a Proxy

We will use the service from our first example and implement a proxy to utilize the service.

tqdbusproxy.cpp

     #include <tqstringlist.h>
     #include <tqdbusconnection.h>
     #include "serviceproxy.h"
     
     int main(int argc, char** argv)
     {
     
       TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
     //        TQT_DBusConnection connection = TQT_DBusConnection::systemBus();
       if (!connection.isConnected())
                       tqFatal("Cannot connect to session bus");
     
       MyNameSpace::Proxy::Service serviceProxy("org.example.Service", "/");
     
       serviceProxy.setConnection(connection);
     
       TQStringList list;
       list << "D" << "C" << "B" << "A";
     
       tqWarning("OUTPUT BEFORE SORT");
       for (TQStringList::iterator it= list.begin(); it != list.end(); ++it) {
               tqWarning("\t%s",(*it).utf8().data());
       }
     
       TQStringList data;
       TQT_DBusError error;
       if (!serviceProxy.ListSorter(list,data,error)) {
               tqFatal("'org.example.Service.ListSorter' failed. ");
       }
       else {
               tqWarning("OUTPUT AFTER SORT");
               for (TQStringList::iterator it = data.begin(); it != data.end(); ++it) {
                       tqWarning("\t%s",(*it).utf8().data());
               }
       }
     
       return 0;
     }

buildtqdbusproxy.sh

     /usr/bin/g++   -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time \
     -D_FORTIFY_SOURCE=2  -DQT_NO_ASCII_CAST -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT \
     -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT -D_REENTRANT -include tqt.h -I/usr/include/tqt3 \
     -I/opt/trinity/include/ -I/usr/include/dbus-1-tqt -I/usr/include/tqt -I -DQT_NO_ASCII_CAST \
     -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT \
     -D_REENTRANT -include tqt.h -g  -Wl,-z,relro tqdbusproxy.cpp serviceproxy.cpp \
      -o tqdbusproxy -ldbus-1-tqt \
     /opt/trinity/lib/libtdeparts.so.2.1.0 /opt/trinity/lib/libtdeio.so.14.0.0 \
     /opt/trinity/lib/libtdecore.so.14.0.0 -ltqt -ltqt-mt -lXrender -lX11 -lc \
     /usr/lib/x86_64-linux-gnu/libz.so -lidn -lXcomposite -lICE -lSM -lutil -lr -lacl -lattr -ltqui

Try it

Start the service

  ./tqdbusexample

Test the service from command line

  $ dbus-send --print-reply  --session --dest=org.example.Service / org.example.Service.ListSorter array:string:"B","A","C"
  method return time=1533488709.968425 sender=:1.352 -> destination=:1.355 serial=8 reply_serial=2
     array [
        string "A"
        string "B"
        string "C"
     ]


Test the service with proxy client

  ./tqdbusproxy
     OUTPUT BEFORE SORT
       D
       C
       B
       A
     OUTPUT AFTER SORT
       A
       B
       C
       D