home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / perl5 / Net / DBus / Tutorial / ExportingObjects.pod next >
Encoding:
Text File  |  2008-02-20  |  12.5 KB  |  330 lines

  1. # -*- perl -*-
  2. #
  3. # Copyright (C) 2004-2005 Daniel P. Berrange
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18. #
  19. # $Id: ExportingObjects.pod,v 1.1 2006/01/27 14:02:35 dan Exp $
  20.  
  21. =pod
  22.  
  23. =head1 NAME
  24.  
  25. Net::DBus::Tutorial::ExportingObjects - tutorials on providing a DBus service
  26.  
  27. =head1 DESCRIPTION
  28.  
  29. This document provides a tutorial on providing a DBus service using the
  30. Perl Net::DBus application bindings. This examples in this document
  31. will be based on the code from the L<Music::Player> distribution, which 
  32. is a simple DBus service providing a music track player.
  33.  
  34. =head1 CREATING AN OBJECT
  35.  
  36. The first step in creating an object is to create a new package
  37. which inherits from L<Net::DBus::Object>. The Music::Player::Manager
  38. object provides an API for managing the collection of music player
  39. backends for different track types. To start with, lets create the
  40. skeleton of the package & its constructor. The constructor of the
  41. super type, L<Net::DBus::Object> expects to be given to parameters,
  42. a handle to the L<Net::DBus::Service> owning the object, and a path
  43. under which the object shall be exported. Since the manager class is
  44. intended to be a singleton object, we can hard code the path to it
  45. within the constructor:
  46.  
  47.   package Music::Player::Manager;
  48.  
  49.   use base qw(Net::DBus);
  50.  
  51.   sub new {
  52.       my $class = shift;
  53.       my $service = shift;
  54.       my $self = $class->SUPER::new($service, "/music/player/manager");
  55.       
  56.       bless $self, $class;
  57.       
  58.       return $self;
  59.   }
  60.  
  61.   1;
  62.  
  63.  
  64. Now, as mentioned, the manager with handle a number of different 
  65. player backends. So we need to provide methods for registering
  66. new backends, and querying for backends capable of playing a 
  67. particular file type. So modifying the above code we add a hash
  68. table in the constructor, to store the backends:
  69.  
  70.  
  71.   sub new {
  72.       my $class = shift;
  73.       my $service = shift;
  74.       my $self = $class->SUPER::new($service, "/music/player/manager");
  75.  
  76.       $self->{backends} = {};
  77.       
  78.       bless $self, $class;
  79.       
  80.       return $self;
  81.   }
  82.  
  83. And now a method to register a new backend. This takes a Perl
  84. module name and uses it to instantiate a backend. Since the
  85. backends are also going to be DBus objects, we need to pass
  86. in a reference to the service we are attached to, along with
  87. a path under which to register the backend. We use the C<get_service>
  88. method to retreieve a reference to the service the manager is
  89. attached to, and attach the player backend to this same service:
  90. When a method on DBus object is invoked, the first parameter is
  91. the object reference (C<$self>), and the remainder are the 
  92. parameters provided to the method call. Thus writing a method 
  93. implementation on a DBUs is really no different to normal object
  94. oriented Perl (cf L<perltoot>):
  95.  
  96.   sub register_backend {
  97.       my $self = shift;
  98.       my $name = shift;
  99.       my $module = shift;
  100.  
  101.       eval "use $module";
  102.       if ($@) {
  103.           die "cannot load backend $module: $@" ;
  104.       }
  105.  
  106.       $self->{backends} = $module->new($self->get_service,
  107.                                        "/music/player/backend/$name");
  108.   }
  109.  
  110. Looking at this one might wonder what happens if the C<die>
  111. method is triggered. In such a scenario, rather than terminating
  112. the service process, the error will be caught and propagated back 
  113. to the remote caller to deal with.
  114.  
  115. The player backends provide a method C<get_track_types> which returns
  116. an array reference of the music track types they support. We can use 
  117. this method to provide an API to allow easy retrieval of a backend 
  118. for a particular track type. This method will return a path with which 
  119. the backend object can be accessed
  120.  
  121.   sub find_backend {
  122.       my $self = shift;
  123.       my $extension = shift;
  124.       
  125.       foreach my $name (keys %{$self->{backends}}) {
  126.          my $backend = $self->{backends}->{$name};
  127.          foreach my $type (@{$backend->get_track_types}) {
  128.             if ($type eq $extension) {
  129.                 return $backend->get_object_path;
  130.             }
  131.          }
  132.       }
  133.       
  134.       die "no backend for type $extension";
  135.   }
  136.  
  137. Lets take a quick moment to consider how this method would be used to
  138. play a music track. If you've not already done so, refresh your memory
  139. from L<Net::DBus::Tutorial::UsingObjects>. Now, we have an MP3 file 
  140. which we wish to play, so we search for the path to a backend, then 
  141. retrieve the object for it, and play the track:
  142.  
  143.   ...get the music player service...
  144.   # Ask for a path to a player for mp3 files
  145.   my $path = $service->find_backend("mp3");
  146.   # $path now contains '/music/player/backend/mpg123'
  147.   # and we can get the backend object
  148.   my $backend = $service->get_object($path);
  149.   # and finally play the track
  150.   $backend->play("/vol/music/beck/guero/09-scarecrow.mp3");
  151.  
  152. =head1 PROVIDING INTROSPECTION DATA
  153.  
  154. The code above is a complete working object, ready to be registered with
  155. a service, and since the parameters and return values for the two methods 
  156. are both simple strings we could stop there. In some cases, however, one
  157. might want to be more specific about data types expected for parameters,
  158. for example signed vs unsigned integers. Adding explicit data typing also
  159. makes interaction with other programming languages more reliable. Providing
  160. explicit data type defintions for exported method is known in the DBus world
  161. as C<Introspection>, and it makes life much more reliable for users of one's
  162. service whom may be using a strongly typed language such as C.
  163.  
  164. The first step in providing introspection data for a DBus object in Perl, is
  165. to specify the name of the interface provided by the object. This is typically
  166. a period separated string, by convention containing the domain name of the 
  167. application as its first component. Since most Perl modules end up living on
  168. CPAN, one might use C<org.cpan> as the first component, followed by the package
  169. name of the module (replacing :: with .), eg C<org.cpan.music.player.manager>. If it is
  170. not planned to host the module on CPAN, a personal/project domain might be
  171. used eg C<com.berrange.music.player.manager>. The interface for an object is defined
  172. by loading the L<Net::DBus::Exporter> module, providing the interface as its
  173. first parameter. So the earlier code example would be modified to look like:
  174.  
  175.   package Music::Player::Manager;
  176.  
  177.   use base qw(Net::DBus);
  178.   use Net::DBus::Exporter qw(com.berrange.music.player.manager)
  179.  
  180. Next up, it is neccessary to provide data types for the parameters and return
  181. values of the methods. The L<Net::DBus::Exporter> module provides a method
  182. C<dbus_method> for this purpose, which takes three parameter, the name of the
  183. method being exported, an array reference of parameter types, and an array
  184. reference of return types (the latter can be omitted if there are no return
  185. values). This can be called at any point in the module's code, but by convention
  186. it is preferrable to associate calls to C<dbus_method> with the actual method
  187. implementation, thus:
  188.  
  189.   dbus_method("register_backend", ["string", "string"]);
  190.   sub register_backend {
  191.       my $self = shift;
  192.       my $name = shift;
  193.       my $module = shift;
  194.  
  195.       .. snipped rest of method body ...
  196.   }
  197.  
  198. And, thus:
  199.  
  200.   dbus_method("find_backend", ["string"], ["string"])
  201.   sub find_backend {
  202.       my $self = shift;
  203.       my $extension = shift;
  204.       ... snip method body...
  205.   }
  206.  
  207.  
  208. =head1 DEFINING A SERVICE
  209.  
  210. Now that the objects have been written, it is time to define
  211. a service. A service is nothing more than a well known name
  212. for a given API contract. A contract can be thought of as a
  213. definition of a list of object paths, and the corresponding
  214. interfaces they provide. So, someone else could come along a
  215. provide an alternate music player implementation using the 
  216. Python or QT bindings for DBus, and if they provided the same
  217. set of object paths & interfaces, they could justifiably register
  218. the same service on the bus. 
  219.  
  220. The L<Net::DBus::Service> module provides the means to register
  221. a service. Its constructor expects a reference to the bus object
  222. (an instance of L<Net::DBus>), along with the name of the service.
  223. As with interface names, the first component of a service name is 
  224. usually derived from a domain name, and then suffixed with the 
  225. name of the application, in our example forming C<org.cpan.Music.Player>.
  226. While some objects will be created on the fly during execution
  227. of the application, others are created upon initial startup. The
  228. music player manager object created earlier in this tutorial is
  229. an example of the latter. It is typical to instantiate and register
  230. these objects in the constructor for the service. Thus a service
  231. object for the music player application would look like:
  232.  
  233.     package Music::Player;
  234.  
  235.     use base qw(Net::DBus::Service);
  236.  
  237.     sub new {
  238.         my $class = shift;
  239.         my $bus = shift;
  240.         my $self = $class->SUPER::new($bus, "org.cpan.music.player");
  241.  
  242.         bless $self, $class;
  243.  
  244.         $self->{manager} = Music::Player::Manager->new($self);
  245.  
  246.         return $self;
  247.     }
  248.  
  249. The L<Net::DBus::Service> automatically provides one special
  250. object to all services, under the path C</org/freedesktop/DBus/Exporter>.
  251. This object implements the C<org.freedesktop.DBus.Exporter> interface
  252. which has a method C<ListObject>. This enables clients to determine
  253. a list of all objects exported within a service. While not functionally
  254. neccessary for most applications, it is none-the-less a useful tool for
  255. developers debugging applications, or wondering what a service provides.
  256.  
  257. =head1 CONNECTING TO THE BUS
  258.  
  259. The final step in getting our service up and running is to connect it
  260. to the bus. This brings up an interesting conundrum, does one export
  261. the service on the system bus (shared by all users & processes on the
  262. machine), or the session bus (one per user logged into a machine). In
  263. some cases the answer, with only one of the two buses conceptually making
  264. sense. In other cases, however, both the session & system bus are valid.
  265. In the former one would use the C<session> or <system> methods on L<Net::DBus>
  266. to get a handle to the desired bus, while in the latter case, the C<find>
  267. method would be used. This applies a heuristic to determine the correct
  268. bus based on execution environment. In the case of the music player, either
  269. bus is relevant, so the code to connect the service to the bus would look 
  270. like:
  271.  
  272.    use Net::DBus;
  273.  
  274.    my $bus = Net::DBus->find;
  275.    my $player = Music::Player->new($bus);
  276.  
  277. With the service attached to the bus, it is merely neccessary to run 
  278. the main event processing loop to listen out for & handle incoming
  279. DBus messages. So the above code is modified to start a simple reactor:
  280.  
  281.    use Net::DBus;
  282.    use Net::DBus::Reactor;
  283.  
  284.    my $bus = Net::DBus->find;
  285.    my $player = Music::Player->new($bus);
  286.  
  287.    Net::DBus::Reactor->main->run;
  288.  
  289.    exit 0;
  290.  
  291. Saving this code into a script C</usr/bin/music-player.pl>, coding 
  292. is complete and the service ready for use by clients on the bus.
  293.  
  294. =head1 SERVICE ACTIVATION
  295.  
  296. One might now wonder how best to start the service, particularly 
  297. if it is a service capable of running on
  298. both the system and session buses. DBus has the answer in the 
  299. concept of C<activation>. What happens is that when a client
  300. on the bus attempts to call a method, or register a signal
  301. handler against, a service not currently running, it will first
  302. try and start the service. Service's which wish to participate
  303. in this process merely need stick a simple service definition
  304. file into the directoy C</usr/share/dbus-1/services>. The file
  305. should be named to match the service name, with the file extension
  306. C<.service> appended. eg, C</usr/share/dbus-1/services/org.cpan.music.player.service>
  307. The file contains two keys, first the name of the service, and 
  308. second the name of the executable used to run the service, or in 
  309. this case the Perl script. So, for our simple service the data
  310. file would contain:
  311.  
  312.   [D-BUS Service]
  313.   Name=org.cpan.music.player
  314.   Exec=/usr/bin/music-player.pl
  315.  
  316. =head1 SEE ALSO
  317.  
  318. L<Net::DBus::Tutorial> for details of other tutorials, and
  319. L<Net::DBus> for API documentation
  320.  
  321. =head1 AUTHORS
  322.  
  323. Daniel Berrange <dan@berrange.com>
  324.  
  325. =head1 COPYRIGHT
  326.  
  327. Copyright (C) 2005 Daniel P. Berrange
  328.  
  329. =cut
  330.