Provided by: libnet-dbus-perl_1.1.0-4build2_amd64 bug

NAME

       Net::DBus::Tutorial::ExportingObjects - tutorials on providing a DBus service

DESCRIPTION

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

CREATING AN OBJECT

       The first step in creating an object is to create a new package which inherits from
       Net::DBus::Object. The Music::Player::Manager object provides an API for managing the
       collection of music player backends for different track types. To start with, lets create
       the skeleton of the package & its constructor. The constructor of the super type,
       Net::DBus::Object expects to be given to parameters, a handle to the Net::DBus::Service
       owning the object, and a path under which the object shall be exported. Since the manager
       class is intended to be a singleton object, we can hard code the path to it within the
       constructor:

         package Music::Player::Manager;

         use base qw(Net::DBus::Object);

         sub new {
             my $class = shift;
             my $service = shift;
             my $self = $class->SUPER::new($service, "/music/player/manager");

             bless $self, $class;

             return $self;
         }

         1;

       Now, as mentioned, the manager with handle a number of different player backends. So we
       need to provide methods for registering new backends, and querying for backends capable of
       playing a particular file type. So modifying the above code we add a hash table in the
       constructor, to store the backends:

         sub new {
             my $class = shift;
             my $service = shift;
             my $self = $class->SUPER::new($service, "/music/player/manager");

             $self->{backends} = {};

             bless $self, $class;

             return $self;
         }

       And now a method to register a new backend. This takes a Perl module name and uses it to
       instantiate a backend. Since the backends are also going to be DBus objects, we need to
       pass in a reference to the service we are attached to, along with a path under which to
       register the backend. We use the "get_service" method to retreieve a reference to the
       service the manager is attached to, and attach the player backend to this same service:
       When a method on DBus object is invoked, the first parameter is the object reference
       ($self), and the remainder are the parameters provided to the method call. Thus writing a
       method implementation on a DBUs is really no different to normal object oriented Perl (cf
       perltoot):

         sub register_backend {
             my $self = shift;
             my $name = shift;
             my $module = shift;

             eval "use $module";
             if ($@) {
                 die "cannot load backend $module: $@" ;
             }

             $self->{backends}->{$name} = $module->new($self->get_service,
                                                       "/music/player/backend/$name");
         }

       Looking at this one might wonder what happens if the "die" method is triggered. In such a
       scenario, rather than terminating the service process, the error will be caught and
       propagated back to the remote caller to deal with.

       The player backends provide a method "get_track_types" which returns an array reference of
       the music track types they support. We can use this method to provide an API to allow easy
       retrieval of a backend for a particular track type. This method will return a path with
       which the backend object can be accessed

         sub find_backend {
             my $self = shift;
             my $extension = shift;

             foreach my $name (keys %{$self->{backends}}) {
                my $backend = $self->{backends}->{$name};
                foreach my $type (@{$backend->get_track_types}) {
                   if ($type eq $extension) {
                       return $backend->get_object_path;
                   }
                }
             }

             die "no backend for type $extension";
         }

       Lets take a quick moment to consider how this method would be used to play a music track.
       If you've not already done so, refresh your memory from Net::DBus::Tutorial::UsingObjects.
       Now, we have an MP3 file which we wish to play, so we search for the path to a backend,
       then retrieve the object for it, and play the track:

         ...get the music player service...
         # Ask for a path to a player for mp3 files
         my $path = $service->find_backend("mp3");
         # $path now contains '/music/player/backend/mpg123'
         # and we can get the backend object
         my $backend = $service->get_object($path);
         # and finally play the track
         $backend->play("/vol/music/beck/guero/09-scarecrow.mp3");

PROVIDING INTROSPECTION DATA

       The code above is a complete working object, ready to be registered with a service, and
       since the parameters and return values for the two methods are both simple strings we
       could stop there. In some cases, however, one might want to be more specific about data
       types expected for parameters, for example signed vs unsigned integers. Adding explicit
       data typing also makes interaction with other programming languages more reliable.
       Providing explicit data type defintions for exported method is known in the DBus world as
       "Introspection", and it makes life much more reliable for users of one's service whom may
       be using a strongly typed language such as C.

       The first step in providing introspection data for a DBus object in Perl, is to specify
       the name of the interface provided by the object. This is typically a period separated
       string, by convention containing the domain name of the application as its first
       component. Since most Perl modules end up living on CPAN, one might use "org.cpan" as the
       first component, followed by the package name of the module (replacing :: with .), eg
       "org.cpan.music.player.manager". If it is not planned to host the module on CPAN, a
       personal/project domain might be used eg "com.berrange.music.player.manager". The
       interface for an object is defined by loading the Net::DBus::Exporter module, providing
       the interface as its first parameter. So the earlier code example would be modified to
       look like:

         package Music::Player::Manager;

         use base qw(Net::DBus);
         use Net::DBus::Exporter qw(com.berrange.music.player.manager)

       Next up, it is necessary to provide data types for the parameters and return values of the
       methods. The Net::DBus::Exporter module provides a method "dbus_method" for this purpose,
       which takes three parameter, the name of the method being exported, an array reference of
       parameter types, and an array reference of return types (the latter can be omitted if
       there are no return values). This can be called at any point in the module's code, but by
       convention it is preferrable to associate calls to "dbus_method" with the actual method
       implementation, thus:

         dbus_method("register_backend", ["string", "string"]);
         sub register_backend {
             my $self = shift;
             my $name = shift;
             my $module = shift;

             .. snipped rest of method body ...
         }

       And, thus:

         dbus_method("find_backend", ["string"], ["string"])
         sub find_backend {
             my $self = shift;
             my $extension = shift;
             ... snip method body...
         }

