URL: https://linuxfr.org/news/communiquer-avec-d-bus-en-java-avec-jnidbus Title: Communiquer avec D-Bus en Java avec JNIDBus Authors: pulkomandy SeekDaSky, Davy Defaud, Ysabeau đŸ§¶, claudex, NĂżco et BAud Date: 2019ćčŽ09月16æ—„T14:07:53+02:00 License: CC By-SA Tags: dbus, java et jnidbus Score: 30 Avec mes collĂšgues chez [Viveris](http://viveris.fr), on s’est dit qu’on aimerait bien faire plus de logiciel libre. On a donc montĂ© un « groupe _opensource_ » dont le but est d’identifier les projets pour lesquels on peut publier tout ou une partie du code sous licence libre, et aussi de contribuer aux outils et bibliothĂšques qu’on utilise le plus. Il y a quelques mois je vous prĂ©sentais [QTestFramework](https://linuxfr.org/users/pulkomandy/journaux/un-harnais-de-test-pour-qt), depuis on a Ă©galement pu contribuer au [dissecteur 0MQ pour Wireshark](https://github.com/whitequark/zmtp-wireshark) et [un outil pour le _boundary scan_ JTAG](https://github.com/viveris/jtag-boundary-scanner). On vient de publier il y a quelques jours une bibliothĂšque Java pour communiquer en D-Bus. ---- [Projets opensource chez Viveris](https://opensource.viveris.fr) [JNIDBus sur GitHub](https://github.com/viveris/jnidbus) [D-Bus sur WikipĂ©dia](https://fr.wikipedia.org/wiki/D-Bus) ---- Contexte ======== D-Bus ---- D-Bus est un systĂšme de communication inter-processus utilisĂ© sous GNU/Linux. Le projet a Ă©tĂ© lancĂ© par des dĂ©veloppeurs de Red Hat au sein de Freedesktop. Il a Ă©tĂ© intĂ©grĂ© dans GNOME 2 et KDE 4, et aujourd’hui il est utilisĂ© par de trĂšs nombreux composants d’un systĂšme GNU/Linux : systemd, NetworkManager et PulseAudio, par exemple. Il y a mĂȘme une implĂ©mentation dans le noyau Linux lui‐mĂȘme. JNI ---- JNI (_Java Native Interface_) est une API qui permet d’interfacer du code tournant dans une machine virtuelle Java avec du code natif. Cela nĂ©cessite d’écrire (en C ou C++) des _wrappers_ qui vont manipuler la pile de la JVM pour rĂ©cupĂ©rer les arguments et pousser les valeurs de retour, et Ă©ventuellement accĂ©der aux objets Java manipulĂ©s. Les mĂ©thodes ainsi implĂ©mentĂ©es peuvent ensuite ĂȘtre appelĂ©es depuis le code Java de façon transparente. Solutions existantes et leurs limitations ----------------------------------------- Freedesktop propose [DBus-Java](https://www.freedesktop.org/wiki/Software/DBusBindings/), mais il n’y a pas eu de version publiĂ©e depuis 2009. La derniĂšre version a besoin de Java 7 pour fonctionner. De plus, cette bibliothĂšque implĂ©mente le protocole D-Bus en Java, ce qui risque de poser des problĂšmes d’interopĂ©rabilitĂ© avec l’implĂ©mentation en C. Il existe bien [une version mise Ă  jour](https://github.com/hypfvieh/dbus-java) de la bibliothĂšque qui corrige au moins le premier problĂšme, cependant l’API n’utilise pas les nouvelles fonctionnalitĂ©s de Java et c’est bien dommage. JNIDBus ======= Historique ---------- Dans le cadre d’une migration d’un de nos logiciels depuis Java 7, nous avons dĂ©couvert que DBus-Java ne prenait pas en charge les versions plus rĂ©centes. Nous aurions pu nous contenter d’une mise Ă  jour de cette implĂ©mentation, mais il y avait beaucoup de code Ă  reprendre dedans et de toute façon, l’API ne nous convenait pas. En effet, dbus-java reprĂ©sente les messages D-Bus par des « tuples » gĂ©nĂ©riques, ce qui est assez peu pratique Ă  utiliser et rend le code illisible. De plus, les API sont bloquantes et cela nous contraignait Ă  utiliser une rĂ©serve (_pool_) de fils d’exĂ©cution qui complexifiait encore le logiciel. L’ensemble des ces dĂ©fauts et l’absence d’alternative viable nous ont poussĂ© Ă  dĂ©velopper notre propre alternative, en essayant de rĂ©pondre a toutes les problĂ©matiques. Afin de ne pas rĂ©implĂ©menter le protocole D-Bus nous voulions utiliser la bibliothĂšque `libdbus-1`. L’écosystĂšme Java possĂšde deux maniĂšres d’appeler du code natif : JNI et JNA, ce dernier Ă©tant Ă©cartĂ© pour des raisons de performances et de complexitĂ© des bibliothĂšques de liaison (_bindings_) Ă  Ă©crire. Enfin, la derniĂšre contrainte Ă©tait de rĂ©duire le code natif au strict minimum, afin de limiter la complexitĂ© de ce dernier qui est trĂšs difficile Ă  tester unitairement. Utilisation ----------- La base de JNIDBus est la sĂ©rialisation d’objet Java. La signature du message est dĂ©crite dans une annotation. *Exemple pour un message contenant une chaĂźne de caractĂšres et un entier :* ```java @DBusType( /* Pour plus d’info sur le format de la signature, rĂ©fĂ©rez vous * Ă  la documentation D-Bus */ signature = "si", /* Donne le nom des propriĂ©tĂ©s contenant les donnĂ©es du message, * dans notre cas la chaĂźne de caractĂšres est contenue dans la * propriĂ©tĂ© nommĂ©e « string » et l’entier dans le champs « integer » */ fields = {"string","integer"} ) public class StringMessage extends Message { /* Les champs seront accĂ©dĂ©s au travers de ses setters et * getters qui devront respecter la convention "setXxx"/"getXxx" */ private String string; private int integer public String getString() { ... } public void setString(String string) { ... } public int getInteger() { ... } public void setInteger(int string) { ... } } ``` L’appel de mĂ©thodes distantes est transparent grĂące a l’utilisation de `proxy` Java, il suffit de dĂ©crire l’interface de l’objet et de donner le nom de son bus pour pouvoir l’appeler. Toute mĂ©thode distante retourne un `PendingCall` auquel on doit attacher un _listener_ pour ĂȘtre notifiĂ© de l’arrivĂ©e du rĂ©sultat. JNIDBus permet Ă©galement d’exposer des mĂ©thodes distante par le biais de `handlers`. Un _handler_ est simplement une classe annotĂ©e dĂ©crivant et implĂ©mentant les mĂ©thodes distantes, les signatures sont infĂ©rĂ©es grĂące aux types d’entrĂ©es et de sorties. Les _handlers_ seront exĂ©cutĂ©s dans la boucle d’évĂšnements, il est donc primordial d’éviter tout appel bloquant ou toute tĂąche lourde. Afin de tout de mĂȘme pouvoir effectuer ces tĂąches lourdes, les _handlers_ gĂšrent un type de retour asynchrone (`Promise`). *Exemple d’un* handler *pour un signal et un appel :* ```java @Handler( /* pour plus d’information rĂ©fĂ©rez vous Ă  la documentation D-Bus */ path = "/some/object/path", interfaceName = "some.dbus.interface" ) public class SomeHandler extends GenericHandler { @HandlerMethod( //le nom exposĂ© a D-Bus peut ĂȘtre diffĂ©rent du nom de la mĂ©thode Java member = "someSignal", type = HandlerType.SIGNAL ) //Ici notre signal n’a aucun paramĂštre, on utilise donc le singleton EmptyMessage public void someSignal(Message.EmptyMessage emptyMessage) { ... } @HandlerMethod( member = "stringSignal", type = HandlerType.METHOD ) public SomeOutput someCall(SomeInput input) { ... } } ``` *Comment enregistrer le handler auprĂšs de D-Bus :* ```java //connection au bus Dbus receiver = new Dbus(BusType.SESSION,"my.bus.name"); //instanciation SomeHandler handler = new SomeHandler(); //ajout, JNIDBus lancera une exception si le handler n’est pas valide this.receiver.addHandler(handler); ``` Le langage Kotlin est pris en charge par le biais d’un artefact Gradle supplĂ©mentaire dĂ©finissant des extensions qui suspendent l’appel de fonction et offrant la possibilitĂ© d’avoir des `handlers` qui facilitent la mise en place de cette suspension : ```kotlin //il faut enregistrer la classe qui se chargera d’invoquer les mĂ©thodes qui suspendent KotlinMethodInvocator.registerKotlinInvocator() @Handler(path = "...", interfaceName = "...") class CallHandler : KotlinGenericHandler() { @HandlerMethod(member = "suspendingCall", type = MemberType.METHOD) suspend fun suspendingCall(emptyMessage: Message.EmptyMessage): SingleStringMessage { //coroutines are awesome delay(2000) return SingleStringMessage().apply { string = "test" } } } ```

AltStyle ă«ă‚ˆăŁăŠć€‰æ›ă•ă‚ŒăŸăƒšăƒŒă‚ž (->ă‚ȘăƒȘă‚žăƒŠăƒ«) /