1Net::DBus::Tutorial::ExUpsoerrtiCnognOtbrjiebcuttse(d3N)Peetr:l:DDBoucsu:m:eTnuttaotriioanl::ExportingObjects(3)
2
3
4

NAME

6       Net::DBus::Tutorial::ExportingObjects - tutorials on providing a DBus
7       service
8

DESCRIPTION

10       This document provides a tutorial on providing a DBus service using the
11       Perl Net::DBus application bindings. This examples in this document
12       will be based on the code from the Music::Player distribution, which is
13       a simple DBus service providing a music track player.
14

CREATING AN OBJECT

16       The first step in creating an object is to create a new package which
17       inherits from Net::DBus::Object. The Music::Player::Manager object pro‐
18       vides an API for managing the collection of music player backends for
19       different track types. To start with, lets create the skeleton of the
20       package & its constructor. The constructor of the super type,
21       Net::DBus::Object expects to be given to parameters, a handle to the
22       Net::DBus::Service owning the object, and a path under which the object
23       shall be exported. Since the manager class is intended to be a single‐
24       ton object, we can hard code the path to it within the constructor:
25
26         package Music::Player::Manager;
27
28         use base qw(Net::DBus);
29
30         sub new {
31             my $class = shift;
32             my $service = shift;
33             my $self = $class->SUPER::new($service, "/music/player/manager");
34
35             bless $self, $class;
36
37             return $self;
38         }
39
40         1;
41
42       Now, as mentioned, the manager with handle a number of different player
43       backends. So we need to provide methods for registering new backends,
44       and querying for backends capable of playing a particular file type. So
45       modifying the above code we add a hash table in the constructor, to
46       store the backends:
47
48         sub new {
49             my $class = shift;
50             my $service = shift;
51             my $self = $class->SUPER::new($service, "/music/player/manager");
52
53             $self->{backends} = {};
54
55             bless $self, $class;
56
57             return $self;
58         }
59
60       And now a method to register a new backend. This takes a Perl module
61       name and uses it to instantiate a backend. Since the backends are also
62       going to be DBus objects, we need to pass in a reference to the service
63       we are attached to, along with a path under which to register the back‐
64       end. We use the "get_service" method to retreieve a reference to the
65       service the manager is attached to, and attach the player backend to
66       this same service: When a method on DBus object is invoked, the first
67       parameter is the object reference ($self), and the remainder are the
68       parameters provided to the method call. Thus writing a method implemen‐
69       tation on a DBUs is really no different to normal object oriented Perl
70       (cf perltoot):
71
72         sub register_backend {
73             my $self = shift;
74             my $name = shift;
75             my $module = shift;
76
77             eval "use $module";
78             if ($@) {
79                 die "cannot load backend $module: $@" ;
80             }
81
82             $self->{backends} = $module->new($self->get_service,
83                                              "/music/player/backend/$name");
84         }
85
86       Looking at this one might wonder what happens if the "die" method is
87       triggered. In such a scenario, rather than terminating the service
88       process, the error will be caught and propagated back to the remote
89       caller to deal with.
90
91       The player backends provide a method "get_track_types" which returns an
92       array reference of the music track types they support. We can use this
93       method to provide an API to allow easy retrieval of a backend for a
94       particular track type. This method will return a path with which the
95       backend object can be accessed
96
97         sub find_backend {
98             my $self = shift;
99             my $extension = shift;
100
101             foreach my $name (keys %{$self->{backends}}) {
102                my $backend = $self->{backends}->{$name};
103                foreach my $type (@{$backend->get_track_types}) {
104                   if ($type eq $extension) {
105                       return $backend->get_object_path;
106                   }
107                }
108             }
109
110             die "no backend for type $extension";
111         }
112
113       Lets take a quick moment to consider how this method would be used to
114       play a music track. If you've not already done so, refresh your memory
115       from Net::DBus::Tutorial::UsingObjects. Now, we have an MP3 file which
116       we wish to play, so we search for the path to a backend, then retrieve
117       the object for it, and play the track:
118
119         ...get the music player service...
120         # Ask for a path to a player for mp3 files
121         my $path = $service->find_backend("mp3");
122         # $path now contains '/music/player/backend/mpg123'
123         # and we can get the backend object
124         my $backend = $service->get_object($path);
125         # and finally play the track
126         $backend->play("/vol/music/beck/guero/09-scarecrow.mp3");
127

PROVIDING INTROSPECTION DATA

129       The code above is a complete working object, ready to be registered
130       with a service, and since the parameters and return values for the two
131       methods are both simple strings we could stop there. In some cases,
132       however, one might want to be more specific about data types expected
133       for parameters, for example signed vs unsigned integers. Adding
134       explicit data typing also makes interaction with other programming lan‐
135       guages more reliable. Providing explicit data type defintions for
136       exported method is known in the DBus world as "Introspection", and it
137       makes life much more reliable for users of one's service whom may be
138       using a strongly typed language such as C.
139
140       The first step in providing introspection data for a DBus object in
141       Perl, is to specify the name of the interface provided by the object.
142       This is typically a period separated string, by convention containing
143       the domain name of the application as its first component. Since most
144       Perl modules end up living on CPAN, one might use "org.cpan" as the
145       first component, followed by the package name of the module (replacing
146       :: with .), eg "org.cpan.music.player.manager". If it is not planned to
147       host the module on CPAN, a personal/project domain might be used eg
148       "com.berrange.music.player.manager". The interface for an object is
149       defined by loading the Net::DBus::Exporter module, providing the inter‐
150       face as its first parameter. So the earlier code example would be modi‐
151       fied to look like:
152
153         package Music::Player::Manager;
154
155         use base qw(Net::DBus);
156         use Net::DBus::Exporter qw(com.berrange.music.player.manager)
157
158       Next up, it is neccessary to provide data types for the parameters and
159       return values of the methods. The Net::DBus::Exporter module provides a
160       method "dbus_method" for this purpose, which takes three parameter, the
161       name of the method being exported, an array reference of parameter
162       types, and an array reference of return types (the latter can be omit‐
163       ted if there are no return values). This can be called at any point in
164       the module's code, but by convention it is preferrable to associate
165       calls to "dbus_method" with the actual method implementation, thus:
166
167         dbus_method("register_backend", ["string", "string"]);
168         sub register_backend {
169             my $self = shift;
170             my $name = shift;
171             my $module = shift;
172
173             .. snipped rest of method body ...
174         }
175
176       And, thus:
177
178         dbus_method("find_backend", ["string"], ["string"])
179         sub find_backend {
180             my $self = shift;
181             my $extension = shift;
182             ... snip method body...
183         }
184

