1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
|
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
<!ENTITY version SYSTEM "version.xml">
]>
<chapter>
<title>Migrating to GDBus</title>
<section>
<title>Conceptual differences</title>
<para>
The central concepts of D-Bus are modelled in a very similar way
in dbus-glib and GDBus. Both have objects representing connections,
proxies and method invocations. But there are some important
differences:
<itemizedlist>
<listitem><para>
dbus-glib uses the <ulink
url="http://www.freedesktop.org/wiki/Software/dbus#ReferenceImplementation.28dbus-daemonandlibdbus.29">libdbus
reference implementation</ulink>, GDBus doesn't. Instead, it
relies on GIO streams as transport layer, and has its own
implementation for the D-Bus connection setup and
authentication. Apart from using streams as transport,
avoiding libdbus also lets GDBus avoid some thorny
multithreading issues.
</para></listitem>
<listitem><para>
dbus-glib uses the GObject type system for method arguments and
return values, including a homegrown container specialization
mechanism. GDBus relies on the #GVariant type system which is
explicitly designed to match D-Bus types.
</para></listitem>
<listitem><para>
dbus-glib models only D-Bus interfaces and does not provide
any types for objects. GDBus models both D-Bus interfaces
(via the #GDBusInterface, #GDBusProxy and
#GDBusInterfaceSkeleton types) and objects (via the
#GDBusObject, #GDBusObjectSkeleton and #GDBusObjectProxy types).
</para></listitem>
<listitem><para>
GDBus includes native support for the <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties">org.freedesktop.DBus.Properties</ulink> (via the #GDBusProxy type) and <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">org.freedesktop.DBus.ObjectManager</ulink> D-Bus interfaces, dbus-glib doesn't.
</para></listitem>
<listitem><para>
The typical way to export an object in dbus-glib involves
generating glue code from XML introspection data using
<command>dbus-binding-tool</command>. GDBus provides a
similar tool called <command><link
linkend="gdbus-codegen">gdbus-codegen</link></command> that
can also generate Docbook D-Bus interface documentation.
</para></listitem>
<listitem><para>
dbus-glib doesn't provide any convenience API for owning and
watching bus names, GDBus provides the g_bus_own_name() and
g_bus_watch_name() family of convenience functions.
</para></listitem>
<listitem><para>
GDBus provides API to parse, generate and work with <link
linkend="gio-D-Bus-Introspection-Data">Introspection
XML</link>, dbus-glib doesn't.
</para></listitem>
<listitem><para>
GTestDBus provides API to create isolated unit tests <link
linkend="gio-D-Bus-Test-Scaffolding">GDBus Test Scaffolding</link>.
</para></listitem>
</itemizedlist>
</para>
</section>
<section>
<title>API comparison</title>
<table id="dbus-glib-vs-gdbus">
<title>dbus-glib APIs and their GDBus counterparts</title>
<tgroup cols="2">
<thead>
<row><entry>dbus-glib</entry><entry>GDBus</entry></row>
</thead>
<tbody>
<row><entry>#DBusGConnection</entry><entry>#GDBusConnection</entry></row>
<row><entry>#DBusGProxy</entry><entry>#GDBusProxy, #GDBusInterface - also see #GDBusObjectProxy</entry></row>
<row><entry>#DBusGObject</entry><entry>#GDBusInterfaceSkeleton, #GDBusInterface - also see #GDBusObjectSkeleton</entry></row>
<row><entry>#DBusGMethodInvocation</entry><entry>#GDBusMethodInvocation</entry></row>
<row><entry>dbus_g_bus_get()</entry><entry>g_bus_get_sync(), also see
g_bus_get()</entry></row>
<row><entry>dbus_g_proxy_new_for_name()</entry><entry>g_dbus_proxy_new_sync() and
g_dbus_proxy_new_for_bus_sync(), also see g_dbus_proxy_new()</entry></row>
<row><entry>dbus_g_proxy_add_signal()</entry><entry>not needed, use the generic #GDBusProxy::g-signal</entry></row>
<row><entry>dbus_g_proxy_connect_signal()</entry><entry>use g_signal_connect() with #GDBusProxy::g-signal</entry></row>
<row><entry>dbus_g_connection_register_g_object()</entry><entry>g_dbus_connection_register_object() - also see g_dbus_object_manager_server_export()</entry></row>
<row><entry>dbus_g_connection_unregister_g_object()</entry><entry>g_dbus_connection_unregister_object() - also see g_dbus_object_manager_server_unexport()</entry></row>
<row><entry>dbus_g_object_type_install_info()</entry><entry>introspection data is installed while registering
an object, see g_dbus_connection_register_object()</entry></row>
<row><entry>dbus_g_proxy_begin_call()</entry><entry>g_dbus_proxy_call()</entry></row>
<row><entry>dbus_g_proxy_end_call()</entry><entry>g_dbus_proxy_call_finish()</entry></row>
<row><entry>dbus_g_proxy_call()</entry><entry>g_dbus_proxy_call_sync()</entry></row>
<row><entry>dbus_g_error_domain_register()</entry><entry>g_dbus_error_register_error_domain()</entry></row>
<row><entry>dbus_g_error_has_name()</entry><entry>no direct equivalent, see g_dbus_error_get_remote_error()</entry></row>
<row><entry>dbus_g_method_return()</entry><entry>g_dbus_method_invocation_return_value()</entry></row>
<row><entry>dbus_g_method_return_error()</entry><entry>g_dbus_method_invocation_return_error() and variants</entry></row>
<row><entry>dbus_g_method_get_sender()</entry><entry>g_dbus_method_invocation_get_sender()</entry></row>
</tbody>
</tgroup>
</table>
</section>
<section>
<title>Owning bus names</title>
<para>
Using dbus-glib, you typically call RequestName manually
to own a name, like in the following excerpt:
<informalexample><programlisting><![CDATA[
error = NULL;
res = dbus_g_proxy_call (system_bus_proxy,
"RequestName",
&error,
G_TYPE_STRING, NAME_TO_CLAIM,
G_TYPE_UINT, DBUS_NAME_FLAG_ALLOW_REPLACEMENT,
G_TYPE_INVALID,
G_TYPE_UINT, &result,
G_TYPE_INVALID);
if (!res)
{
if (error != NULL)
{
g_warning ("Failed to acquire %s: %s",
NAME_TO_CLAIM, error->message);
g_error_free (error);
}
else
{
g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
}
goto out;
}
if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
if (error != NULL)
{
g_warning ("Failed to acquire %s: %s",
NAME_TO_CLAIM, error->message);
g_error_free (error);
}
else
{
g_warning ("Failed to acquire %s", NAME_TO_CLAIM);
}
exit (1);
}
dbus_g_proxy_add_signal (system_bus_proxy, "NameLost",
G_TYPE_STRING, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (system_bus_proxy, "NameLost",
G_CALLBACK (on_name_lost), NULL, NULL);
/* further setup ... */
]]>
</programlisting></informalexample>
</para>
<para>
While you can do things this way with GDBus too, using
g_dbus_proxy_call_sync(), it is much nicer to use the high-level API
for this:
<informalexample><programlisting><![CDATA[
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
/* further setup ... */
}
/* ... */
owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
NAME_TO_CLAIM,
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL,
NULL);
g_main_loop_run (loop);
g_bus_unown_name (owner_id);
]]>
</programlisting></informalexample>
Note that g_bus_own_name() works asynchronously and requires
you to enter your mainloop to await the on_name_aquired()
callback. Also note that in order to avoid race conditions (e.g.
when your service is activated by a method call), you have to export
your manager object <emphasis>before</emphasis> acquiring the
name. The on_bus_acquired() callback is the right place to do
such preparations.
</para>
</section>
<section>
<title>Creating proxies for well-known names</title>
<para>
dbus-glib lets you create proxy objects for well-known names, like the
following example:
<informalexample><programlisting><![CDATA[
proxy = dbus_g_proxy_new_for_name (system_bus_connection,
"org.freedesktop.Accounts",
"/org/freedesktop/Accounts",
"org.freedesktop.Accounts");
]]>
</programlisting></informalexample>
For a #DBusGProxy constructed like this, method calls will be sent to
the current owner of the name, and that owner can change over time.
</para>
<para>
The same can be achieved with #GDBusProxy:
<informalexample><programlisting><![CDATA[
error = NULL;
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* GDBusInterfaceInfo */
"org.freedesktop.Accounts",
"/org/freedesktop/Accounts",
"org.freedesktop.Accounts",
NULL, /* GCancellable */
&error);
]]>
</programlisting></informalexample>
For an added layer of safety, you can specify what D-Bus
interface the proxy is expected to conform to by using the
#GDBusInterfaceInfo type. Additionally, #GDBusProxy loads,
caches and tracks changes to the D-Bus properties on the remote
object. It also sets up match rules so D-Bus signals from the
remote object are delivered locally.
</para>
<para>
The #GDBusProxy type normally isn't used directly - instead
proxies subclassing #GDBusProxy generated by <command><link
linkend="gdbus-codegen">gdbus-codegen</link></command> is used, see <xref linkend="gdbus-example-gdbus-codegen"/>
</para>
</section>
<section>
<title>Generating code and docs</title>
<section id="gdbus-example-gdbus-codegen">
<title>Using gdbus-codegen</title>
<para>
dbus-glib comes with <command>dbus-binding-tool</command>, which
can produce somewhat nice client- and server-side wrappers for a D-Bus interface.
With GDBus, <command><link
linkend="gdbus-codegen">gdbus-codegen</link></command> is used and like
its counterpart, it also takes D-Bus Introspection XML as input:
</para>
<example id="gdbus-example-codegen-input"><title>Example D-Bus Introspection XML</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../../gio/tests/gdbus-object-manager-example/gdbus-example-objectmanager.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: gdbus-example-objectmanager.xml</xi:fallback></xi:include></programlisting></example>
<para>
If this XML is processed like this
<informalexample><programlisting><![CDATA[
gdbus-codegen --interface-prefix org.gtk.GDBus.Example.ObjectManager. \
--generate-c-code generated-code \
--c-namespace Example \
--c-generate-object-manager \
--generate-docbook generated-docs \
gdbus-example-objectmanager.xml
]]></programlisting></informalexample>
then two files <filename>generated-code.h</filename> and
<filename>generated-code.c</filename> are
generated. Additionally, two XML files
<filename>generated-docs-org.gtk.GDBus.Example.ObjectManager.Animal</filename> and
<filename>generated-docs-org.gtk.GDBus.Example.ObjectManager.Cat</filename>
with Docbook XML are generated. For an example of what the docs look
like see <link
linkend="gdbus-interface-org-gtk-GDBus-Example-ObjectManager-Animal">the Animal D-Bus interface documentation</link>.
and
<link
linkend="gdbus-interface-org-gtk-GDBus-Example-ObjectManager-Cat">the Cat D-Bus interface documentation</link>.
</para>
<para>
While the contents of <filename>generated-code.h</filename> and
<filename>generated-code.c</filename> are best described by the
<command><link
linkend="gdbus-codegen">gdbus-codegen</link></command> manual
page, brief examples of how this generated code can be used can be found in
<xref linkend="gdbus-example-codegen-server"/>
and <xref
linkend="gdbus-example-codegen-client"/>. Additionally, since
the generated code has 100% gtk-doc coverage, see
#ExampleAnimal, #ExampleCat, #ExampleObject and
#ExampleObjectManagerClient pages for documentation.
</para>
<example id="gdbus-example-codegen-server"><title>Server-side application using generated code</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../../gio/tests/gdbus-example-objectmanager-server.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: gdbus-example-objectmanager-server.c</xi:fallback></xi:include></programlisting></example>
<example id="gdbus-example-codegen-client"><title>Client-side application using generated code</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../../gio/tests/gdbus-example-objectmanager-client.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: gdbus-example-objectmanager-client.c</xi:fallback></xi:include></programlisting></example>
</section>
<!-- All XInclude paths are relative to the html/ directory under the build root directory -->
<xi:include href="../../../../gio/tests/gdbus-object-manager-example/objectmanager-gen-org.gtk.GDBus.Example.ObjectManager.Animal.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: objectmanager-gen-org.gtk.GDBus.Example.ObjectManager.Animal.xml</xi:fallback></xi:include>
<xi:include href="../../../../gio/tests/gdbus-object-manager-example/objectmanager-gen-org.gtk.GDBus.Example.ObjectManager.Cat.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: objectmanager-gen-org.gtk.GDBus.Example.ObjectManager.Cat.xml</xi:fallback></xi:include>
<xi:include href="../gdbus-object-manager-example/xml/ExampleAnimal.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: ExampleAnimal.xml</xi:fallback></xi:include>
<xi:include href="../gdbus-object-manager-example/xml/ExampleCat.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: ExampleCat.xml</xi:fallback></xi:include>
<xi:include href="../gdbus-object-manager-example/xml/ExampleObject.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: ExampleObject.xml</xi:fallback></xi:include>
<xi:include href="../gdbus-object-manager-example/xml/ExampleObjectManagerClient.xml"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT: ExampleObjectManagerClient.xml</xi:fallback></xi:include>
</section>
</chapter>
|