Cet article est paru dans le magazine
francophone consacré à Linux et aux
logiciels libres n°41 (juillet 2002).

©2002
Editions Diamond


Processus externes

Cette série a pour but de montrer la possibilité de construire un environnement utilisateur entièrement en Java/Swing au dessus de Linux et XFree. Ce troisième article s'intéresse à l'appel de programmes externes.

Objectif

Le système GNU/Linux dispose d'une quantité impressionnante de logiciels, souvent disponibles dès l'installation. Une bonne partie de ceux-ci se présente comme des outils en ligne de commande, d'autres ont separé leur traitement dans une bibliothèque utilisable séparement. L'objectif est de montrer comment utiliser cette mine inépuisable de pépites directement dans notre bureau.

Lancement d'un logiciel

Les explications suivantes s'appliquent à la version 0.21 de JDistro. L'utilisation d'un logiciel, qu'il soit natif ou non, se fait par la création d'une entité le représentant. Sélectionnez "Créer une entité", choisissez le type "Application" pour un exécutable natif, entrez le chemin du binaire et éventuellement le chemin vers une icône (gif, jpg ou png). Vous pouvez saisir d'éventuels arguments dans le second onglet. (désolé, tout n'a pas encore été traduit)

L'entité sera accessible à la fois sur le bureau et dans le panneau. Pour lancer le logiciel, il suffit de double-cliquer dessus.

Cas réel: XMMS

Le but étant d'avoir un bureau pur java, l'exemple de la calculatrice n'était pas des plus probants. La MVJ dispose de capacités multimedia intéressantes (audio et video grâce à JMF, le Java Media Framework) mais qui ne sont pas encore très pratiques à mettre en oeuvre et qui sont assez consommatrices en cycles processeur. D'autre part, j'aime bien XMMS et celui-ci, en plus de son interface graphique, dispose d'un mode en ligne de commande xmms-shell.

Le but du script est de simplifier l'usage de XMMS en permettant le passage d'une liste de fichiers ou d'un répertoire dans lequel les fichiers musicaux seront recherchés.

#!/bin/sh

# Lancement de XMMS
/usr/bin/xmms&
sleep 1

# Construction de la commande
s="window all hide;clear;"
if [ -n "$*" ]; then
for f in $*
do
  if [ -d "$f" ]; then
    for p in `find "$f" -follow | \
      egrep ".*\.(ogg|mp3|wav)" `
    do
      s="${s}load $p;"
    done
  else
    if [ `basename "$f" .m3u`
      == `basename "$f"` ]; then
      s="${s}load $f;"
    else
      for p in `cat "$f"`
      do
        s="${s}load $p;"
      done
    fi
  fi
done
s="${s}play;"
fi
s="${s}quit;"

# Déverminage
echo "$s">$HOME/xmms-bridge.tmp

# Envoi de la commande
echo "$s" | /usr/bin/xmms-shell \
  2>&1 >$HOME/xmms-bridge.err

Pour utiliser XMMS, il suffit alors de créer l'entité représentant le script. Ceci fait, vous pouvez ouvrir l'explorateur et faire glisser des fichiers audio vers l'entité pour les écouter. D'autre part, le quatrième onglet ("Features") sert à indiquer les (mime) types gérés, comme par exemple audio/*. Ceci permet d'afficher les actions appropriées dans l'aperçu et jouer un morceau en double-cliquant dessus.

Les dessous de l'affaire

La MVJ propose principalement deux classes pour gérer les processus externes: Runtime et Process. La première dispose d'une méthode exec(String[] _params)_params est un tableau de chaînes de caractères dont le premier élément est le chemin de l'exécutable et les suivants les arguments de la ligne de commande. Cette méthode lance l'exécutable et renvoie une instance de Process représentant le processus en cours. Voici un exemple d'utilisation:

String[] parametres=new String[] {
  "/usr/bin/xmms",
  "le-dernier-tube-de-metal.ogg" };
Process processus=Runtime.
  getRuntime().exec(parametres);
// Attendre la fin
processus.waitFor();

La classe Process permet notamment de récupérer les flux standards. On peut les utiliser pour interagir avec le processus. Notre script peut alors être réécrit en Java.

import java.io.*;

public class Xmms
{
  public static void main(String[] _args)
    throws Exception
  {
    Process p=Runtime.getRuntime()
      .exec("xmms-shell");

    PrintWriter out=new PrintWriter
      (p.getOutputStream());

    int l=_args.length;
    for(int i=0;i<l;i++)
      out.println("load "+_args[i]);

    out.println("play");
    out.println("quit");
    out.close();
    p.waitFor();
  }
}

Je vous laisse le soin d'implanter la gestion des répertoires. Ce n'est pas très difficile vu les facilités offertes par la classe File et en particulier les méthodes boolean isDirectory() et File[] listFiles(FileFilter _filter). Il faut juste une goutte de récursivité pour avoir le même comportement que find. De la même manière, on peut récupérer des informations en lisant la sortie du processus.

  BufferedReader in=new BufferedReader
    (new InputStreamReader(p.getInputStream()));

  // Skip the prompt
  int c=0;
  while((c=in.read())!=' ') ;
  out.println("status");
  out.flush();

  String s=in.readLine();
  s=s.substring(s.indexOf(':')+2,s.indexOf('(')-1);
  System.out.println("Le morceau en cours est "+s+".");
  in.close();

La communication avec le processus externe peut se faire aussi par d'autres moyens. Une possibilité est d'utiliser des sockets (ce qui est très similaire à la méthode exposée vu qu'une socket possède un flux d'entrée et un flux de sortie). Une autre est d'utiliser JNI (Java Native Interface) qui permet de charger une bibliothèque dynamique (.so) dans l'espace mémoire de la MVJ. Elle présente l'avantage de donner accès à l'ensemble des fonctions C, au détriment toutefois de la robustesse et de la portabilité. D'autres possibilités, plus lourdes, résident dans l'utilisation de DCOP, CORBA, XML-RPC ou encore SOAP.

____
Guillaume Desnoix
Mai 2002
RÉFÉRENCES Ring
BEST Online, Screenshot million dollar, russia vacation, Karachi, axfeh, , ...