DEFINING A SERVICE

186       Now that the objects have been written, it is time to define a service.
187       A service is nothing more than a well known name for a given API con‐
188       tract. A contract can be thought of as a definition of a list of object
189       paths, and the corresponding interfaces they provide. So, someone else
190       could come along a provide an alternate music player implementation
191       using the Python or QT bindings for DBus, and if they provided the same
192       set of object paths & interfaces, they could justifiably register the
193       same service on the bus.
194
195       The Net::DBus::Service module provides the means to register a service.
196       Its constructor expects a reference to the bus object (an instance of
197       Net::DBus), along with the name of the service.  As with interface
198       names, the first component of a service name is usually derived from a
199       domain name, and then suffixed with the name of the application, in our
200       example forming "org.cpan.Music.Player".  While some objects will be
201       created on the fly during execution of the application, others are cre‐
202       ated upon initial startup. The music player manager object created ear‐
203       lier in this tutorial is an example of the latter. It is typical to
204       instantiate and register these objects in the constructor for the ser‐
205       vice. Thus a service object for the music player application would look
206       like:
207
208           package Music::Player;
209
210           use base qw(Net::DBus::Service);
211
212           sub new {
213               my $class = shift;
214               my $bus = shift;
215               my $self = $class->SUPER::new($bus, "org.cpan.music.player");
216
217               bless $self, $class;
218
219               $self->{manager} = Music::Player::Manager->new($self);
220
221               return $self;
222           }
223
224       The Net::DBus::Service automatically provides one special object to all
225       services, under the path "/org/freedesktop/DBus/Exporter".  This object
226       implements the "org.freedesktop.DBus.Exporter" interface which has a
227       method "ListObject". This enables clients to determine a list of all
228       objects exported within a service. While not functionally neccessary
229       for most applications, it is none-the-less a useful tool for developers
230       debugging applications, or wondering what a service provides.
231

CONNECTING TO THE BUS

233       The final step in getting our service up and running is to connect it
234       to the bus. This brings up an interesting conundrum, does one export
235       the service on the system bus (shared by all users & processes on the
236       machine), or the session bus (one per user logged into a machine). In
237       some cases the answer, with only one of the two buses conceptually mak‐
238       ing sense. In other cases, however, both the session & system bus are
239       valid.  In the former one would use the "session" or <system> methods
240       on Net::DBus to get a handle to the desired bus, while in the latter
241       case, the "find" method would be used. This applies a heuristic to
242       determine the correct bus based on execution environment. In the case
243       of the music player, either bus is relevant, so the code to connect the
244       service to the bus would look like:
245
246          use Net::DBus;
247
248          my $bus = Net::DBus->find;
249          my $player = Music::Player->new($bus);
250
251       With the service attached to the bus, it is merely neccessary to run
252       the main event processing loop to listen out for & handle incoming DBus
253       messages. So the above code is modified to start a simple reactor:
254
255          use Net::DBus;
256          use Net::DBus::Reactor;
257
258          my $bus = Net::DBus->find;
259          my $player = Music::Player->new($bus);
260
261          Net::DBus::Reactor->main->run;
262
263          exit 0;
264
265       Saving this code into a script "/usr/bin/music-player.pl", coding is
266       complete and the service ready for use by clients on the bus.
267

SERVICE ACTIVATION

269       One might now wonder how best to start the service, particularly if it
270       is a service capable of running on both the system and session buses.
271       DBus has the answer in the concept of "activation". What happens is
272       that when a client on the bus attempts to call a method, or register a
273       signal handler against, a service not currently running, it will first
274       try and start the service. Service's which wish to participate in this
275       process merely need stick a simple service definition file into the
276       directoy "/usr/share/dbus-1/services". The file should be named to
277       match the service name, with the file extension ".service" appended.
278       eg, "/usr/share/dbus-1/services/org.cpan.music.player.service" The file
279       contains two keys, first the name of the service, and second the name
280       of the executable used to run the service, or in this case the Perl
281       script. So, for our simple service the data file would contain:
282
283         [D-BUS Service]
284         Name=org.cpan.music.player
285         Exec=/usr/bin/music-player.pl
286

SEE ALSO

288       Net::DBus::Tutorial for details of other tutorials, and Net::DBus for
289       API documentation
290

AUTHORS

292       Daniel Berrange <dan@berrange.com>
293
295       Copyright (C) 2005 Daniel P. Berrange
296
297
298
299perl v5.8.8                       2008-N0e2t-:2:0DBus::Tutorial::ExportingObjects(3)
Impressum