Net::DBus::Tutorial::ExportingObjects man page on Mageia

Man page or keyword search:  
man Server   17783 pages
apropos Keyword Search (all sections)
Output format
Mageia logo
[printable version]

Net::DBus::Tutorial::EUsertContributedNet::DBus::Tutorial::ExportingObjects(3)

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);

	 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} = $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 neccessary 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 neccessary
       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 neccessary 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

perl v5.18.1			  2011Net::DBus::Tutorial::ExportingObjects(3)
[top]

List of man pages available for Mageia

Copyright (c) for man pages and the logo by the respective OS vendor.

For those who want to learn more, the polarhome community provides shell access and support.

[legal] [privacy] [GNU] [policy] [cookies] [netiquette] [sponsors] [FAQ]
Tweet
Polarhome, production since 1999.
Member of Polarhome portal.
Based on Fawad Halim's script.
....................................................................
Vote for polarhome
Free Shell Accounts :: the biggest list on the net