DBUS en grosses mailles
DBus est un excellent système qui comme beaucoup dans le monde du libre ne brille pas par sa documentation et sa profusion d'exemples simples. Fort heureusement le portage Java de DBus même si lui-aussi sous documenté est livré avec une foule de tests unitaires qui fournissent une bonne base de départ. Et finalement, un petit serveur DBus fonctionnel en java m'a pris tout au plus 1/4 d'heure à mettre en œuvre.
DBUS, comme Corba, DCop, RMI, WebServices ou encore XmlRPC, est un système d'invocation et de notification à distance orienté objet. L'idée sous-jacente est celle d'un serveur qui va "publier" un certain nombre d'objets, qui exposent un certain nombre d'interfaces, qui contiennent un certain nombre de méthode. Il est alors possible, à distance, d'explorer ces objets, et d'en invoquer, à distance, les méthodes.
DBUS est conçu pour faire office de super bus de communication inter-processus standardisé par freeDesktop.org pour, à terme, être utilisé par toutes les applications Linux. Il peut s'agir d'un démon système (ex. BlueTooth qui notifie les arrivées de nouveaux périphériques) ou d'une application bureau (ex. l'activation de l'économiseur d'écran de Gnome). Chacune de ces applications publie leurs interfaces et peuvent ainsi communiquer entre elles. Techniquement rien n'empêche Gnome ou Kde d'utiliser DBUS en lieu et place de Corba/DCOP mais je doute que le travaild e ré-écriture que cela implique soit envisagé. Au mieux nous disposerons sûrement de points entre les trois architectures.
Installation
L'écriture du binding DBus/Java a été réalisée par Matthew Johnson.
Pour l'installer, le plus simple reste d'utiliser le paquet qui va bien, maintenant inclus dans toutes les distributions modernes.
urpmi dbus-java
Ensuite, il ne vous reste plus qu'à utiliser la librairie qui se trouve en /usr/share/java/dbus.jar.
Mise en œuvre
L'objectif de ce petit exemple est de créer un simplicime serveur DBus qui contient un seul objet (Main) et une seule méthode (Devinez... helloWorld ;-). Tout d'abord, nous devons définir notre interface (fichier DBusHelloWorldInterface.java):
import org.freedesktop.DBus;
import org.freedesktop.dbus.DBusInterface;
public interface DBusHelloWorldInterface extends DBusInterface
{
@DBus.Description("Petit message de HelloWorld avec un nom en paramètre")
public String helloWorld(String nom);
}
Rien de bien sorcier pour qui connais les annotations introduites en Java 1.5.
Nous allons maintenant écrire le code du serveur (fichier DbusHelloWorldServer.java):
import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
public class DBusHelloWorldServer implements DBusHelloWorldInterface
{
private DBusConnection dbusConnection;
public boolean isRemote()
{
return false;
}
private boolean stop=false;
public String helloWorld(String name)
{
stop=true;
return "Hello World : "+name;
}
public void start()
{
try
{
dbusConnection = DBusConnection.getConnection(DBusConnection.SESSION);
dbusConnection.requestBusName("mon.premier.bus");
dbusConnection.exportObject("/Main", this);
while (!stop)
{
try {
Thread.sleep(1000);
} catch (Exception e) {}
}
dbusConnection.disconnect();
}
catch (DBusException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
new DBusHelloWorldServer().start();
}
}
Le programme est très simple. La méthode main() fabrique le serveur et exécute sa méthode start. Le serveur lui, ouvre une connexion de type SESSION sur le système DBUS, demande un droit sur le nom de bus mon.premier.bus et y publie son objet Main. Lorsque la méthode helloWorld est invoquée à distance, l'appel se termine dans son implémentation côté serveur qui a comme seuls traitement la mise à true de la variable (la boucle du serveur dans le main-thread va donc s'arrêter) et le renvoi d'un classique HelloWorld suivi du nom passé en paramètre.
Il nous faut maintenant de compiler et lancer le tout.
export CLASSPATH=/usr/share/java/dbus.jar:/usr/share/java/dbus-viewer.jar:/usr/share/java/dbus-bin.jar:/usr/share/java/hexdump.jar:.
javac DBusHelloWorldInterface.java
javac DBusHelloWorldServer.java
java DBusHelloWorldServer.java & // serveur lancé en tâche de fond...
Introspection
java org.freedesktop.dbus.viewer.DBusViewer
Une petite application Swing apparaît alors affichant tous les bus DBus publiés. Vous devez donc en trouver un qui se nomme mon.premier.bus qui publie un objet /Main. Sélectionnez cette ligne et cliquez sur Introspect. Là apparaît une autre fenêtre contenant dans un onglet la déclaration XML de l'interface contenant votre méthode helloWorld. L'autre onglet contient une version Java de cette interface. Tout est bien en place, nous pouvons donc maintenant quitter cette application pour lancer notre première commande DBUS.
Commande à distance du serveur
Pour cela, nous allons utiliser la commande dbus-send fournie en standard dans le paquet DBUS en lui passant les paramètres complets de notre commande :
dbus-send --print-reply --dest='mon.premier.bus' /Main mon.premier.bus.helloWorld string:'gaston'
Le premier paramètre indique à la commande qu'il lui faut afficher les résultats. Le second donne le nom de notre bus (mon.premier.bus) tel que définit dans DBusHelloWorldServer.Java. Le troisième indique le chemin relatif de l'objet que l'on chercher à attaquer (/Main). Le quatrième donne le chemin complet de la commande (nom du bus + "." + nom de la commande).
Enfin les derniers arguments sont les paramètres à passer à la commande. Ils se composent chacun (ici nous n'en avons qu'un) du type de donnée (ici String, mais ce pourrait être variant, int32, uint16, boolean en fonction de ce qui a été définit dans l'interface) suivi de la valeur du paramètre (entre quotes car il s'agit d'une chaîne).
Une fois validée, la commande devrait alors vous renvoyer la valeur :
string "Hello World : gaston"
Et notre serveur, c'est très logiquement, arrêté. Voilà, ça marche.
Conclusion
Je n'ai pas la prétention de vous donner ici autre chose qu'un avant goût de ce que permet DBUS et son binding Java. Mais j'espère que cela vous a donné des idées des possibles offerts par cette technologie. C'est une pièce importante à l'heure de l'intégration fine de Java et du bureau Unix.
A noter, un très bon outil pour explorer les services dbus :
Drupal a la réputation d'un outil aussi puissant qu'ardu à apréhender. Destiné aux concepteurs de site Web, cet ouvrage a été conçu pour permettre une prise en main progressive et pragmatique du CMS qui ne cesse de faire parler de lui.
Commentaires
C'est marrant, le premier daemon tux utilisait DBUS comme liaison entre le daemon et les clients :-)
Effectivement, la boucle est bouclée ;-) Mais pour revenir sur notre précédente conversation, DBUS pour le démon c'était effectivement pas top car peu implémenté sur les langages classiques. Surtout si tu veux un jour étendre cela à d'autres OS (mac & co.).
En tout cas le support DBUS sur la .5 de l'API marche pas mal, c'est comme cela que je notifie de l'arrivée de nouveaux eMails, c'est bien pratique. C'est tellement efficace que je me demande si le lecteur de RSS a finalement de l'intérêt. En effet, il suffit de cloquer un appel DBUS sur la notification et ça marche tout aussi bien...
Oui bien entendu, ça n'avait pas de sens de faire passer le protocole daemon/client par DBUS, mais y'avait déjà une petite application qui notifiait quand evolution recevait un email, c'est c'était bien (mais j'utilise pas évolution ;-)
L'utilisation de tux avec DBUS n'est quand même pas évidente. Tant qu c'est ton application qui scrute DBUS et contrôle tux en fonction, tout va bien. Mais il a été proposé que les applications DBUS en général puissent envoyer des contrôles à tux (l'esprit du premier daemon) et là ça pose problème puisqu'il n'y a pas moyen de gérer les conflits, quelle application doit répondre à l'appui du bouton de tête à tel moment? C'est comme ça qu'est venu l'idée d'un manager qui est maintenant le gadget manager, il gère les priorités.
Sinon DBUS c'est quand même bien foutu, j'étais rentré dedans et j'avais vraiment bien aimé le concept, et maintenant qu'il y a pas mal d'applications qui le supporte, c'est facile pour la notification.
En fait, je me faisais une remarque en te lisant. Finalement c'est peut-être l'appellation de "démon" qui trompe son monde, il s'agit plus en réalité d'un pilote type "user-space".
Ceci dit, l'idée DBUS n'était pas idiote loin de là, car ce n'est pas à ce niveau que se gère les conflits. Si deux applications s'abonnent à un switch, ben les deux sont notifiées, y'a pas de malaise à ce niveau.
Pour moi DBUS, c'est la même chose que XmlRPC, WebServices, RMI, Cobra, DCOP etc.. C'est de l'invocation distante d'objets. Le seul inconvénient de DBUS c'est qu'il n'est pas facile à gérer de n'importe où. Là par exemple, vu que j'y suis obligé, j'ai une vmware avec windows. Du coup j'ai lancé le manager java pour voir ce que cela donne, et ça marche nickel. En DBUS j'aurais eu un peu plus de mal ;-)
Allez zou, je retourne sons windows pour finir ma journée (Grrrr. :-)
Oui, mais admettons que si rien ne se passe et que tu appuies sur le bouton de tête, tu veux qu'une application te dise la météo, et si tu reçois un email, tux te notifie et si tu appuies sur le bouton dans les 30 secondes, il te lit l'email. Ben sans manager c'est pas évident.
Tiens, t'as de l'expérience avec mingw ou cygwin, y'aurait moyen de faire tourner le daemon sous windows sans gros changements? La seule dépendance que je vois, c'est libusb et ça existe aussi sous windows.
J'ai fait un rapide test avec cygwin/WinXP-SP2.0/VMWare.
Udev
Pour éviter que udev ne me fauche le périphériques j'ai bougé la régle du dossier rules.d. Débranché le Tux et viré les démons Linux récalcitrants.
Dépendances
Installation des paquets cygwin nécessaires via setup.exe. J'avais déjà un peu de monde sous cette install donc il peut y avoir d'autre dépendances.
Compilation
svn co http://svn.tuxisalive.com/daemon/trunk/ daemon cd daemon /usr/bin/makeÇa passe sans soucis !!! Juste une petit couillette d'un retour chariot en trop dans le version.h généré.
LibUSB
Tout d'abord il faut installer libusb-win32 (fallait le savoir ;-) en lançant la commande /usr/sbin/libusb-install
Ensuite j'insère le Tux. Par défaut windows ne semble pas avoir le pilote pour le son. Il ne trouve pas de drivers tout court.
Pour tester la visibilité des périphériques USB sous cygwin :
testlibusb # Dev #0: 0000 - 0000 # Dev #1: C2ME BELGIUM - TUX RF DONGLEÇa s'annonce plutôt bien...
Lancement
Lancement du démon, tada...
./tuxd #----------------------------------- #Tux USB Daemon version 0.3.2 (SVN/UNRELEASED) #Kysoh 2007. #----------------------------------- #error: Can't claim interface: No such file or directory (2) #error: You must load the daemon in root mode #tuxd stoppedDommage... Il semble que ce soit là que ça coince :
error = usb_claim_interface(tux_hdl, TUX_INTERFACE); if (error != 0) { log_error("Can't claim interface: %s (%d)", strerror(errno), errno); return NULL; }Ce qui est bizzare car à ce stade le périphérique est ouvert... Je regarde 10 secondes si je peux trouver d'où ça vient sinon je te laisse la main..
Bon, j'arrête là mais j'ai mis en commentaire (pour voir) le test problématique te mis à part cela le démon se lance, le socket est en écoute et il reçoit bien les ordres.
Mais, et c'est logique, ils ne sont pas relayés par le tux.
Donc il faut absolument débugger cette ligne et le reste devrait suivre. Mais je n'ai pas la compétence sur l'usb pour le faire moi même, désolé...
Donc, à toi de me dire ce que je dois tester. Je garde la plateforme en place dans cette idée.
Il faut créér une dll avec libusb pour que ça fonctionne, je te fais ça demain, je vais voir comment ça marche. Mais là j'ai répète.
A demain
LOL :) pas de soucis, c'est toi qui me demandais pour cygwin, moi j'ai pas d'urgence sur ce sujet ;-) Répète bien !
Voilà, j'ai fais quelques recherches, il semble qu'il soit nécessaire de faire un usb_set_configuration() avant le claim sous Windows. J'ai ajouté ça dans le daemon dans le trunk. Tu peux recompiler. J'ai également créé un driver pour tuxdroid sous libusb à dead://www.tuxisalive.com/Members/jaguarondi/tuxdroid.inf
Quand tu branches le dongle, Windows te demande le driver (devrait en tout cas) et là tu te débrouilles pour lui faire manger le .inf comme tu peux.
Pour ce qui est de l'audio, je viens d'essayer sous vmware, j'ai installé winamp, Ctrl-p->output>configure et tu choisis TUX RF DONGLE comme périphérique. Y'en a 2 listés (le canal son et le canal tts), y'a que l'audio qui fonctionne et ici ça marche.
J'espère que ça nous mènera un peu plus loin. J'ai créé une page sur le wiki où on peut reporter ce qui fonctionne, au cas où d'autres (non francophones) voudraient aussi essayer: http://wiki.tuxisalive.com/index.php/Windows (y'a encore rien grand chose pour le moment)
J'installerai mingw à l'occasion aussi.
A+
Bon, et bien ça marche plutôt pas mal. L'intérêt d'un TuxDroid muet n'est pas énorme mais le concept fonctionne.
Super :-)
Il ne doit pas être muet normalement, Windows le repère puisque tux est un device usb standard. Tu as essayé de regarder dans Windows audio devices quelque part si tu vois les 2 interfaces audio liées à tux?
J'ai dit qu'il était muet, pas qu'il ne faisait pas de bruit :)
Ah juste :-( Faudra va pour le démon tts alors, les librairies sont dispo pour windows, je suppose que ça doit marcher aussi. Qu'est-ce que tu as du changer dans version.h que je corrige ça?
juste un retour chariot intempestif entre la dernière lettre et le " final (il était à la ligne du coup)
Pour ceux qui sont notifiés, l'article a été déplacé
Publier un nouveau commentaire