DEFINING A SERVICE

       Now that the objects have been written, it is time to define a service. A service is
       nothing more than a well known name for a given API contract. A contract can be thought of
       as a definition of a list of object paths, and the corresponding interfaces they provide.
       So, someone else could come along a provide an alternate music player implementation using
       the Python or QT bindings for DBus, and if they provided the same set of object paths &
       interfaces, they could justifiably register the same service on the bus.

       The Net::DBus::Service module provides the means to register a service. Its constructor
       expects a reference to the bus object (an instance of Net::DBus), along with the name of
       the service.  As with interface names, the first component of a service name is usually
       derived from a domain name, and then suffixed with the name of the application, in our
       example forming "org.cpan.Music.Player".  While some objects will be created on the fly
       during execution of the application, others are created upon initial startup. The music
       player manager object created earlier in this tutorial is an example of the latter. It is
       typical to instantiate and register these objects in the constructor for the service. Thus
       a service object for the music player application would look like:

           package Music::Player;

           use base qw(Net::DBus::Service);

           sub new {
               my $class = shift;
               my $bus = shift;
               my $self = $class->SUPER::new($bus, "org.cpan.music.player");

               bless $self, $class;

               $self->{manager} = Music::Player::Manager->new($self);

               return $self;
           }

       The Net::DBus::Service automatically provides one special object to all services, under
       the path "/org/freedesktop/DBus/Exporter".  This object implements the
       "org.freedesktop.DBus.Exporter" interface which has a method "ListObject". This enables
       clients to determine a list of all objects exported within a service. While not
       functionally necessary for most applications, it is none-the-less a useful tool for
       developers debugging applications, or wondering what a service provides.

CONNECTING TO THE BUS

       The final step in getting our service up and running is to connect it to the bus. This
       brings up an interesting conundrum, does one export the service on the system bus (shared
       by all users & processes on the machine), or the session bus (one per user logged into a
       machine). In some cases the answer, with only one of the two buses conceptually making
       sense. In other cases, however, both the session & system bus are valid.  In the former
       one would use the "session" or <system> methods on Net::DBus to get a handle to the
       desired bus, while in the latter case, the "find" method would be used. This applies a
       heuristic to determine the correct bus based on execution environment. In the case of the
       music player, either bus is relevant, so the code to connect the service to the bus would
       look like:

          use Net::DBus;

          my $bus = Net::DBus->find;
          my $player = Music::Player->new($bus);

       With the service attached to the bus, it is merely necessary to run the main event
       processing loop to listen out for & handle incoming DBus messages. So the above code is
       modified to start a simple reactor:

          use Net::DBus;
          use Net::DBus::Reactor;

          my $bus = Net::DBus->find;
          my $player = Music::Player->new($bus);

          Net::DBus::Reactor->main->run;

          exit 0;

       Saving this code into a script "/usr/bin/music-player.pl", coding is complete and the
       service ready for use by clients on the bus.

SERVICE ACTIVATION

       One might now wonder how best to start the service, particularly if it is a service
       capable of running on both the system and session buses. DBus has the answer in the
       concept of "activation". What happens is that when a client on the bus attempts to call a
       method, or register a signal handler against, a service not currently running, it will
       first try and start the service. Service's which wish to participate in this process
       merely need stick a simple service definition file into the directoy
       "/usr/share/dbus-1/services". The file should be named to match the service name, with the
       file extension ".service" appended. eg,
       "/usr/share/dbus-1/services/org.cpan.music.player.service" The file contains two keys,
       first the name of the service, and second the name of the executable used to run the
       service, or in this case the Perl script. So, for our simple service the data file would
       contain:

         [D-BUS Service]
         Name=org.cpan.music.player
         Exec=/usr/bin/music-player.pl

SEE ALSO

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

AUTHORS

       Daniel Berrange <dan@berrange.com>

COPYRIGHT

       Copyright (C) 2005 Daniel P. Berrange